/*
* Copyright (c)1998 by Angry Red Planet.
*
* This code is distributed under a modified form of the
* Artistic License. A copy of this license should have
* been included with it; if this wasn't the case, the
* entire package can be obtained at
* .
*
* A logical GUI layout engine: the programmer describes
* high-level relationships between the different user interface
* object through formal container classes, which then take care
* of their physical placement. The system is completely
* font-sensitive and resizeable.
*
* ----------------------------------------------------------------------
*
* ArpLayoutable.h
*
* The layout engine's base class, which encapsulates
* information about a rectangular UI area and how it
* can be dimensioned. It may be used either as a stand-alone
* object, for simple containers that don't need to interact
* with the user, or combined with a BView class [or subclass]
* to create a GUI object that can be placed in the layout
* system.
*
* ----------------------------------------------------------------------
*
* Known Bugs
* ~~~~~~~~~~
*
* • Archiving is not yet completely implemented.
* At the least, it needs to be fixed to never deep copy
* an attached BView object, and make sure to archive all
* ArpLayoutable children. It would also be nice to be
* able to automatically archive all of an objects Params().
* • I'm not very happy with the parameters and globals stuff.
* Possibly, it should just disappear -- that would shave
* about 1/3 off the size of this class...
* • The SetError() stuff should go away -- it hasn't been
* useful ever since the post-R8 days of exceptions.
* This also implies that performing a series of nested
* allocations will cause a leak if one of the allocations
* throws an exception. This needs to be fixed.
* • The cur_dimens member variable should be made private
* to the class.
*
* ----------------------------------------------------------------------
*
* To Do
* ~~~~~
*
* • Integrate ArpViewWrapper with the ArpLayoutable base
* object?
* • Improve name handling: always change an associated view
* when the layoutable name is changed, return the associated
* view's name instead of the layout object's if possible.
*
* ----------------------------------------------------------------------
*
* History
* ~~~~~~~
*
* Dec 6, 1998:
* First public release.
*
* May 5, 1999:
* - Added "ArpAnySize" constant.
*
*/
#ifndef ARPLAYOUT_ARPLAYOUTABLE_H
#define ARPLAYOUT_ARPLAYOUTABLE_H
#ifndef ARPKERNEL_ARPMESSAGE_H
#include
#endif
#ifndef ARPKERNEL_ARPSTRING_H
#include
#endif
#ifndef _LIST_H
#include
#endif
#ifndef _VIEW_H
#include
#endif
/*
* This class is used to pass information about the basic
* display dimensions of a user interface element. This
* is an extension to the existing GetPreferredSize() function
* call, which provides more detailed information about how
* the object can be dimensioned.
*/
// Place this value into the maximum width/height to allow
// unbounded expansion.
extern const float ArpAnySize;
typedef struct arp_layout_dimens {
// The absolute minimum size the object can be, in pixels.
float min_width, min_height;
// The minimum size the object would prefer to be, in pixels.
float pref_width, pref_height;
// The absolute maximum size the object can use, in pixels.
float max_width, max_height;
} ArpLayoutDimens;
// Quick way to pass no (a.k.a. default) paramters to a function.
extern ArpMessage ArpNoParams;
class ArpLayoutable {
public:
ArpLayoutable(const char* name = 0);
ArpLayoutable(BMessage* data);
virtual ~ArpLayoutable();
// Standard archiving interface.
static ArpLayoutable* Instantiate(BMessage* archive);
virtual status_t Archive(BMessage* data, bool deep=true);
/* Error control. These functions are used to set and get
* the error state of an object, i.e. if a NULL child object
* was added, meaning a child somewhere ran out of memory.
* These are safe to call on NULL objects.
*/
ArpLayoutable* SetError(status_t err);
status_t Error();
/* Initialization functions. These can be called on NULL
* object pointers, and won't behave poorly.
*/
ArpLayoutable* SetLayoutName(const char* inname);
const char* LayoutName(void) const { return name; }
/* Parameters.
* These are values that apply to a specific view object.
* This is primarily modifiable state information, such as
* the background color, font to use, various control
* flags, etc. Their interpretation is derived entirely
* from the particular view object in which they are set.
*
* The parameter interface is through a standard ArpMessage.
* When setting parameters, a reference to an ArpMessage
* object is passed in, and the ArpLayoutable object retrieves
* all the values contained in it that it understands.
* The message is no longer referenced by the object after
* the function returns, and can be safely deleted. The
* object usually stores the values it finds in its own
* members, rather than another ArpMessage, so that they
* are easy to access.
*
* When retrieving an object's parameters, an ArpMessage
* is returned. The caller can either specify a particular
* parameter name it is interested in, and only that one will
* be placed in the return message, or it can specify NULL
* to have the object return all of its parameters.
*
* The ArpLayoutable base class defines these common
* parameters:
*
* BackgroundPen (char*). Default: "BackgroundColor".
* The name of the background pen the view should use.
* This is actually the name of a variable in the
* global name space that contains an rgb_color value.
* ForegroundPen (char*). Default: "ForegroundColor".
* The name of the foreground color of the view, i.e.
* the color text is drawn in on top of the background.
* BasicStyle (char*). Default: "PlainFont".
* The name of the font that normal text should be
* drawn in.
*/
ArpLayoutable* SetParams(const ArpMessage& p);
const ArpMessage Params(const char* name);
static const char* BackgroundColor;
static const char* ForegroundColor;
static const char* BasicFont;
/* Globals.
* These are values that apply to an entire tree of
* layout objects. They are used primarily to define
* common attributes that apply across the layout; this
* is intended to make it easy to create visually
* consistent interfaces that are easy to dynamically
* modify as the program executes.
*
* The global interface is, like parameters, through an
* ArpMessage object. Unlike parameters, however, the
* layout object does not have to make a copy of the values
* contained in the message. Instead, it just stores
* a reference to the message. Since it always has a
* reference to the message, it can look up values when
* needed. This is important, because the parameters
* above usually specify a global variable that contains
* the parameter's value, rather than directly specifying
* the value itself.
*
* By indirectly defining parameter values through this
* global name space, a change in a global parameter value
* can easily be propagated into all of the objects that
* it is used in. This is made easier by the second
* function, RefreshGlobals(). The function is called with
* an ArpMessage that contains only the changed global
* values -- the object can then determine what changed,
* and limit the computation that it needs to do. Note
* that this only informs the object about a change in
* global values; the application itself needs to make
* sure the ArpMessage set by SetGlobals() has actually
* changed.
*
* In addition, changes to the global data space are
* propagated to all of a layout object's children: both
* SetGlobals() and RefreshGlobals() call the same function
* on their children, and a child retrieves the global
* values from its parent when it is attached.
*
* The last method, AddGlobals(), is used by subclasses to
* add new values to the global parameter space. The default
* implementation propagates up the view hierarchy, until the
* ArpLayoutable that contains the global data is reached. It
* returns an error if it couldn't add the value(s) -- for
* example, if one already exists of a different type.
*/
ArpLayoutable* SetGlobals(const ArpMessage* gl);
bool RefreshGlobals(const ArpMessage* gl);
const ArpMessage* Globals(void);
virtual status_t AddGlobals(const ArpMessage* gl);
/* Constraints.
* Unlike the previous two data spaces, constraints are
* not defined by an object itself. Instead, they are
* used by its parent object to hold child-specific
* information about how it is to be layed out. A subclass
* implementation needs to worry little about this:
* The ArpLayoutable class takes care of storing and
* retrieving the values, by simply copying them into
* a local ArpMessage object and returning that object
* when requested.
*/
ArpLayoutable* SetConstraints(const ArpMessage& c);
ArpMessage& Constraints(void);
/* Set this to true to keep the view from refreshing during
* partial updates. If false, it will perform a new layout
* any time its (or one of its childrens') dimensions change.
* When changed to false from being true, any stored-up
* changes will occur then and there.
*/
virtual ArpLayoutable* SetLayoutInhibit(bool state);
bool LayoutInhibit(void) const { return inhibit_layout; }
/* Child manipulation. Work just like BView's respective
* functions. These should -always- be called instead
* those in BView -- they will take care of doing the
* correct things with the layoutable's associated BView
* object and parent/children.
*/
virtual ArpLayoutable* AddLayoutChild(ArpLayoutable* v,
const BMessage& c = ArpNoParams,
ArpLayoutable* before = NULL);
virtual bool RemoveLayoutChild(ArpLayoutable* child);
ArpLayoutable* LayoutParent(void) const { return parent; }
int32 CountLayoutChildren(void) const;
ArpLayoutable* LayoutChildAt(int32 index) const;
ArpLayoutable* NextLayoutSibling(void);
ArpLayoutable* PreviousLayoutSibling(void);
bool LayoutRemoveSelf(void);
ArpLayoutable* FindLayoutable(const char* name);
/* Associated BView information. If this ArpLayoutable
* is associated with an actual BView, OwnerView() must
* return a pointer to it before AddLayoutChild() can be
* called on this object. The BView must then remain
* constant for the entire time the layoutable has a parent
* or any children.
* InView() returns the actual view the layoutable exists
* in -- this is either OwnerView() if there is one, or the
* closest parents' OwnerView().
*/
virtual BView* OwnerView() { return NULL; }
BView* InView() { return in_view ? in_view : (in_view=OwnerView()); }
/* Dimensioning.
* LayoutDimens() is called by others to retrieve this
* object's dimensioning information. It, in turn, will
* call ComputeDimens() if they need to be recomputed.
*/
const ArpLayoutDimens& LayoutDimens(void);
/* Layout.
* These are called to position the object; if they make
* a change, all of its children will automatically be
* updated as well.
*/
virtual void ResizeLayout(float width, float height, bool force = false);
virtual void MoveLayout(float left, float top, bool force = false);
virtual void SetLayout(const BRect& frame, bool force = false);
virtual void RequestLayout(bool force = false);
/* Display. Call this function to cause the view to be
* redrawn.
*/
void InvalidateView(void);
/* Dimensioning. Call this function to cause a recomputation
* the next time dimension information is requested.
*/
void InvalidateDimens(void);
/* Retrieve the layoutables's frame, relative to its parent.
* This is the size of the frame, with left and top being
* relative to the closest parent BView's coordinate system
* (That is, -not- necessarily relative to the BView it is
* in; see below for that.)
* This is the frame that should be used when calling
* LayoutTo() et. al.
*/
BRect LayoutFrame() const { return cur_frame; }
/* Retrieve layoutable's frame, from interior coordinate
* space -- that is, the rendering frame relative to its
* nearest parent BView.
* For objects that have their own associated BView, this
* frame is always positioned at (0,0), as you would expect.
* Otherwise, 'left' and 'top' are the offsets needed to
* draw at the appropriate place in InView().
* This is the region to use, for example, when drawing and
* positioning children inside the object.
*
* (Note: this implies that the ArpLayoutable coordiate
* system is not directly compatible with BView's
* ScrollTo() et al. I'm not sure what will need to be
* done to make these interact together correctly.)
*/
BRect LayoutBounds() const { return cur_bounds; }
/* Focus rendering control. This is intended as a more
* general replacement of BScrollView's mechanism for
* informing its child that it is attached. When a child
* has received focus but can not display this itself, it
* should call its SetFocusShown() function with true; the
* default ArpLayoutable implementation of this function
* simply calls its parent with the same value -- thus the
* need to show focus will move up the instance hierarchy
* until it reaches a parent that can take care of it.
* In this way, any container object can show its children's
* focus.
* (The basic need for this is that we implement our own
* BScrollView-type object that works better with the
* rest of the ArpLayoutable system; however, it can't
* register itself with things like BListView because it
* isn't really a BScrollView.)
*/
virtual void SetFocusShown(bool state, bool andParent=true);
bool FocusShown(void) const;
/* Window activation. This is the same as BView::WindowActivated(),
* but we need to do additional tracking of that state for
* ArpLayoutable objects without an attached BView. Setting this
* flag also sets it for all the children; if you override this,
* be sure to also call the superclass.
*/
virtual void SetLayoutActivated(bool state);
bool LayoutActivated(void) const;
/* Subclasses should override this to return true if they are the
* root of a layout heirarchy. This tells ArpLayoutable that their
* associated BView should never be positioned or resized. For the
* most part, only ArpRootLayout need to be concerned about this.
*/
virtual bool IsLayoutRoot(void) const { return false; }
protected:
/* Parameter retrieval.
*/
status_t
ExtractParam(const ArpMessage* params, const char* name,
type_code type, const ArpMessage* globs,
ArpString* gName, void* data);
/* This is called by the above function to actually pull
* the particular data from a message. It undertands the
* following types:
*
* type_code type void* data
* B_STRING_TYPE ArpString
* B_BOOL_TYPE bool
* B_INT8_TYPE int8
* B_INT16_TYPE int16
* B_INT32_TYPE int32
* B_INT64_TYPE int64
* B_FLOAT_TYPE float
* B_DOUBLE_TYPE double
* B_POINT_TYPE BPoint
* B_RECT_TYPE BRect
* B_REF_TYPE entry_ref
* B_RGB_COLOR_TYPE rgb_color
* B_PATTERN_TYPE pattern
* B_MESSAGE_TYPE BMessage
* B_MESSENGER_TYPE BMessenger
* B_POINTER_TYPE void*
* FFont::FONT_TYPE BFont
*
* The 'data' you supply MUST match 'type', or Bad Things
* will certainly happen.
*
* If you want to use other types, you will need to
* override this function to handle them yourself.
*/
virtual status_t
InterpretType(const ArpMessage& msg, const char* name,
type_code type, void* data);
/* Parameter setting. This function is called whenever
* SetParam() is called on the object. It should look
* for the parameters it understands and update them
* appropriately. Be sure to call InvalidateDimens()
* or InvalidateView() as appropriate.
*
* This function is also called whenever the global values
* changed -- either because SetGlobals() or RefreshGlobals()
* was called. You can differentiate this from a SetParam()
* call because 'p' will be NULL. The 'g' ArpMessage is
* the message that was passed into either of those
* functions; in either case, the entire global data space
* can be retrieved with Globals(). As with parameters,
* be sure to call InvalidateDimens() and InvalidateView()
* as needed.
*/
virtual void DoSetParams(const ArpMessage* g,
const ArpMessage* p);
/* Parameter retrieval. This function is called whenever
* paremeter values are requested with Params(). It
* should place its parameters into the given ArpMessage,
* as follows:
* - If 'name' is NULL, place all parameters into message.
* - If 'name' is non-NULL, place this parameter into
* message if it is one you understand, otherwise call
* the superclass.
* Return 'true' if all requested parameters were successfully
* placed into the BMessage.
*/
virtual bool DoParams(ArpMessage& p, const char* name);
/* Subclass layout. This function is called whenever the
* object size and/or position has changed, and it needs
* reposition its children. By default, it just places
* the first child within its entire frame.
*/
virtual void Layout();
/* Dimensioning. When a recomputation of the object is
* needed (indicated with InvalidateDimens() above),
* ComputeDimens() will be called and should fill
* in cur_dimens with the appropriate information. By
* default, this sets its dimensions to be the same as its
* first child, or zero.
*/
virtual void ComputeDimens();
ArpLayoutDimens cur_dimens;
/* BView management. This function is called whenever the
* layoutable's parent BView changes -- the parameter passed
* in will either be a valid BView to which this object
* should attach itself, or NULL if it should detach itself
* from its current parent. It is GUARANTEED not to be called
* with a valid BView if the object is already attached to
* another BView.
* The default implementation of this function takes care of
* three cases:
* (1) If this object does not itself have an owner BView,
* it calls AttachView() on all of its children.
* (2) Otherwise, if par_view is non-NULL, it adds its owner
* BView to par_view.
* (3) Otherwise, if InView() is currently valid, it removes
* its owner BView that.
* You can override this function to, for example, attach
* any extra BViews created by your subclass, which do not
* have their own ArpLayoutable object (that is, they are not
* a visible part of the layout system). In such a case,
* though, you will almost certainly still want to call the
* inherited form of this function.
*/
virtual void AttachView(BView* par_view, BView* before = 0);
/* Object rendering. This function is called whenever the
* object should draw itself. It is not useful to override
* in layout objects that are incorporated with actual
* BViews, which should be overriding the BView Draw()
* function. Conversely, a subclass of BView should always
* call its ArpLayoutable DrawLayout() function to be sure
* its children are drawn.
* You should also always call the superclass when overriding
* this function to be sure your children are drawn.
*/
virtual void DrawLayout(BView* inside, BRect region);
/* Current object parameters. PG_* parameters are
* the of the value in the global data space; they may
* be NULL if not supplied. PV_* values are the actual
* value and are guaranteed to always be valid.
*/
ArpString PG_BackgroundColor;
ArpString PG_ForegroundColor;
ArpString PG_BasicFont;
rgb_color PV_BackgroundColor;
rgb_color PV_ForegroundColor;
BFont PV_BasicFont;
private:
void LayoutChanged(bool force);
BView* FindClosestView(int32 startIndex);
void do_construct(void);
void do_inhibit_layout(bool state);
status_t lookup_param(const ArpMessage& msg, const char* name,
type_code type, void* data);
ArpString name;
BList children;
ArpLayoutable* parent;
BView* in_view;
bool dimens_changed;
bool inhibit_layout;
bool focus_shown;
bool layout_activated;
const ArpMessage* globals;
/* Current constraints. This is used to hold parent-defined
* state information about the object; it is not for use
* by this implementor.
*/
ArpMessage constraints;
// Current LayoutFrame() and LayoutBounds(), as
// described for those functions above.
BRect cur_frame, cur_bounds;
// The last frame this object/BView was sized to, used to
// avoid unnessasary exchanges with the app_server.
BRect last_frame;
status_t error;
};
#endif