From joe@trystero.art.com Thu Jan 14 21:20 MET 1993
Received: from freya.let.rug.nl by tyr.let.rug.nl with SMTP
	(16.8/16.2) id AA02636; Thu, 14 Jan 93 21:20:24 +0100
Return-Path: <joe@trystero.art.com>
Received: from trystero.art.com by freya.let.rug.nl with SMTP
	(16.8/16.2) id AA03409; Thu, 14 Jan 93 21:16:13 +0100
Received: by trystero.art.com (910711.SGI/910805.SGI)
	for bert@let.rug.nl id AA21880; Thu, 14 Jan 93 12:16:14 -0800
Date: Thu, 14 Jan 93 12:16:14 -0800
From: joe@trystero.art.com (Joe English)
Message-Id: <9301142016.AA21880@trystero.art.com>
Mime-Version: 1.0
To: bert@let.rug.nl, totty@cs.uiuc.edu, jjhayes@brn.ca
Subject: FWF Scrolling Widgets Protocol
Cc: joe@trystero.art.com
Content-Type: multipart/mixed;
	boundary="PART.BOUNDARY.105.21823.IRIX.727042571.2"
Status: RO

> THIS IS A MESSAGE IN 'MIME' FORMAT.  Your mail reader does not support MIME.
> Some parts of this will be readable as plain text.
> To see the rest, you will need to upgrade your mail reader.

--PART.BOUNDARY.105.21823.IRIX.727042571.2
Content-type: text/richtext
Content-Transfer-Encoding: quoted-printable

<nl>
Here's what I believe is the final draft of the Scrolling Widgets
Interface Policy.  Also included is <Xfwf/scroll.h>.  Will send
scroll.c  as soon as it's written (not much to it.)
<nl>
<nl>
Copies to:
<nl>
<nl>	Bert Bos (co-developer)
<nl>	Brian Totty (head honcho)
<nl>	Jeff Hayes (documentation coordinator)
<nl>	=


--PART.BOUNDARY.105.21823.IRIX.727042571.2
Content-type: text/plain
Content-Transfer-Encoding: quoted-printable


                 Free Widget Foundation
            Scrolling Widgets Interface Policy
	     Draft Proposal, Third Revision

	    Joe English <joe@trystero.art.com>
	        Bert Bos <bert@let.rug.nl>


Introduction
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D

This document describes a common programmatic interface between
``scroller'' widgets (such as scrollbars, panners, and zoomers) and
``scrollee'' widgets (such as lists, text editors, and canvases).  A
common protocol is defined so that widgets of different origins and
parentage may interoperate simply by following these policies.  The
interface is a message-passing protocol which is implemented using
Xt's callback mechanism.  The callback mechanism is used instead of
widget class methods so that there is no prescribed base class.

Scrolling may occur in the horizontal or vertical axes.  Zooming is
considered as a special case of scrolling.  Both axes may be
controlled by a single widget (e.g., a Slider2), and zooming may be
supported by some widgets (e.g., a Slider4).  There are separate
mechanisms for dragging the scroller continuously and for
instantaneously setting a new position.  This distinction is made for
scrollee widgets which take a long time to redraw.

Scrollee widgets may have internal scrolling capabilities as well
(like a List), and the scroller must be updated whenever the scrollee
decides to move.  It should also be possible to have a widget be
controlled by more than one scroller; e.g., a horizontal and a
vertical scrollbar and a Slider4 could all be attached to a single
canvas widget, and all four widgets must be kept synchronized.  In the
most complex case, two scrollee widgets may be linked together and
kept synchronized through this protocol.

There are many user requests which scroller widgets may wish to
support but cannot always interpret.  For example, if a scrollbar is
attached to a multi-font text editor and the user presses the 'line
up' button on the scrollbar, only the text editor knows how far to
scroll up.  Because of this, the protocol uses a request-response
mechanism: Scroller widgets send a _command_ message to a attached
scrollee, and the scrollee replies by sending a _notify_ message back
to all attached scrollers.  Scrollee widgets may independently
generate notify messages if some user action causes scrolling.  The
protocol is symmetric, however: all widgets must be prepared to
respond to all messages types, whethere they were designed primarily
as scrollers or as scrollees.


Programmatic Interface:
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D

A widget's visible area in each axis is defined by two floating point
numbers.  The _size_ field is the fraction of the total area which is
visible in the range of zero to one inclusive.  The _position_ field
represents the topmost (or leftmost) visible point of the total area;
it must be in the range of zero to one inclusive.  Some widgets may
further restrict the position range to [0, 1-size] if ``scrolling off
the bottom'' is not allowed.

