#ifndef WIDGET_H_ #define WIDGET_H_ // TODO: Make this cross platform (MSVC) #include #include #include "vector.h" #include "geom.h" /* * Forward declarations */ typedef struct _classInfo classInfo; typedef struct _widget widget; typedef struct _widgetVtbl widgetVtbl; typedef enum _eventType eventType; typedef enum _mouseButton mouseButton; typedef struct _event event; typedef struct _eventMouse eventMouse; typedef struct _eventMouseBtn eventMouseBtn; typedef struct _eventKey eventKey; typedef struct _eventMisc eventMisc; typedef bool (*callback) (widget *widget, event *evt, void *userData); typedef struct _eventTableEntry eventTableEntry; typedef enum _hAlign hAlign; typedef enum _vAlign vAlign; /* * Information about the `type' (class) of a widget */ struct _classInfo { const struct _classInfo *parentType; const char *ourType; }; /* * The valid event types */ enum _eventType { // Mouse events EVT_MOUSE_DOWN, EVT_MOUSE_UP, EVT_MOUSE_CLICK, EVT_MOUSE_ENTER, EVT_MOUSE_MOVE, EVT_MOUSE_LEAVE, // Keyboard events EVT_KEY_DOWN, EVT_KEY_UP, // Misc EVT_FOCUS, EVT_BLUR }; /* * The possible mouse states as understood by the events system */ enum _mouseButton { BUTTON_LEFT, BUTTON_RIGHT, BUTTON_WHEEL_UP, BUTTON_WHEEL_DOWN, BUTTON_OTHER }; /* * Event structures */ /* * The 'base' event structure. All events can be cast to this */ struct _event { // The time at which the event took place int time; // The type of the event eventType type; }; /* * The event structure used for mouse motion events */ struct _eventMouse { event event; // Location of the event point loc; }; /* * The event structure used for mouse button events */ struct _eventMouseBtn { event event; // Location point loc; // Button pressed mouseButton button; }; /* * The event structure used for keyboard events */ struct _eventKey { event event; // The key which was pressed, this should be used for text-input &c // NB: This is only valid for EVT_KEY_DOWN events! int unicode; // The keycode of the key which was pressed int keycode; // Active modifier keys bool ctrl; bool shift; bool alt; }; /* * */ struct _eventMisc { event event; }; /* * Event table structure */ struct _eventTableEntry { eventType type; callback callback; void *userData; }; /* * The possible horizontal sides a child can be aligned to */ enum _hAlign { LEFT, CENTRE, RIGHT }; /* * The possible vertical sides a child can be aligned to */ enum _vAlign { TOP, MIDDLE, BOTTOM }; /* * The widget classes virtual method table */ struct _widgetVtbl { bool (*handleEvent) (widget *self, event *evt); bool (*addChild) (widget *self, widget *child); void (*removeChild) (widget *self, widget *child); bool (*fireCallbacks) (widget *self, event *evt); int (*addEventHandler) (widget *self, eventType type, callback handler, void *userData); void (*removeEventHandler) (widget *self, int id); void (*focus) (widget *self); void (*blur) (widget *self); void (*enable) (widget *self); void (*disable) (widget *self); point (*getMinSize) (widget *self); point (*getMaxSize) (widget *self); void (*setAlign) (widget *self, vAlign v, hAlign h); void (*drawChildren) (widget *self, cairo_t *cr); void (*doDraw) (widget *self, cairo_t *cr); bool (*doLayout) (widget *self); void (*destroy) (widget *self); }; /* * */ struct _widget { //-------------------------------------- // Private/protected members //-------------------------------------- widgetVtbl *vtbl; /* * The list of registered event handlers */ vector *eventVtbl; /* * The child widgets of ourself */ vector *children; /* * The widgets parent widget */ widget *parent; /* * The minimum size the widget can be */ point minSize; /* * The maximum size the widget can be */ point maxSize; /* * Child alignment */ vAlign vAlignment; hAlign hAlignment; /* * If a mouse button is currently depressed on the widget */ bool hasMouseDown; //-------------------------------------- // Public members //-------------------------------------- /* * The id of the widget */ char *id; /* * The class (or subclass) that widget is (used for type checking) */ const classInfo *classInfo; /* * Arbitary user-defined data */ void *pUserData; int32_t userData; /* * The offset of the widget relative to its parent */ point offset; /* * The size of the widget */ point size; /* * If the widget currently has keyboard focus */ bool hasFocus; /* * If the mouse is currently over the widget */ bool hasMouse; /* * If the widget is currently enabled or not */ bool isEnabled; }; /* * Type information */ const classInfo widgetClassInfo = { NULL, // Root class and therefore no parent "widget" }; /* * Helper macros */ #define WIDGET(self) ((widget *) (self)) #define WIDGET_GET_VTBL(self) ((WIDGET(self))->vtbl) #define WIDGET_CHECK_METHOD(self, method) (assert(WIDGET_GET_VTBL(self)->method)) /* * Protected methods */ void widgetInit(widget *instance, const char *id); void widgetDestroyImpl(widget *instance); bool widgetAddChildImpl(widget *self, widget *child); void widgetRemoveChildImpl(widget *self, widget *child); bool widgetFireCallbacksImpl(widget *self, event *evt); int widgetAddEventHandlerImpl(widget *self, eventType type, callback handler, void *userData); void widgetRemoveEventHandlerImpl(widget *self, int id); void widgetEnableImpl(widget *self); void widgetDisableImpl(widget *self); void widgetFocusImpl(widget *self); void widgetBlurImpl(widget *self); point widgetGetMinSizeImpl(widget *self); point widgetGetMaxSizeImpl(widget *self); void widgetSetAlignImpl(widget *self, vAlign v, hAlign h); void widgetDrawChildrenImpl(widget *self, cairo_t *cr); bool widgetHandleEventImpl(widget *instance, event *evt); /* * Public static methods */ /** * Checks to see if it is legal to cast self to instanceOf. Or, put in OO terms * checks if self `is a' instanceOf instance. * * @param self The widget to check the class of. * @param instanceOf The class we are interested in. * @return True if it is legal to cast, false otherwise. */ bool widgetIsA(widget *self, classInfo *instanceOf); /* * Public methods */ /** * Draws the widget along with its child widgets. * * @param self The widget to be drawn. * @param cr The cairo context the widget should draw itself on. */ void widgetDraw(widget *self, cairo_t *cr); /** * Recursively searches the child widgets of self for a widget whose ->id is * id. If no widget with such an id exists NULL is returned. * * @param self The widget to start the search from. * @param id The id of the desired widget. * @return A pointer to the widget if found, NULL otherwise. */ widget *widgetFindById(widget *self, const char *id); /** * Returns the absolute position of the widget (ie, a position that is not * relative to any other widget. * * @param self The widget to get the position of. * @return The absolute position of self. */ point widgetAbsolutePosition(widget *self); /** * Returns the absolute bounding rectangle of the widget. * * @param self The widget to get the bounds of. * @return The absolute bounds of self. */ rect widgetAbsoluteBounds(widget *self); /** * Transverses up the hierarchy until it finds parent-less widget (known as * the root widget). A pointer to this widget is returned. * * @param self The widget to find the root widget of. * @return A pointer to the root widget. */ widget *widgetGetRoot(widget *self); /** * Attempts to add child as a child widget of self. The exact location of the * widget (as well as its dimensions) are decided based off of the min & max * sizes of the child. * * @param self The widget to add the child widget to. * @param child The widget to be added. * @return true if child was successfully added, false otherwise. */ bool widgetAddChild(widget *self, widget *child); /** * Attempts to remove child from the list of child widgets. If the child widget * is found anywhere in the hierarchy it is removed and its destructor called. * * A convenient way of using this methid is as follows: * widgetRemoveChild(self, widgetFindById(self, "id_to_remove")); * * @param self The widget to remove child from. * @param child The child widget to remove. */ void widgetRemoveChild(widget *self, widget *child); /** * Adds handler to self's event handler table, registering it to respond to * events of type. An unique id is assigned to the event when it is added. This * id can be used at a later date to widgetRemoveEventHandler to remove the * event. * * The userData pointer is passed verbatim to handler via the userData * parameter. If no user data is required then NULL can be passed. * * It is perfectly legal for there to be multiple event handlers installed for * a single event type. When this is the case the event handlers are fired in * the order in which they were added. * * @param self The widget to add the event handler to. * @param type The type of event that handler should respond to. * @param handler The function to call when the event type fires. * @return The id of the newly added event. */ int widgetAddEventHandler(widget *self, eventType type, callback handler, void *userData); /** * Removes the event from the events table at offset id. * * @param self The widget to remove the event handler from. * @param id The id of the event to be removed. */ void widgetRemoveEventHandler(widget *self, int id); /** * Enables the current widget along with all of its child widgets. If the * widget is currently enabled but one or more of its child widgets are not * then they will also be enabled. * * If, however, the parent widget is disabled then this method is effectively * a no-op. * * @param self The widget to enable. */ void widgetEnable(widget *self); /** * Disables the current widget along with all of its child widgets. If the * widget is currently disabled then this method is a no-op. * * @param self The widget to disable. */ void widgetDisable(widget *self); /** * Destroys the widget and frees *all* memory associated with it. * * @param self The widget to destroy. */ void widgetDestroy(widget *self); /** * Returns a pointer to the widget furthest down the hierarchy which currently * has focus. If self does not currently have focus then NULL is returned. * Should none of self's child widgets have focus (but it does) then self is * returned. * * @param self The widget to get the further down focused child of. * @return A pointer to the widget, or NULL if self is not focused. */ widget *widgetGetCurrentlyFocused(widget *self); /** * If the widget is capable of holding keyboard focus and does not currently * hold it then this method will bring it into focus. Should ->parent not be * focused then widgetFocus(self->parent) will be called to focus it. * * This method will also blur any widgets which will no longer be in focus as a * result of self being focued. In addition it takes responsibility for firing * the EVT_FOCUS callbacks for self. * * @param self The widget to focus. */ void widgetFocus(widget *self); /** * Blurs the current widget (removes keyboard focus from it). Before self is * blurred any child widget with focus is blurred first. Finally the EVT_BLUR * event handlers for self are fired. * * @param self The widget to blur. */ void widgetBlur(widget *self); /** * Returns the minimum size that the widget can be. * * @param self The widget to return the miniumum size of. * @return The miniumum (x,y) size of the widget. */ point widgetGetMinSize(widget *self); /** * Returns the maxiumum size that the widget can be. A value of -1 for either * the x or y co-ordinate means that there is no maximum size. * * @param self The widget to return the maximum size of. * @return The maximum (x,y) size of the widget. */ point widgetGetMaxSize(widget *self); /** * Sets the alignment of child widgets of self. This is used when the maximum * size of the child widgets is less than that of the size of self. * * @param self The widget to set the alignment of. * @param v The vertical alignment of the widget (TOP, MIDDLE, BOTTOM). * @param h The horizontal alignment of the widget (LEFT, CENTRE, RIGHT). */ void widgetSetAlign(widget *self, vAlign v, hAlign h); /** * @TODO */ bool widgetHandleEvent(widget *self, event *evt); /* * Protected methods */ /** * This method is called by widgetDraw to draw the child widgets of self. The * default implementation just draws each of the child widgets in-turn, which * should be the desired behaviour for most containers. * * More complex containers can override this method to provide alternative * behaviour. * * @param self The widget that should have its children drawn. * @param cr The context to draw the children to. */ void widgetDrawChildren(widget *self, cairo_t *cr); /** * A protected `pure virtual' method which is called to draw the widget. The * cairo translation matrix is set-up such that (0,0) is the top-left of the * widget. * * @param self The widget that should draw itself. * @param cr The context to draw the widget to. */ void widgetDoDraw(widget *self, cairo_t *cr); /** * */ bool widgetDoLayout(widget *self); /** * Fires all of the event handlers registered for evt->type on the widger self. * * @param self The widget to fire the callbacks on. * @param evt The event to fire the callbacks for. */ bool widgetFireCallbacks(widget *self, event *evt); #endif /*WIDGET_H_*/