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

Re: Dynamically resizing editable-text-dialog-items?

On: Mon, 15 Nov 1993, Peter Szolovits writes about
problems scrolling an editable-text-dialog-item:
> I am trying to make an editable text field
> whose size grows and shrinks depending on the size of its contents (like,
> for example, the file and folder names in the Finder).  Alas, when I type
> at the end of one of these, the front few characters scroll off into
> invisibility, and I am left with a corresponding amount of white space at
> the right end.  This makes some sense because there needs to be room for
> the new char, but it doesn't help to add extra space.  I would like to be
> able to turn off this horizontal scrolling, or control it so it is not
> triggered.
Here's the original code:
> (defparameter *w* (make-instance 'dialog))
> (defclass etdi (editable-text-dialog-item)
  ((origin :accessor origin)
   (extra-width :accessor extra-width))
    :dialog-item-text "Untitled"
    :view-size #@(55 16)                ;; empirically determined "Untitled" in
Chicago 12.
                                        ;; I have tried upping the 55, to no
> (defmethod initialize-instance :after ((di etdi) &key)
  (let ((p (view-position di))
        (s (view-size di)))
    (setf (origin di)
          (make-point (+ (point-h p) (round (point-h s) 2))
                      (+ (point-v p) (round (point-v s) 2))))
    (setf (extra-width di)              ; this accounts for width-correction
          (- (point-h s)                ; and frame, I think
             (string-width (dialog-item-text di) (view-font di))))
> (defmethod dialog-item-action ((di etdi))
  (recalc-posn di)
  ;; (fred-update di)                      ; this doesn't seem to matter either
> (defun recalc-posn (di)
  (let ((o (origin di))
        (hs (string-width (dialog-item-text di) (view-font di))))
    (set-view-size di
                   (+ hs (extra-width di))
                   (point-v (view-size di)))
    (set-view-position di
                       (- (point-h o)
                          (round (+ hs (extra-width di)) 2))
                       (- (point-v o)
                          (round (point-v (view-size di)) 2)))))
> (add-subviews *w* (make-instance 'etdi :view-position #@(100 100)))

Here's the corrected code which does what you want. 
The text dialog item is centered in the window. 
You could be more clever by computing the font
ascent and descent and font width to make the correction 
and compute the height of the text box.
The code relies on the fact that "M" is the widest character.
The code does not handle very wide text. It should insert
a line feed between words and wrapp the rest of the line
onto the next line, increasing the height of the dialog item
and adjusting the width.

(defparameter *w* nil)
(defparameter *edti* nil)
(defclass etdi (editable-text-dialog-item)
  ((origin :accessor origin)            ; slot is not used now!
   (extra-width :accessor extra-width))
    :dialog-item-text "Untitled"
    :view-size #@(55 16)))
(defmethod initialize-instance :after ((di etdi) &key)
  (setf (origin di) #@(0 0))
  (setf (extra-width di) (* 2 (string-width "M" (view-font di))))
  (recalc-di di))

(defmethod set-view-font :after ((di etdi) font-spec)
  (declare (ignore font-spec))
  (recalc-di di))
(defmethod dialog-item-action ((di etdi))
  (recalc-di di))

(defun recalc-size (di)
  (let* ((hs (string-width (dialog-item-text di) (view-font di)))
        (correction (extra-width di))
        (new-width (+ hs correction))
        (new-height (point-v (view-size di))))
    (values new-width new-height)))
(defun recalc-di (di)
  (multiple-value-bind (new-width new-height)
                       (recalc-size di)
    (recalc-posn di new-width new-height)))

(defun recalc-posn (di new-width new-height)
  (set-view-size di new-width new-height)
  (when (view-container di)
    (let* ((container (view-container di))
           (container-size (view-size container))
           (container-h (point-h container-size))
           (container-v (point-v container-size)))
      (set-view-size di (setq new-width (min new-width container-h))
                     (setq new-height (min new-height container-v)))
      (set-view-position di
                         (max 0 (round (- container-h new-width) 2))
                         (point-v (view-position di))))))

(defmethod set-dialog-item-text :after ((di etdi) text)
  (declare (ignore text))
  (recalc-di di))
(setq *w* (make-instance 'dialog))
(add-subviews *w* (setq *edti* (make-instance 'etdi :view-position #@(100
(set-dialog-item-text *edti* "Untitled")

(view-origin *edti*)

(view-position *edti*)