/* * 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