All Manuals > CAPI User Guide and Reference Manual > 12 Creating Panes with Your Own Drawing and Input


12.4 output-pane scrolling

An output-pane or an instance of any of its subclasses can be made to scroll by passing the :vertical-scroll and/or :horizontal-scroll initargs which are inherited from simple-pane.

By default, the scrolling is what is called ordinary scrolling . In this case you just need to specify that you want scrolling by :vertical-scroll and/or :horizontal-scroll, and maybe also specify the internal scroll dimension(s) (see below).

In ordinary scrolling, all the interactions are done as if the pane has an "internal canvas" with dimensions (the "internal dimensions") which are different from the visible dimensions on the screen, and typically larger. The coordinates of input gestures and drawing in the pane are all with respect to this internal canvas. Only part of the canvas is displayed at any one time, depending on the position of the scroll slugs. The effect of scrolling is to change what part of the pane is visible, which causes a display-callback to draw any newly visible areas. However, the call to the display-callback is an ordinary call like any call (for example, like a call as result of part of the window being exposed), and the display-callback does not need to know anything about scrolling.

If you need to know when scrolling happened, rather than just display what is needed to display, you can use the :scroll-callback initarg to specify a callback that is called before the display-callback . However, this is not required for ordinary scrolling to work.

The internal dimensions of the pane can be specified by the initargs :scroll-height and :scroll-width, and can also be set dynamically set by set-vertical-scroll-parameters and set-horizontal-scroll-parameters. Some subclasses can compute their internal dimensions, for example graph-pane computes its internal dimensions to show all the graph, and static-layout and its subclass pinboard-layout by default compute the internal dimensions to fit their children (unless fit-size-to-children is nil).

For example, create an output-pane with vertical scroll and internal height of 600 pixels, minimum visible height of 300 pixels, and a display-callback that prints the y coordinate and the height and displays a green square at (0,100) of size 10x10 and a blue square at (0,400) of size 10x10:

(defun my-display-callback (pane x y width height)
  (declare (ignore x width))
  (format t " y = ~d,  height =  ~d~%" y height)
  (gp:draw-rectangle pane 0 100 10 10
                     :foreground :green :filled t)
  (gp:draw-rectangle pane 0 400 10 10
                     :foreground :blue :filled t))
(setq output-pane
      (make-instance 'capi:output-pane
                      :vertical-scroll t
                      :scroll-height 600
                      :visible-min-height 300
                      :display-callback 'my-display-callback))

Then display it:

(capi:contain output-pane)

When it appears on the screen its height is 300 pixels, the scrollbar is half the height. You receive a display callback with y being 0 and height 300. You see the green square 100 pixels down from the top. The blue square is invisible, because it is drawn at y = 400, which is not inside the visible area.

Now if you scroll to the bottom, you will receive a callback with y = 300 and height still 300 (possibly after several callbacks with intermediate y values). Now you see the blue square 100 pixels from the top, and the green square is invisible.

Note that the display callback knows nothing about the scrolling. It just draws. A real display callback may be made faster by avoiding the drawings which are not going to be visible, for example:

(defun my-display-callback-1 (pane x y width height)
  (declare (ignore x width))
  (format t " y = ~d,  height =  ~d~%" y height)
  (unless (or (> y 110) (< (+ Y height) 100) (> x 10))
    (gp:draw-rectangle pane 0 100 10 10
                       :foreground :green :filled t))
  (unless (or (> y 410) (< (+ Y height) 400) (> x 10))
     (gp:draw-rectangle pane 0 400 10 10
                        :foreground :blue :filled t)))

but this is just optimization. It does not affect what is shown on the screen.

The other type of scrolling is called internal scrolling (sometimes "pane scrolling"), and it is set up by passing the output-pane initarg :pane-can-scroll t. In general, internal scrolling is more complex to use, but allows more flexible scrolling.

In internal scrolling, the drawing is done in coordinates with respect to the visible area. Input interactions are done as in ordinary scrolling, in particular the coordinates in the callbacks of the input-model take scrolling into account. Scrolling gestures by the user does not actually perform scrolling, and you need to use :scroll-callback. The scroll-callback is responsible for doing something to ensure that "scrolling" happened.

In general, the scroll-callback will have to use some of the functions set-vertical-scroll-parameters, set-horizontal-scroll-parameters, get-vertical-scroll-parameters and get-horizontal-scroll-parameters to get and set scroll values. Some of these values may be set by :scroll-* initargs of output-pane. See set-vertical-scroll-parameters for the details. scroll-callback may also do other computations.

Once it has performed its setups scroll-callback needs to ensure display, by calling invalidate-rectangle on the area (or on each of multiple areas) that need(s) to be redisplayed.

For example how pane-can-scroll can be used to implement operations other than simple scrolling see:

(example-edit-file "capi/output-panes/pane-can-scroll")

CAPI User Guide and Reference Manual (Macintosh version) - 25 Feb 2015