If the total displayable area is less than or equal to than the
widget's size (e.g., a 10-line list with only 5 items present) then
the size field should be set to one.

Every widget class which supports this scrolling interface must
provide two resources:

    XtNscrollCallback (callback list)

        Invoked by the widget in response to any user input or
	internal state change which causes the widget to scroll.  The
	widget must pass a pointer to an XfwfScrollInfo (see below) as
	the call_data argument.

    XtNscrollResponse (calllback procedure)

	This is a pointer to a callback function, which is attached to
	a sibling widget's scrollCallback callback list.  The
	scrollResponse procedure has the following signature:

	void scrollResponseCB(Widget w,XtPointer closure,XtPointer call_data);

        where _w_ is the widget invoking the scroll operation,
	_closure_ is the receiving widget =

		(cast to a (Widget)),
	and _call_data_ points to the scroll message =

		(cast to an (XfwScrollInfo *)). =


To connect two scrolling widgets, the application attaches the first
widget's scrollResponse callback to the second's scrollCallback
callback list and vice versa, as follows:


void XfwfConnectScrollingWidgets(Widget w1, Widget w2)
{
    XtCallbackProc response_cb_1 =3D NULL, response_cb_2 =3D NULL;

    /* Error checking omitted */
    XtVaGetValues(w1, XtNscrollResponse, &response_cb_1, NULL);
    XtVaGetValues(w2, XtNscrollResponse, &response_cb_2, NULL);

    XtAddCallback(
	widget1,
	XtNscrollCallback,
	response_cb_2,
	(XtPointer)widget2);

    XtAddCallback(
	widget2,
	XtNscrollCallback,
	response_cb_1,
	(XtPointer)widget1);
}


For any widget that wasn't designed with the scrolling policy in mind,
the application will have to provide its own scrollResponse callback
procedure.  It will also have to reconfigure the scroller by hand if
the scrollee has internal scrolling.

A widget passes a scroll message to other connected widgets by
invoking its XtNscrollCallback.  A widget receives a scroll message
when its scrollResponse function is invoked (note that the receiving,
or ``self,'' widget is the second parameter (XtPointer closure), not
the first parameter (Widget w).)

The scroll message is defined by an XfwfScrollInfo structure, which
contains:

    * a 'reason' field, indicating the type of scroll message;
    * a 'flags' field indicating which of the size and position
      fields are valid for this message;
    * the new or suggested size and position as specified by 'flags'.

Not all widgets will support all fields.  For example, a horizontal
scrollbar would simply ignore the vertical size and position fields,
and a text or list widget would ignore any requests to zoom.

There are several possible values for the 'reason' field.  A reason of
XfwfSNotify indicates a _notify_ message, and all other reasons
indicate a _command_ message.  A widget must always respond to a
command message by generating a notify message in return; a widget
must not respond to a notify message except to update its internal
state and redraw itself.


Data structures:
--------------------

/*
 * File: <Xfwf/scroll.h> (excerpts)
 */

/*
 * The XfwfSReason typedef defines all the possible types
 * of scroll messages.  XfwfSNotify indicates a _notify_
 * message, and all others indicate a _command_ message.
 */
typedef enum _XfwfSReason
{
    XfwfSNotify,        /* Widget has changed position or size */

    XfwfSMove,          /* Request to move widget */
    XfwfSDrag,          /* widget is being moved continuously */

    XfwfSZoom,       	/* Request to Zoom (resize visible area) */
    XfwfSStretch,       /* widget is being zoomed continuously */

    XfwfSUp,            /* User request to ``move up one unit'' */
    XfwfSLeft,          /* User request to ``move left one unit'' */
    XfwfSDown, XfwfSRight,      /* similar */

    XfwfSPageUp,        /* User request to ``move up one page'' */
    XfwfSPageLeft, XfwfSPageDown, XfwfSPageRight,       /* similar */

    XfwfSZoomIn,        /* User invoked ``Zoom In'' */
    XfwfSZoomOut,       /* User invoked ``Zoom Out'' */

    XfwfSTop,		/* User invoked ``Scroll to top'' */
    XfwfSBottom, XfwfSLeftSide, XfwfSRightSide, /* similar */

    XfwfSZoomInFull, XfwfSZoomOutFull  /* similar, but wrt zoom state */

} XfwfSReason;

