unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* FW: fit-frame.el
@ 2008-03-10  4:44 Drew Adams
  2008-03-10 15:34 ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Drew Adams @ 2008-03-10  4:44 UTC (permalink / raw)
  To: Emacs-Devel

[-- Attachment #1: Type: text/plain, Size: 1896 bytes --]

Resending for review.

----
> From: Richard Stallman Sent: Tuesday, January 15, 2008 6:42 PM
> Would people please read the file fit-frame.el
> that was sent by Drew Adams on Dec 30, and comment on it?

----
> From: Drew Adams Sent: Sunday, December 30, 2007 7:02 PM
> Attached is library `fit-frame.el', for possible inclusion in Emacs.
> 
> Command `fit-frame' fits a frame to its buffer(s). See the 
> doc string, in particular, for the use of a prefix arg. A 
> common use is to fit a one-window frame to its buffer. 
> 
> Users can set minimum and maximum width and height limits. 
> Within those limits, the width of a one-window frame that has 
> been fit accommodates all of the buffer lines without 
> wrapping, and the height accommodates all of the buffer lines.
> 
> Please give it a try. Let me know, especially, how well it 
> works on different platforms and with different kinds of 
> buffers. I've tried to take into account the menu-bar, 
> minibuffer, and tool-bar, and I've tried to compensate for 
> long header lines in some buffers, such as Dired. 
> 
> Wrt the last point: header lines can be wrapped in some modes 
> - see option `fit-frame-skip-header-lines-alist'. In these 
> cases, a header line is ignored, in order not to let its 
> exceptional length skew the width measurement. Example: Dired 
> with `dired-details' hiding details - the frame is only as 
> wide as the longest file-name length, not the directory name 
> in the header line.
> 
> Note too that if a buffer contains faces larger than the 
> default for the frame, or characters that are extra-wide, 
> then fitting is not perfect. For example, TAB characters in 
> Info menus can in some cases cause a menu line to wrap, and 
> likewise for the slightly larger characters of raised buttons 
> in Customize. This is because the computed size is based only 
> on the frame's default character size.

[-- Attachment #2: fit-frame-2007-12-30.el --]
[-- Type: application/octet-stream, Size: 21921 bytes --]

;;; fit-frame.el --- Resize a frame.  In particular, fit a frame to its buffers.
;;
;; Filename: fit-frame.el
;; Description: Resize a frame.  In particular, fit a frame to its buffers.
;; Author: Drew Adams
;; Maintainer: Drew Adams
;; Copyright (C) 2000-2007, Drew Adams, all rights reserved.
;; Created: Thu Dec  7 09:32:12 2000
;; Version: 22.0
;; Last-Updated: Sun Dec 30 18:43:49 2007 (-28800 Pacific Standard Time)
;;           By: dradams
;;     Update #: 1159
;; Keywords: convenience, frame, window
;; Compatibility: GNU Emacs 22.x
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;;    Resize a frame.  In particular, fit a frame to its buffers.
;;
;;  Commands and user options (variables) are provided here to resize
;;  (shrink-wrap) a frame to fit its displayed buffers, its selected
;;  buffer, or the `fill-column' width.
;;
;;  The command to fit a frame is `fit-frame'.  The main user options
;;  for this command are `fit-frame-inhibit-fitting-flag' and
;;  `fit-frame-max-*[-percent]'.  You can use a prefix argument to
;;  control the behavior of command `fit-frame'.
;;
;;  Put this in your initialization file (`~/.emacs'):
;;
;;    (require 'fit-frame)
;;    (add-hook 'after-make-frame-functions 'fit-frame)
;;
;;  The second line here causes newly created frames to be fitted to
;;  their buffer.
;;
;;  Command `fit-frame' does *not* take the following into account,
;;  when determining the proper frame size:
;;
;;   - font sizes, other than the default frame font
;;   - characters, such as TAB, that have special widths
;;
;;  Suggested key bindings:
;;
;;   (global-set-key [(control ?x) (control ?_)] 'fit-frame)
;;   (global-set-key [vertical-line down-mouse-1]
;;                   'fit-frame-or-mouse-drag-vertical-line)
;;
;;  Customize the menu-bar.  Uncomment this to try it out.
;;
;;   (defvar menu-bar-frames-menu (make-sparse-keymap "Frames"))
;;   (define-key global-map [menu-bar frames]
;;     (cons "Frames" menu-bar-frames-menu)))
;;   (define-key menu-bar-frames-menu [fit-frame]
;;     '("Fit This Frame" . fit-frame))
;;
;;  TO DO:
;;
;;  Emacs needs a command similar to `fit-frame' for windows, that is,
;;  a command that will fit the existing windows of a frame to their
;;  buffers, as well as possible.  That could be then be used in
;;  combination with `fit-frame'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
\f
;;; User options ---------------------------------------------------

;;;###autoload
(defgroup fit-frame nil
  "Resize a frame to fit its buffers."
  :group 'frames :group 'convenience)

;;;###autoload
(defcustom fit-frame-inhibit-fitting-flag nil
  "*Non-nil means command `fit-frame' does nothing.
You can bind this to non-`nil' to temporarily inhibit frame fitting:
    (let ((fit-frame-inhibit-fitting-flag t))...)"
  :type 'boolean :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-min-width 20
  "*Minimum width, in characters, that `fit-frame' gives to a frame.
The actual minimum is at least the greater of this and `window-min-width'."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-max-width nil
  "*Maximum width, in characters, that `fit-frame' gives to a frame.
If nil, then the function `fit-frame-max-width' is used instead."
  :type '(choice (const :tag "Use `fit-frame-max-width-percent' instead" nil)
                 integer)
  :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-max-width-percent 94
  "*Maximum percent of display width that `fit-frame' gives to a frame'.
See function `fit-frame-max-width'.
Not used unless `fit-frame-max-width' is nil."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-min-height window-min-height
  "*Minimum height, in lines, that `fit-frame' gives to a frame.
The actual minimum is at least the greater of this and `window-min-height'."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-max-height nil
  "*Maximum height, in lines, that `fit-frame' gives to a frame.
If nil, then the function `fit-frame-max-height' is used instead."
  :type '(choice (const :tag "Use `fit-frame-max-height-percent' instead" nil)
                 integer)
  :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-max-height-percent 82
  "*Maximum percent of display height that `fit-frame' gives to a frame.
See function `fit-frame-max-height'.
Not used unless `fit-frame-max-height' is nil."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-empty-width (or (cdr (assq 'width default-frame-alist)) 80)
  "*Width, in characters, that `fit-frame' gives to an empty frame."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-empty-height (or (cdr (assq 'height default-frame-alist)) 35)
  "*Height, in lines, that `fit-frame' gives to an empty frame."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-empty-special-display-width 80
  "*Width, in chars, that `fit-frame' gives to an empty special-display frame.
If this is nil, it is ignored."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-empty-special-display-height 9
  "*Height, in lines, that `fit-frame' gives to an empty special-display frame.
If this is nil, it is ignored."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-fill-column-margin 7
  "*Difference between `fill-column' and frame width after fitting a frame.
Used when `fit-frame' fits a frame, if the prefix arg is negative.
Depending on the average word length of the language used in the
selected window, you might want different values for this.  For this
reason, writers of some modes might want to make this buffer-local."
  :type 'integer :group 'fit-frame)

;;;###autoload
(defcustom fit-frame-skip-header-lines-alist
  '((Info-mode . 1) (dired-mode . 2) (compilation-mode . 2))
  "*Alist of major-modes and header lines to ignore.

When `fit-frame' calculates the width of the current buffer, it can
first skip some lines at the buffer beginning, ignoring their
widths.  For example, Info, Dired, and compilation buffers sometimes
have a long header line at the top.  You can use this alist to tell
`fit-frame' to ignore the width of these header lines.

Each item in the alist is of form (MODE . LINES).
 MODE is a major-mode name.
 LINES is the number of lines to skip at the beginning of the buffer."
  :type '(repeat (cons :format "%v" (symbol :tag "Major Mode")
                       (integer :tag "Header Lines to Ignore")))
  :group 'fit-frame)
\f
;;; Commands ---------------------------------------------------

;;;###autoload
(defun fit-frame (&optional frame width height all-windows-p)
  "Resize FRAME to fit its buffer(s).
Does nothing if `fit-frame-inhibit-fitting-flag' is non-nil.

FRAME defaults to the current (i.e. selected) frame.

If non-nil, WIDTH and HEIGHT specify the frame width and height.  To
define them interactively, use a non-negative prefix arg (e.g. `C-9').

To set the width to `fill-column' + `fit-frame-fill-column-margin',
use a negative prefix arg (e.g. `M--').

To fit the frame to all of its displayed buffers, use no prefix arg.
To fit it to just the current buffer, use a plain prefix arg (`C-u').

Fitting a non-empty buffer means resizing the frame to the smallest
size such that the following are both true:

 * The width is at least `fit-frame-min-width' and `window-min-width'.
   The width is at most `fit-frame-max-width(-percent)' and the
   longest line length.

 * The height is at least `fit-frame-min-height' and
   `window-min-height'.  The height is at most
   `fit-frame-max-height(-percent)' and the number of lines.

You can thus use those user variables to control the maximum and
minimum frame sizes.  The `*-percent' options let you specify the
maximum as a percentage of your display size.

See also option `fit-frame-skip-header-lines-alist'.

The following user options control how an empty frame is fit.
An empty frame is a one-window frame displaying an empty buffer.

 * `fit-frame-empty-width', `fit-frame-empty-height' (normal buffer)
 * `fit-frame-empty-special-display-width',
   `fit-frame-empty-special-display-height' (special-display buffer)"
  (interactive
   (let ((option (prefix-numeric-value current-prefix-arg)))
     (list nil
           ;; Plain `C-u' means WIDTH, HEIGHT, and ALL-WINDOWS-P are all nil.
           ;; Non-negative prefix arg means prompt user for WIDTH and HEIGHT.
           ;; Negative prefix arg means use
           ;;   `fill-column' + `fit-frame-fill-column-margin'
           ;;   for WIDTH, and use current frame height for HEIGHT.
           (and current-prefix-arg (atom current-prefix-arg)
                (if (natnump option)
                    (floor (if (fboundp 'read-number)
                               (read-number "New width: ")
                             (string-to-number (read-string "New width: "))))
                  (+ fill-column fit-frame-fill-column-margin)))
           (and current-prefix-arg (atom current-prefix-arg)
                (if (natnump option)
                    (floor (if (fboundp 'read-number)
                               (read-number "New height: ")
                             (string-to-number (read-string "New height: "))))
                  (frame-height)))
           (atom current-prefix-arg))))
  (setq frame (or frame (selected-frame)))
  (unless fit-frame-inhibit-fitting-flag
    (let (extra-lines computed-max-frame-size empty-buf-p specbuf-p)
      (save-window-excursion
        (select-frame frame)
        (setq empty-buf-p (and (= (point-min) (point-max))
                               (one-window-p (selected-window)))
              specbuf-p (and empty-buf-p
                             (special-display-p (buffer-name (window-buffer))))))
      ;; `extra-lines' for minimum frame height. `set-frame-size' includes the
      ;; tool-bar and the minibuffer.  For Emacs without a toolkit, the one-line
      ;; menu-bar is also included - add 1 line for that.  Add 1 line for the
      ;; minibuffer, unless it is standalone.  Perhaps we should also take into
      ;; account a possible horizontal scroll bar, but we don't do that.
      (let* ((fparams (frame-parameters frame)))
        (setq extra-lines (+ 2          ; Minimum is 1 for empty + 1 extra.
                             (or (cdr (assq 'tool-bar-lines fparams)) 0))) ; Tool bar.
        (when (and (cdr (assq 'minibuffer fparams)) ; Frame has a minibuffer, but
                   (save-window-excursion (select-frame frame) ; it's not standalone.
                                          (not (one-window-p nil 'selected-frame))))
          (setq extra-lines (1+ extra-lines))))
      (when (and (not (eq system-type 'windows-nt)) (not (featurep 'x-toolkit)))
        (setq extra-lines (1+ extra-lines)))
      (unless (or empty-buf-p (and width height))
        (setq computed-max-frame-size (fit-frame-max-frame-size frame all-windows-p)))
      (set-frame-size
       ;; Frame
       frame
       ;; Columns
       (or width
           (and empty-buf-p (if specbuf-p
                                fit-frame-empty-special-display-width
                              fit-frame-empty-width))
           (max fit-frame-min-width window-min-width
                (min (or fit-frame-max-width (fit-frame-max-width frame))
                     (1+ (car computed-max-frame-size)))))
       ;; Rows
       (or height
           (and empty-buf-p (if specbuf-p
                                fit-frame-empty-special-display-height
                              fit-frame-empty-height))
           (max fit-frame-min-height window-min-height
                (min (or fit-frame-max-height (fit-frame-max-height frame))
                     (+ (cdr computed-max-frame-size) extra-lines))))))))

;;;###autoload
(defun fit-frame-or-mouse-drag-vertical-line (start-event)
  "If only window in frame, `fit-frame'; else `mouse-drag-vertical-line'."
  (interactive "e")
  (if (one-window-p t) (fit-frame) (mouse-drag-vertical-line start-event)))
\f
;;; Non-Interactive Functions -------------------------------------------

(defun fit-frame-max-width (&optional frame)
  "Returns the max width, in chars, for `fit-frame' to use for a new frame.
If option `fit-frame-max-width' is non-nil, then this is not used.

The value is relative to your display size and FRAME's character
size, and depends on the value of `fit-frame-max-width-percent':

  (/ (* fit-frame-max-width-percent (x-display-pixel-width))
     (* 100 (frame-char-width FRAME)))"
  (setq frame (or frame (selected-frame)))
  (/ (* fit-frame-max-width-percent
        (if (fboundp 'winmgr-display-available-pixel-bounds) ; For MacIntosh.
 	    (nth 2 (winmgr-display-available-pixel-bounds))
          (x-display-pixel-width)))
     (* 100 (frame-char-width frame))))

(defun fit-frame-max-height (&optional frame)
  "Returns the max height, in lines, for `fit-frame' to use for a new frame.
If option `fit-frame-max-height' is non-nil, then this is not used.

The value is relative to your display size and FRAME's character
size, and depends on the value of `fit-frame-max-height-percent':

  (/ (* fit-frame-max-height-percent (x-display-pixel-height))
     (* 100 (frame-char-height FRAME)))"
  (setq frame (or frame (selected-frame)))
  (/ (* fit-frame-max-height-percent
        (if (fboundp 'winmgr-display-available-pixel-bounds) ; For MacIntosh.
 	    (nth 3 (winmgr-display-available-pixel-bounds))
          (x-display-pixel-height)))
     (* 100 (frame-char-height frame)
        ;; When fitting a thumbnail frame, we don't want the height to use the
        ;; whole display height.  So, we apply a fudge factor:
        ;; `fit-frame-thumbnail-factor'.  We could also use it in
        ;; `fit-frame-max-width', in addition to `fit-frame-max-height',
        ;; but we don't need to.
        (fit-frame-thumbnail-factor frame))))

(defun fit-frame-max-frame-size (frame all-windows-p)
  "Return maximum size of frame FRAME as a cons: (MAX-WIDTH . MAX-HEIGHT).
If ALL-WINDOWS-P is non-nil, then consider all buffers shown in FRAME.
Otherwise, consider only the selected buffer."
  (save-window-excursion
    (select-frame frame)
    (if (not all-windows-p)
        (fit-frame-max-window-size (selected-window))
      (let* ((wins ())
             (marked-wins ()) ; Windows whose size was already considered.
             (max-width 0)
             (max-height 0))
        (walk-windows (lambda (w) (push w wins)) 'no-mini 'this-frame)
        (setq wins (sort wins (lambda (w1 w2) ; Top to bottom, left to right.
                                (let ((edges1 (window-edges w1))
                                      (edges2 (window-edges w2)))
                                  (or (< (cadr edges1) (cadr edges2)) ; top
                                      (and (= (cadr edges1) (cadr edges2))
                                           (<= (car edges1) (car edges2)))))))) ; left
        ;; Iterate over all windows in frame.
        ;; For each, check all windows in the same row, and all in the same column.
        ;; When checking those in the same row:
        ;;   1. Increase MAX-HEIGHT to the max buffer height of the row windows.
        ;;   2. Increase MAX-WIDTH to the sum of the buffer widths of the row windows.
        ;; When checking those in the same column:
        ;;   1. Increase MAX-WIDTH to the max buffer width of the column windows.
        ;;   2. Increase MAX-HEIGHT to the sum of the buffer heights of the col windows.
        ;; After examining a window in the frame, exclude it from further examination
        ;;  by adding it to MARKED-WINS.
        (dolist (win wins)
          (unless (memq win marked-wins)
            (let* ((win-edges (window-edges win))
                   (win-top (cadr win-edges))
                   (win-left (car win-edges)))
              ;; Add widths of buffers in the same row.  Max the heights of the buffers.
              (dolist (row-win (fit-frame-same-row-windows wins win marked-wins))
                ;; Add ROW-WIN to exclusion list for subsequent iterations.
                (setq marked-wins (cons row-win marked-wins))
                (let* ((win-size (fit-frame-max-window-size row-win))
                       (max-win-width (car win-size))
                       (max-win-height (cdr win-size)))
                  (unless (> (cadr (window-edges row-win)) win-top) ; Use only first.
                    (setq max-width (+ max-width max-win-width)))
                  (setq max-height (max max-height max-win-height))))
              ;; Add heights of buffers in the same column.  Max the buffer widths.
              (dolist (col-win (fit-frame-same-column-windows wins win marked-wins))
                ;; Add COL-WIN to exclusion list for subsequent iterations.
                (setq marked-wins (cons col-win marked-wins))
                (let* ((win-size (fit-frame-max-window-size col-win))
                       (max-win-width (car win-size))
                       (max-win-height (cdr win-size)))
                  (unless (> (car (window-edges col-win)) win-left) ; Use only first.
                    (setq max-height (+ max-height max-win-height)))
                  (setq max-width (max max-width max-win-width)))))))
        (cons max-width max-height)))))

(defun fit-frame-same-row-windows (wins window exclude)
  "Returns the windows in WINS that are in the same row as window WINDOW.
This the list of windows in WINS whose top edge is above the bottom
 edge of WINDOW.
Windows that are in list EXCLUDE are excluded from the result."
  (let ((ref-bottom (cadddr (window-edges window)))
        (row-wins ()))
    (dolist (win wins)
      (when (and (not (memq win exclude)) (< (cadr (window-edges win)) ref-bottom))
        (push win row-wins)))
    row-wins))

(defun fit-frame-same-column-windows (wins window exclude)
  "Returns the windows in WINS that are in the same column as window WINDOW.
This the list of windows in WINS whose left edge is to the left of the
 right edge of WINDOW.
Windows that are in list EXCLUDE are excluded from the result."
  (let ((ref-right (caddr (window-edges window)))
        (col-wins ()))
    (dolist (win wins)
      (when (and (not (memq win exclude)) (< (car (window-edges win)) ref-right))
        (push win col-wins)))
    col-wins))

(defun fit-frame-max-window-size (window)
  "Maximum size that would be needed to display the buffer in WINDOW.
Returned as a cons: (MAX-WIDTH . MAX-HEIGHT), where:
 MAX-WIDTH is the maximum width, in characters.
 MAX-HEIGHT is the maximum height, in lines."
  (select-window window)
  (let ((hdr-lines (cdr (assq major-mode fit-frame-skip-header-lines-alist)))
        (hdr-widths ())
        (max-win-width 0)
        (max-win-height 0))
    (save-excursion
      (set-buffer (window-buffer))
      (goto-char (point-min))
      ;; Don't count header lines for width calculation.
      (while (and hdr-lines (> hdr-lines 0))
        (end-of-line)
        (setq hdr-widths (cons (current-column) hdr-widths)
              hdr-lines  (1- hdr-lines))
        (forward-line)
        (setq max-win-height (1+ max-win-height)))
      ;; Calculate maximum line width and number of lines.
      (while (not (eobp))
        (end-of-line)
        (setq max-win-width (max (current-column) max-win-width))
        (forward-line 1)
        (setq max-win-height (1+ max-win-height))))
    ;; Add height for any wrap-around header lines.
    (while hdr-widths
      (when (> (car hdr-widths) max-win-width)
        (if (zerop max-win-width)
            (setq max-win-height (1+ max-win-height))
          (let ((nb-wraps (/ (car hdr-widths) max-win-width))
                (remainder (% (car hdr-widths) max-win-width)))
            (unless (zerop remainder) (setq nb-wraps (1+ nb-wraps)))
            (setq max-win-height (+ max-win-height nb-wraps)))))
      (setq hdr-widths (cdr hdr-widths)))
    (cons max-win-width max-win-height)))

(defun fit-frame-thumbnail-factor (frame)
  "Shrink factor for thumbnail frames.  See `thumb-frm.el'.
FRAME is the frame to apply factor to."
  (let ((char-height (frame-char-height frame)))
    (if (and (fboundp 'thumbnail-frame-p) ; Defined in `thumb-frm.el'
             (thumbnail-frame-p frame))
        ;; Need integer result for `set-frame-size'.  1+ because of integer round-off.
        (1+ (/ (+ char-height frame-thumbnail-font-difference) char-height))
      1)))

;;;;;;;;;;

(provide 'fit-frame)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; fit-frame.el ends here

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: FW: fit-frame.el
  2008-03-10  4:44 FW: fit-frame.el Drew Adams
@ 2008-03-10 15:34 ` Stefan Monnier
  2008-03-10 17:04   ` Drew Adams
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Monnier @ 2008-03-10 15:34 UTC (permalink / raw)
  To: Drew Adams; +Cc: Emacs-Devel

> Resending for review.

Could you describe what is the behavior of your function when there are
more than 1 windows in the frame?  After trying it out, I'm still not
sure.  I get the impression that part of the problem is that you
calculate a frame size that would allow all windows to have the right
size, but you don't actually resize the windows, so they get a somewhat
arbitrary size which may or may not be the right one.

Also when the C-u arg is provided, it seems to resize the frame to the
size it should have if it displayed only 1 window, whereas of course
it's not the case: there are other windows there (otherwise C-u makes
no difference anyway).  So again, the result is somewhat unexpected.
I'd have expected it to resize the frame so that the window fits the
buffer, and so that the other windows stay "unchanged".


        Stefan




^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: FW: fit-frame.el
  2008-03-10 15:34 ` Stefan Monnier
@ 2008-03-10 17:04   ` Drew Adams
  2008-03-10 18:24     ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Drew Adams @ 2008-03-10 17:04 UTC (permalink / raw)
  To: 'Stefan Monnier'; +Cc: 'Emacs-Devel'

> Could you describe what is the behavior of your function when 
> there are more than 1 windows in the frame?  After trying it out,
> I'm still not sure. 

With no prefix arg, the frame is fit to all of the buffers displayed in it.
This means that, within the maximum frame-size limits (see the doc for these
- user variables), each buffer contributes to the frame size needed. There
is no explicit resizing of windows within the frame. If each buffer can be
shown fully (no display wrapping) with the frame still within its limits,
then each buffer will be shown fully.

The same fitting algorithm is applied to each buffer, to try to find what
would be an ideal fit for it alone. The requisite widths and heights are
summed to produce the overall ideal frame size to show all of the frame's
buffers. Then the frame-size limits are applied.

Obviously, frame fitting is most useful for either one-window frames or
frames with some buffers that are narrow or short (depending on their
positions).

> I get the impression that part of the problem is that you
> calculate a frame size that would allow all windows to have the right
> size, but you don't actually resize the windows, so they get 
> a somewhat arbitrary size which may or may not be the right one.

You can put it that way, if you like. It is by design that there is no
explicit attempt to resize the windows, relative to each other, to achieve
an overall "best window configuration", whatever that might mean (depends on
what you want, in general and in any specific context). But the relative
sizes of windows can change as a side effect, of course.

It's not obvious, for instance, that you would want, say, a proportional
reduction of each window from its ideal shrink-fit size, in order to fit all
of them within the calculated frame size. You might instead want to
privilege one window (display its buffer fully, if possible, or as much of
it as possible). Or you might instead want to privilege some subset of the
windows in different ways.

You might have a set of buffers (e.g. with names matching some regexp) that
you want to always receive more emphasis (privilege during resizing), and
you might have another set that you want to receive less emphasis. You might
also want buffer mode to be taken into account - for example, it make little
sense to measure the width of a line in longlines mode.

Because the possibilities of window sizes within a given frame are numerous
and can depend on user preference, which buffers and modes are involved, and
other things (phase of moon?), it would be an interesting discussion to come
up with a good design that DTRT generally and lets users easily control
relative window resizing.

Designing different window resizing possibilities and how to specify
interactively which of them you want at any time, perhaps in combination
with frame resizing, is, in any case, a task for the future.

There is this TODO item in fit-frame.el:

;;  Emacs needs a command similar to `fit-frame'
;;  for windows, that is, a command that will fit
;;  the existing windows of a frame to their
;;  buffers, as well as possible.  That could be
;;  then be used in combination with `fit-frame'.

Until that is available, I think that `fit-frame' does the best that can be
expected. The best use case for it is one-window-p frames, but it can also
be useful for other frames, depending on what they are showing. When you are
in a context where it is not useful, don't use it. ;-)

> Also when the C-u arg is provided, it seems to resize the frame to the
> size it should have if it displayed only 1 window,

Correct (for plain C-u).

> whereas of course it's not the case: there are other windows
> there (otherwise C-u makes no difference anyway).  So again,
> the result is somewhat unexpected.

It's not unexpected if that's what's described in the doc. In many cases,
you might not want that behavior. Don't use plain `C-u' when you don't want
that behavior.

> I'd have expected it to resize the frame so that the window fits the
> buffer, and so that the other windows stay "unchanged".

As I said, windows are not resized, except as a side effect of fitting the
frame. Integrating possible window resizing with frame resizing can be a
future challenge. It's not obvious what window-resizing behavior one might
want in different contexts.

When that possible combination (frame and window resizing) is considered, it
might be useful to revisit the prefix-arg and user option possibilities.
More or different options and prefix-arg values might need to be considered,
to take into account mixing frame and window resizing.

The main use case to think about here is one-window frames. The default
behavior (no prefix arg) also does something for multi-window frames that
can be useful in some contexts, but I expect that most people will use this
as I do, to resize one-window frames.

Another important use case is calling it from Lisp code.

Note: There is a companion library, autofit-frame.el, that provides
automatic frame-fitting for the one-window case (only). However, that
library redefines display-buffer, switch-to-buffer, and pop-to-buffer.
Richard suggested that that logic be incorporated into the C source code,
after fit-frame.el has been added to Emacs. I won't be coding that C code
myself, but I can provide the Lisp code to help with the logic. IMO, it is
very handy to some users, such as I, who use frames the way most users use
windows. Use of automatic frame fitting is optional, of course. 

HTH.





^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: FW: fit-frame.el
  2008-03-10 17:04   ` Drew Adams
@ 2008-03-10 18:24     ` Stefan Monnier
  2008-03-10 20:45       ` Drew Adams
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Monnier @ 2008-03-10 18:24 UTC (permalink / raw)
  To: Drew Adams; +Cc: 'Emacs-Devel'

>> Could you describe what is the behavior of your function when
>> there are more than 1 windows in the frame?  After trying it out,
>> I'm still not sure.

> With no prefix arg, the frame is fit to all of the buffers displayed in it.
> This means that, within the maximum frame-size limits (see the doc for these
> - user variables), each buffer contributes to the frame size needed. There
> is no explicit resizing of windows within the frame. If each buffer can be
> shown fully (no display wrapping) with the frame still within its limits,
> then each buffer will be shown fully.

To me, as a user of your code, I found this behavior to look like your
code is just buggy: after calling fit-frame on a frame with several
windows, the result was a frame of different size, where some of the
windows were too small for their buffers and others were too large, so
it looked like it was just all messed up.

> There is this TODO item in fit-frame.el:

> ;;  Emacs needs a command similar to `fit-frame'
> ;;  for windows, that is, a command that will fit
> ;;  the existing windows of a frame to their
> ;;  buffers, as well as possible.  That could be
> ;;  then be used in combination with `fit-frame'.

> Until that is available, I think that `fit-frame' does the best that can be
> expected. The best use case for it is one-window-p frames, but it can also
> be useful for other frames, depending on what they are showing. When you are
> in a context where it is not useful, don't use it. ;-)

I'm not convinced that this "best we can do" is useful.

>> Also when the C-u arg is provided, it seems to resize the frame to the
>> size it should have if it displayed only 1 window,

> Correct (for plain C-u).

That's what I inferred from the behavior, not from the docstring.

>> whereas of course it's not the case: there are other windows
>> there (otherwise C-u makes no difference anyway).  So again,
>> the result is somewhat unexpected.

> It's not unexpected if that's what's described in the doc.

The doc says:

  To fit the frame to all of its displayed buffers, use no prefix arg.
  To fit it to just the current buffer, use a plain prefix arg (`C-u').

but it's far from clear what might be meant by "fit a frame to the
current buffer" (unless the current buffer is the only one shown in the
frame, that is).

> The main use case to think about here is one-window frames.

I think it makes sense to restrict its use to one-window-p (and hence
throw away the C-u argument).  And then rename it to fit-frame-to-buffer.
We might also want to have a variant that only shrinks the frame.
Such a variant probably wouldn't need any customization.



        Stefan




^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: FW: fit-frame.el
  2008-03-10 18:24     ` Stefan Monnier
@ 2008-03-10 20:45       ` Drew Adams
  2008-03-11 18:17         ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Drew Adams @ 2008-03-10 20:45 UTC (permalink / raw)
  To: 'Stefan Monnier'; +Cc: 'Emacs-Devel'

> To me, as a user of your code, I found this behavior to look like your
> code is just buggy: after calling fit-frame on a frame with several
> windows, the result was a frame of different size, where some of the
> windows were too small for their buffers and others were too large, so
> it looked like it was just all messed up.

Perhaps users need to read the doc?

More stress on the one-window use case could be added to the doc, if you
think users are likely to try it on frames with many windows without reading
the doc.

> > There is this TODO item in fit-frame.el:
> 
> > ;;  Emacs needs a command similar to `fit-frame'
> > ;;  for windows, that is, a command that will fit
> > ;;  the existing windows of a frame to their
> > ;;  buffers, as well as possible.  That could be
> > ;;  then be used in combination with `fit-frame'.
> 
> > Until that is available, I think that `fit-frame' does the 
> > best that can be expected. The best use case for it is
> > one-window-p frames, but it can also be useful for other
> > frames, depending on what they are showing. When you are
> > in a context where it is not useful, don't use it. ;-)
> 
> I'm not convinced that this "best we can do" is useful.

No one is forced to call the command. I find it very useful for the
one-window case, and I think it can also be useful for multiple windows,
especially for few windows. It might not be super useful if you have 13
windows in a frame, but for simple cases it can be handy, IMO. The point is
that you only use it when it makes sense for you.

If you think there is some "danger" in providing the multiple-window
behavior, we can add an option that, if non-nil, raises an error if the
command is called for a frame with more than one window. I think that's
silly, but I think I understand what you are saying.

> >> Also when the C-u arg is provided, it seems to resize the 
> >> frame to the size it should have if it displayed only 1 window,
> 
> > Correct (for plain C-u).
> 
> That's what I inferred from the behavior, not from the docstring.
> 
> >> whereas of course it's not the case: there are other windows
> >> there (otherwise C-u makes no difference anyway).  So again,
> >> the result is somewhat unexpected.
> 
> > It's not unexpected if that's what's described in the doc.
> 
> The doc says:
> 
>   To fit the frame to all of its displayed buffers, use no prefix arg.
>   To fit it to just the current buffer, use a plain prefix 
>   arg (`C-u').
> 
> but it's far from clear what might be meant by "fit a frame to the
> current buffer" (unless the current buffer is the only one 
> shown in the frame, that is).

What is meant by "fit a frame to the current buffer" is detailed in the doc
string, starting with "_Fitting a non-empty buffer means_ resizing the frame
to the smallest size such that the following are both true:".

That describes what is meant by fitting a frame to a (non-empty) single
buffer.

> > The main use case to think about here is one-window frames.
> 
> I think it makes sense to restrict its use to one-window-p (and hence
> throw away the C-u argument).  And then rename it to 
> fit-frame-to-buffer.

That was the original behavior. I added the multi-window behavior at user
request. It is useful in some cases. Example: two windows, top and bottom
(e.g. Lisp code and Info). You can resize the frame to shrink-wrap either of
the buffers.

If you have only one window, then the behaviors with and without C-u are the
same.

Why prohibit a user from using this in situations where it is useful?

> We might also want to have a variant that only shrinks the frame.
> Such a variant probably wouldn't need any customization.

Only shrinks it to what size? Using what criteria? There are plenty of
commands to shrink or enlarge frames (I have some). The use case of
fit-frame.el is to fit frames to buffers.






^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: FW: fit-frame.el
  2008-03-10 20:45       ` Drew Adams
@ 2008-03-11 18:17         ` Stefan Monnier
  2008-03-11 18:45           ` Drew Adams
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Monnier @ 2008-03-11 18:17 UTC (permalink / raw)
  To: Drew Adams; +Cc: 'Emacs-Devel'

>> but it's far from clear what might be meant by "fit a frame to the
>> current buffer" (unless the current buffer is the only one 
>> shown in the frame, that is).

> What is meant by "fit a frame to the current buffer" is detailed in the doc
> string, starting with "_Fitting a non-empty buffer means_ resizing the frame
> to the smallest size such that the following are both true:".

This description is an algorithm, i.e. code written in English.
Most people are poor at reading such things, because human languages are
very ambiguous and elliptic in nature.  To make the docstring clear,
you'd need at the very least to somehow mark "fit a frame" in some kind
of special way, so people understand that this is a special term with
a very narrow meaning, explained elsewhere.  And even then it's not
clear that by "fit a frame to the current buffer" you really mean
"pretend there's no other window", especially since the resulting
behavior is just really odd.

> Why prohibit a user from using this in situations where it is useful?

Because it's clearly not a finished feature.  It's like a car without
windshield and doors and with a trunk that doesn't close: sure it can
come in handy sometimes, but to most people it's just a broken car.

When the feature is completed we can reconsider it.

>> We might also want to have a variant that only shrinks the frame.
>> Such a variant probably wouldn't need any customization.

> Only shrinks it to what size?

fit-frame can grow or shrink, depending on the situation.  I'm proposing
to provide a restriction of it that only shrinks (i.e. use the current
size as an upper bound on the desired final size).


        Stefan




^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: FW: fit-frame.el
  2008-03-11 18:17         ` Stefan Monnier
@ 2008-03-11 18:45           ` Drew Adams
  2008-03-11 21:00             ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Drew Adams @ 2008-03-11 18:45 UTC (permalink / raw)
  To: 'Stefan Monnier'; +Cc: 'Emacs-Devel'

> >> but it's far from clear what might be meant by "fit a frame to the
> >> current buffer" (unless the current buffer is the only one 
> >> shown in the frame, that is).
> 
> > What is meant by "fit a frame to the current buffer" is 
> > detailed in the doc string, starting with "_Fitting a
> > non-empty buffer means_ resizing the frame
> > to the smallest size such that the following are both true:".
> 
> This description is an algorithm, i.e. code written in English.
> Most people are poor at reading such things, because human 
> languages are very ambiguous and elliptic in nature.  To make
> the docstring clear, you'd need at the very least to somehow
> mark "fit a frame" in some kind of special way, so people
> understand that this is a special term with a very narrow
> meaning, explained elsewhere.  And even then it's not
> clear that by "fit a frame to the current buffer" you really mean
> "pretend there's no other window", especially since the resulting
> behavior is just really odd.

You can put "Fitting" in quotes, in "_Fitting a non-empty buffer means_", if
you think that helps (I don't). If not, propose an improvement to the doc
string.

I spent several iterations over the doc string with Richard already. If you
have a concrete suggestion, feel free to send it along.
 
> > Why prohibit a user from using this in situations where it 
> > is useful?
> 
> Because it's clearly not a finished feature.  It's like a car without
> windshield and doors and with a trunk that doesn't close: sure it can
> come in handy sometimes, but to most people it's just a broken car.

I don't see anything that's unfinished. It works fine with one-window
frames. It works fine with more than one window also, if you want to resize
the frame so that a particular buffer (window) is displayed without
wrapping. That is the feature, and it does that fine.

You seem to be wanting something that it doesn't pretend to do. And
something that you can't even describe (!): balance window sizes in some
undefined ideal fashion. That's a different feature altogether.

> When the feature is completed we can reconsider it.

OK, forget about it.

> >> We might also want to have a variant that only shrinks the frame.
> >> Such a variant probably wouldn't need any customization.
> 
> > Only shrinks it to what size?
> 
> fit-frame can grow or shrink, depending on the situation.  
> I'm proposing to provide a restriction of it that only shrinks
> (i.e. use the current size as an upper bound on the desired final
> size).

I understood. The question is: shrinks to what size?

It's easy enough to have a variant that does the same thing as now, but
prohibits any expansion, if that's all you mean. I don't see why that's
particularly useful, but it's easy enough to implement. 

And you can add a variant that only grows, instead of only shrinks. And a
variant that only shrinks or only grows vertically, or horizontally...

But it sounds like you're not interested, in general, so there's no sense
bothering with any of that.






^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: FW: fit-frame.el
  2008-03-11 18:45           ` Drew Adams
@ 2008-03-11 21:00             ` Stefan Monnier
  0 siblings, 0 replies; 8+ messages in thread
From: Stefan Monnier @ 2008-03-11 21:00 UTC (permalink / raw)
  To: Drew Adams; +Cc: 'Emacs-Devel'

> I don't see anything that's unfinished. It works fine with one-window
> frames. It works fine with more than one window also, if you want to resize
> the frame so that a particular buffer (window) is displayed without
> wrapping. That is the feature, and it does that fine.

Open a new frame, split it with C-x 2, in the top-window place
a 2-line buffer, in the bottom-window place a 20-line buffer.  Then M-x
fit-frame.  Your 2-line buffer will be in a window too-tall for its
2 lines, and the botton window will not be tall enough.

For line-wrapping, repeat the above example but with C-x 3 and with
columns and instead of lines.

> You seem to be wanting something that it doesn't pretend to do. And
> something that you can't even describe (!): balance window sizes in some
> undefined ideal fashion. That's a different feature altogether.

Your code computes a number of lines and columns it wants to attribute
to each window, so it should at least try to change the window
size accordingly.

>> > Only shrinks it to what size?
>> fit-frame can grow or shrink, depending on the situation.  
>> I'm proposing to provide a restriction of it that only shrinks
>> (i.e. use the current size as an upper bound on the desired final
>> size).

> I understood. The question is: shrinks to what size?

> It's easy enough to have a variant that does the same thing as now, but
> prohibits any expansion, if that's all you mean.

Yup, that's all I mean.  Not sure what else it could mean.

> I don't see why that's particularly useful, but it's easy enough
> to implement.

Because it's pretty safe, so can be called at many more places.
Just like the difference between fit-window-to-buffer and
shrink-window-if-larger-than-buffer.

Also, as mentioned it probably doesn'y need *any* custom variable (tho
this is largely just a side-effect of being safe).

> And you can add a variant that only grows, instead of only shrinks. And a
> variant that only shrinks or only grows vertically, or horizontally...

I've never found a need for such a variant.  OTOH an `undo' might be
handy if the user tries fit-frame and finds that the new size doesn't
"fit".


        Stefan




^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-03-11 21:00 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-10  4:44 FW: fit-frame.el Drew Adams
2008-03-10 15:34 ` Stefan Monnier
2008-03-10 17:04   ` Drew Adams
2008-03-10 18:24     ` Stefan Monnier
2008-03-10 20:45       ` Drew Adams
2008-03-11 18:17         ` Stefan Monnier
2008-03-11 18:45           ` Drew Adams
2008-03-11 21:00             ` Stefan Monnier

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).