/* =

 * #define's for the 'flags' field:
 */
typedef unsigned short XfwfSFlags;
#define XFWF_VPOS       0x1     /* vpos set */
#define XFWF_VSIZE      0x2     /* vsize set */
#define XFWF_HPOS       0x4     /* hpos set */
#define XFWF_HSIZE      0x8     /* hsize set */

/*
 * The XfwfScrollInfo structure defines the scroll message passed
 * between scrolling widgets:
 */
typedef struct _XfwfScrollInfo
{
        XfwfSReason     reason;         /* see above */
        XfwfSFlags      flags;          /* defines which fields are relev=
ant */
        float           vpos;           /* "top" position, [0..1] */
        float           vsize;          /* total visible vertical size [0=
=2E.1] */
        float           hpos;           /* "left" position */
        float           hsize;          /* total visible horizontal size =
*/
} XfwfScrollInfo;



PROTOCOL:
=3D=3D=3D=3D=3D=3D=3D=3D

Although in principal the scrolling policy is symmetric, in practice
there is a distinction between scroller widgets and scrollee widgets. =

An overview is presented first, followed by more specific details as
they apply separately to scrollers and scrollees.

Overview:
--------

Widgets generate _command_ messages in response to a user action.

Widgets generate _notify_ messages in response to a command message,
and when they scroll internally.

When receiving any message a widget may adjust the specified
configuration parameters, and should redraw itself.  If it was *not* a
notify message, the widget must respond by generating a notify message
of its own specifying the the configuration actualy used.

A widget should reconfigure and redraw itself:
  =

   * After it receives a notify message =

   * Before it generates a notify message =


It should also reconfigure itself after it generates a command message
if no notify messages were received in reply.



Scroller policy -- Receiving messages:
-------------------------------------


Upon receiving a notify message, scroller widgets must update their
internal state and redraw themselves.  They may ignore any position
and size fields not relevant to their configuration.

Scroller widgets may receive command messages as well, and they must
respond by invoking their scrollCallback list with a notify message. =

They should also update their internal state as appropriate and redraw
themselves.  The position and size fields, if not specified in the
original message, should be computed as if the user had invoked the
corresponding command.


Scroller policy -- Generating messages:
--------------------------------------

When the user performs an action on a scroller widget, the scroller
widget creates a ScrollInfo message with the appropriate reason, size,
position, and flags fields set, and invokes its scrollCallback
callback list.  Note that it should *not* redraw itself at this point;
that should be done either inside the scroller's scrollResponse
procedure or after the scrollCallback list has been processed.

In many cases the scroller will not be able to specify a new position
or size exactly, but it should make its best guess.  For example, if
the user invokes Page Up, a scroller should create a message with

	reason =3D XfwfSPageUp
	flags =3D XFWF_VPOS
	vpos =3D max(old_vpos - vsize, 0)

Or, if the user invokes Line Up, the scroller should not set any position=

or size fields unless it has some idea of how big a ``line'' is.

The scroller's scrollResponse procedure may keep track of whether or
not it receives a notify message while the scrollCallback list is
being processed.  If so, it must use the position and size specified
in the response message.  If no notify messages are received, it may
use the position and size originally calculated.

NOTE: It is possible for more than one Notify response to be received
if a scroller widget is connected to more than one scrollee.  There is
no prescribed way to resolve this conflict.  Any of the following
actions may be taken:

    * Ignore all but the last (or first) response received;
    * Ignore all responses and use the original values;
    * Issue a warning indicating that the widget is misconfigured.

After the scrollCallback list has been processed, the scroller should
update its internal state with the new position and size values and
then redraw itself if its scrollResponse procedure has not already
done so.


Scrollee policy -- Generating messages:
--------------------------------------

Scrollee widgets must generate a notify message whenever a user action
or internal state change causes it to scroll.  It should fill in all
position and size fields, even those which have not changed.  It must
also generate a notify message in response to a command message.


[ There remains the problem of initial synchronization: --
  As a convention: widgets designed primarily as scrollees
  should generate a Notify message inside their Realize procedure.
  Will this work??? ] =


Scrollee widgets *may* generate command messages instead of notify
messages in response to user actions.  The protocol is the same as for
scrollers in this case.  In particular, it may leave all position and
size fields unset and generate a command message with the intention to
let an attached scroller widgets figure out how far to scroll.  Of
course, there may not *be* any scrollers attached, and those that are
connected might not try to interpret the message either.


Scrollee policy -- Receiving messages:
--------------------------------------

When a scrollee widget receives a command message, it should compute a
new position and size based on:

	* the command reason
	* any position and size fields already set in the message
	* any internal constraints.
	=

It must then reconfigure and redraw itself, and generate a notify
message specifying the position and size actually used.  It must *not*
modify the original XwfScrollInfo structure in place, since other
widgets may be on the invoker's scrollCallback list.

A widget may adjust the position fields to align itself, and may
adjust or set size fields to maintain an aspect ratio.  It may also
reject the request altogether, in which case it should simply reset
the position and size fields to their current values.

For example: if a scrolling list widget with 10 elements receives a
Move message requesting a vertical position of 0.12, it may round this
position to 0.10 so that the second item is precisely aligned with the
top of the visible area. =


Or, if a canvas widget which is currently zoomed to 2x magnification
and panned to the exact center (i.e., vsize=3Dhsize=3D0.5, vpos=3Dhpos=3D=
0.25)
receives a message with

	reason =3D Zoom
	flags =3D HPOS | HSIZE
	hpos =3D 0.1
	hsize =3D 0.9

(i.e., zoom out 90% in the horizontal axis), it may adjust the
vertical size as well to maintain a 1:1 aspect ratio and reply with:

	reason =3D Notify
	flags =3D HPOS | HSIZE | VPOS | VSIZE
	hpos =3D 0.1
	vpos =3D 0		-- or anywhere else, for that matter
	hsize =3D 0.9
	vsize =3D 0.9



When receiving a notify message, scrollee widgets should take the same
action as when receiving a command message except that they must *not*
generate a notify reply; i.e., the widget should simply adjust its
size and redraw itself.


Interpretations of XfwfSReason values:
--------------------------------------

This section describes when the various command reasons should be used
and some additional constraints. =


    Reason              required fields
    -----------------------------------
    XfwfSDrag           HPOS and/or VPOS
    XfwfSStretch        HSIZE and/or VSIZE

        The widget is being dragged interactively, e.g., by dragging a
	scrollbar with the mouse.  The scrollee may draw some kind of
	outline feedback in response to these command messages if
	fully redrawing itself is too time-consuming.

        Multiple Drag (Stretch) requests may appear in rapid sequence. =

	The scroller must follow them with a final Move (Zoom) message
	when the action is complete, to allow attached widgets to
	fully reconfigure themselves.

    XfwfSMove           HPOS and/or VPOS
    XfwfSZoom           HSIZE and/or VSIZE

	The user has requested a specific position (size), e.g., by
	clicking on a scrollbar, or has completed a series of Drag
	(Stretch) operations.


    XfwfSTop		=

    XfwfSBottom	=

    XfwfSPageDown
    XfwfSPageUp   	none; VPOS recommended

    	User request to adjust vertical position.  For XfwfSTop, a
	message with vpos=3D0 is recommended.

	For XfwfSBottom, a message with vpos=3D(1 - vsize) is
	recommended: i.e., the last line should appear at the bottom
	of the visible area.

	For PageUp (PageDown), the current vsize should be subtracted
	(added) to the current vpos.  That is, a ``page'' is
	interpreted as the length of the visible area by default. =

	Attached scrollee widgets may have an entirely different idea
	of logical pages, and are free to replace the suggested
	position with their own calculated position.


    XfwfSLeftSide,
    XfwfSRightSide,
    XfwfSPageLeft,
    XfwfSPageRight	none; HPOS recommended

    	User request to adjust horizontal position.  Similar to
	XfwfSTop, etc., above.

    XfwfSZoomInFull	none; all recommended =


	User request to zoom all the way in (maximum magnification.) A
	widget which generates this message should have an idea of a
	maximum zoom-in factor, either hardcoded or provided by a
	resource.

    XfwfSZoomOutFull	none; all recommended

    	Request to zoom all the way out; normally this will be
	hpos=3Dvpos=3D0, hsize=3Dvsize=3D1.

    XfwfSZoomIn
    XfwfSZoomOut	none; all recommended

    	A reasonable default interpretation of ZoomIn is to scale
	both size fields by a constant factor < 1.

	A reasonable default for ZoomOut is to scale both size fields
	by a constant factor > 1, the reciprocal of the ZoomIn factor.

    For all of the Zoom reasons, the position fields should be
    adjusted to pan the visible to keep it centered at the current
    location.

	    =

    XfwfSUp
    XfwfSDown
    XfwfSLeft
    XfwfSRight		none; none recommended.

    	Unless the invoking scroller widget has some idea of how big a
	horizontal or vertical ``unit'' is, the interpretation of
	these requests is best left to scrollees.



Recommendations:
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D

In addition to making the scrollResponse procedure available as a
widget resource, scrolling widgets should implement it as a class
method to allow subclasses to inherit it.

Scrolling widgets should provide an action named Scroll(), taking a
single case-insensitive argument which is one of the XfwfSReason enum
types, minus the XfwfS prefix.  The action procedure may use the
XfwfCvtStringToScrollReason(char *) convenience function declared in
<Xfwf/scroll.h> This action should operate as if the widget had
received a command message with the corresponding reason and no
position or size fields set.  An action of Scroll(Notify) should
simply generate a Notify message specifying the current position and
size in the relevant axes.

Widgets designed primarily as scrollees should only generate notify
messages. =


Widgets designed primarily as scrollers should not generate notify
messages except in response to receiving a command message.



--PART.BOUNDARY.105.21823.IRIX.727042571.2
Content-type: text/plain
Content-Transfer-Encoding: quoted-printable

/*
 * File: <Xfwf/scroll.h>
 *
 * Declarations and typedefs for the Free Widget Foundation
 * Scrolling Widget Interface Policy.
 * =

 */

#ifndef _XFWF_SCROLL_H
#define _XFWF_SCROLL_H 1

/*
 * The XfwfSReason typedef defines all the possible types
 * of scroll messages.  XfwfSNotify indicates a _notify_
 * message, and all others indicate a _command_ message.
 */
typedef enum _XfwfSReason
{
    XfwfSNotify,        /* Widget has changed position or size */

    XfwfSMove,          /* Request to move widget */
    XfwfSDrag,          /* widget is being moved continuously */

    XfwfSZoom,       	/* Request to Zoom (resize visible area) */
    XfwfSStretch,       /* widget is being zoomed continuously */

    XfwfSUp,            /* User request to ``move up one unit'' */
    XfwfSLeft,          /* User request to ``move left one unit'' */
    XfwfSDown, XfwfSRight,      /* similar */

    XfwfSPageUp,        /* User request to ``move up one page'' */
    XfwfSPageLeft, XfwfSPageDown, XfwfSPageRight,       /* similar */

    XfwfSZoomIn,        /* User invoked ``Zoom In'' */
    XfwfSZoomOut,       /* User invoked ``Zoom Out'' */

    XfwfSTop,		/* User invoked ``Scroll to top'' */
    XfwfSBottom, XfwfSLeftSide, XfwfSRightSide, /* similar */

    XfwfSZoomInFull, XfwfSZoomOutFull  /* similar, but wrt zoom state */

} XfwfSReason;

/* =

 * #define's for the 'flags' field:
 */
typedef unsigned short XfwfSFlags;
#define XFWF_VPOS       0x1     /* vpos set */
#define XFWF_VSIZE      0x2     /* vsize set */
#define XFWF_HPOS       0x4     /* hpos set */
#define XFWF_HSIZE      0x8     /* hsize set */

/*
 * The XfwfScrollInfo structure defines the scroll message passed
 * between scrolling widgets:
 */
typedef struct _XfwfScrollInfo
{
        XfwfSReason     reason;         /* see above */
        XfwfSFlags      flags;          /* defines which fields are relev=
ant */
        float           vpos;           /* "top" position, [0..1] */
        float           vsize;          /* total visible vertical size [0=
=2E.1] */
        float           hpos;           /* "left" position */
        float           hsize;          /* total visible horizontal size =
*/
} XfwfScrollInfo;


/*
 * Application convenience functions:
 *
 * XfwfConnectScrollingWidgets(Widget, Widget) attaches two
 * widgets, which must have the scrollCallback and scrollResponse
 * resources.
 */

extern void XfwfConnectScrollingWidgets( /* Widget w1, Widget w2 */);


/*
 * Widget convenience functions:
 *
 * =

 * XcwfCvtStringToScrollReason(char *s) converts 's' to one
 * of the XfwfS* enumerated values.  The comparison is case-insensitive,
 * and the XfwfS prefix may be present but is not necessary.
 */

extern XfwfSReason XfwfCvtStringToScrollReason(/* char * */);

#endif /* _XFWF_SCROLL_H */

--PART.BOUNDARY.105.21823.IRIX.727042571.2--


