Commit an early non-functional prototype of the new widget code. Feel free to modify as you see fit.

git-svn-id: svn+ssh://svn.gna.org/svn/warzone/trunk@4761 4a71c877-e1ca-e34f-864e-861f7616d084
master
Freddie Witherden 2008-04-24 18:07:47 +00:00
parent 2ffc569a35
commit f8eb466d16
6 changed files with 1017 additions and 0 deletions

9
lib/betawidget/geom.c Normal file
View File

@ -0,0 +1,9 @@
#include "geom.h"
bool pointInRect(point p, rect r)
{
return (r.topLeft.x < p.x
&& r.bottomRight.x > p.x
&& r.topLeft.y < p.y
&& r.bottomRight.y > p.y);
}

43
lib/betawidget/geom.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef GEOM_H_
#define GEOM_H_
// TODO: Make this cross platform (MSVC)
#include <stdbool.h>
/*
* A point
*/
typedef struct _point point;
struct _point
{
int x;
int y;
};
/*
* A rectangle
*/
typedef struct _rect rect;
struct _rect
{
point topLeft;
point bottomRight;
};
/*
* Method signatures
*/
/**
* Checks to see if the point p is inside of the rectangle r.
*
* @param point p
* @param rect r
* @return bool
*/
bool pointInRect(point p, rect r);
#endif /*GEOM_H_*/

104
lib/betawidget/vector.c Normal file
View File

@ -0,0 +1,104 @@
#include <stdlib.h>
#include <string.h>
#include "vector.h"
const int defaultSize = 4;
vector *vectorCreate(destroyCallback cb)
{
vector *v = malloc(sizeof(vector));
if (v == NULL)
{
return NULL;
}
v->mem = calloc(defaultSize, sizeof(void *));
if (v->mem == NULL)
{
free(v);
return NULL;
}
v->size = defaultSize;
v->head = 0;
v->destroy = cb;
return v;
}
void vectorDestroy(vector *v)
{
int i;
for (i = 0; i < vectorSize(v); i++)
{
v->destroy(v->mem[i]);
}
free(v->mem);
free(v);
}
void *vectorAdd(vector *v, void *object)
{
if (v->head + 1 > v->size)
{
void **newMem = realloc(v->mem, 2 * v->size * sizeof(void *));
if (newMem == NULL)
{
return NULL;
}
v->size *= 2;
}
v->mem[v->head++] = object;
return object;
}
void *vectorAt(vector *v, int index)
{
return (index <= vectorSize(v)) ? v->mem[index] : NULL;
}
void *vectorSetAt(vector *v, int index, void *object)
{
if (index >= v->head)
{
return NULL;
}
// Free the current element at index
v->destroy(v->mem[index]);
// Replace the item
v->mem[index] = object;
return object;
}
void vectorRemoveAt(vector *v, int index)
{
if (index >= v->head)
{
return;
}
// Free the element using the provided callback
v->destroy(v->mem[index]);
memmove(&v->mem[index], &v->mem[index + 1],
(v->head - index) * sizeof(void *));
v->head--;
}
int vectorSize(vector *v)
{
return v->head;
}

29
lib/betawidget/vector.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef VECTOR_H_
#define VECTOR_H_
typedef struct _vector vector;
typedef void (*destroyCallback) (void *object);
struct _vector
{
void **mem;
int size;
int head;
destroyCallback destroy;
};
vector *vectorCreate(destroyCallback destroyCb);
void vectorDestroy(vector *v);
void *vectorAdd(vector *v, void *object);
void *vectorAt(vector *v, int index);
void *vectorSetAt(vector *v, int index, void *object);
void vectorRemoveAt(vector *v, int index);
int vectorSize(vector *v);
#endif /*VECTOR_H_*/

484
lib/betawidget/widget.c Normal file
View File

@ -0,0 +1,484 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "widget.h"
static widgetVtbl vtbl;
/*
* Forward declarations
*/
static void widgetDrawChildren(widget *self, cairo_t *cr);
/**
* Prepares the widget classes vtable.
*/
static void widgetInitVtbl(widget *self)
{
static bool initialised = false;
if (!initialised)
{
vtbl.addChild = widgetAddChildImpl;
vtbl.removeChild = widgetRemoveChildImpl;
vtbl.fireCallbacks = widgetFireCallbacksImpl;
vtbl.addEventHandler = widgetAddEventHandlerImpl;
vtbl.removeEventHandler = widgetRemoveEventHandlerImpl;
vtbl.handleEvent = widgetHandleEventImpl;
vtbl.focus = widgetFocusImpl;
vtbl.blur = widgetBlurImpl;
vtbl.enable = widgetEnableImpl;
vtbl.disable = widgetDisableImpl;
vtbl.doDraw = NULL;
vtbl.destroy = widgetDestroyImpl;
initialised = true;
}
// Set the classes vtable
self->vtbl = &vtbl;
// Do any overloading of inherited methods here
}
/*
* Widget class constructor
*/
void widgetInit(widget *self, const char *id)
{
// Prepare our vtable
widgetInitVtbl(self);
// Prepare our container
self->children = vectorCreate((destroyCallback) widgetDestroy);
// Prepare our events table
self->eventVtbl = vectorCreate(free);
// Copy the ID of the widget
self->id = strdup(id);
// Default parent is none
self->parent = NULL;
}
/*
* Widget class destructor (virtual).
*/
void widgetDestroyImpl(widget *self)
{
// Release the container
vectorDestroy(self->children);
// Release the event handler table
vectorDestroy(self->eventVtbl);
// Free the ID
free(self->id);
// Free ourself
free(self);
}
/*
* Draws and widget and its child widgets
*/
void widgetDraw(widget *self, cairo_t *cr)
{
// Draw ourself
widgetDoDraw(self, cr);
// Draw our children
widgetDrawChildren(self, cr);
}
/*
* Draws the child widgets of self.
*/
static void widgetDrawChildren(widget *self, cairo_t *cr)
{
int i;
// Draw our children
for (i = 0; i < vectorSize(self->children); i++)
{
widget *child = WIDGET(vectorAt(self->children, i));
cairo_matrix_t current;
// Translate such that (0,0) is the location of the widget
cairo_get_matrix(cr, &current);
cairo_translate(cr, child->bounds.topLeft.x, child->bounds.topLeft.y);
widgetDraw(child, cr);
// Restore the matrix
cairo_set_matrix(cr, &current);
}
}
widget *widgetGetRoot(widget *self)
{
// If we are the root widget, return early
if (self->parent == NULL)
{
return self;
}
// Otherwise search the hierarchy
else
{
widget *current;
for (current = self->parent; current->parent; current = current->parent);
return current;
}
}
/*
*
*/
widget *widgetFindById(widget *self, const char *id)
{
// See if we have that ID
if (strcmp(self->id, id) == 0)
{
return self;
}
// Try our children
else
{
int i;
for (i = 0; i < vectorSize(self->children); i++)
{
// Get the child widget
widget *child = vectorAt(self->children, i);
// Call its findById method
widget *match = widgetFindById(child, id);
// If it matched, return
if (match)
{
return match;
}
}
}
// If we found nothing return NULL
return NULL;
}
/*
*
*/
bool widgetAddChildImpl(widget *self, widget *child)
{
// FIXME: We need to do some arbitration
// Add the widget
vectorAdd(self->children, child);
// Set ourself as its parent
child->parent = self;
return true;
}
/*
*
*/
void widgetRemoveChildImpl(widget *self, widget *child)
{
int i;
for (i = 0; i < vectorSize(self->children); i++)
{
// If the child is the to-be-removed widget, remove it
if (vectorAt(self->children, i) == child)
{
// vectorRemoveAt will take care of calling the widgets destructor
vectorRemoveAt(self->children, i);
}
// See if it is one of its children
else
{
widgetRemoveChild(vectorAt(self->children, i), child);
}
}
}
/*
*
*/
int widgetAddEventHandlerImpl(widget *self, eventType type, callback handler)
{
eventTableEntry *entry = malloc(sizeof(eventTableEntry));
entry->type = type;
entry->callback = handler;
// Add the handler to the table
vectorAdd(self->eventVtbl, entry);
// Offset = size - 1
return vectorSize(self->eventVtbl) - 1;
}
/*
*
*/
void widgetRemoveEventHandlerImpl(widget *self, int id)
{
vectorRemoveAt(self->eventVtbl, id);
}
bool widgetFireCallbacksImpl(widget *self, event *evt)
{
int i;
for (i = 0; i < vectorSize(self->eventVtbl); i++)
{
eventTableEntry *handler = vectorAt(self->eventVtbl, i);
if (handler->type == evt->type)
{
handler->callback(self, evt);
}
}
// FIXIME
return true;
}
/*
*
*/
void widgetEnableImpl(widget *self)
{
int i;
// First make sure our parent is enabled
if (self->parent && !self->parent->isEnabled)
{
return;
}
// Enable ourself
self->isEnabled = true;
// Enable all of our children
for (i = 0; i < vectorSize(self->children); i++)
{
widgetEnable(WIDGET(vectorAt(self->children, i)));
}
}
void widgetDisableImpl(widget *self)
{
int i;
// If we are currently disabled, return
if (!self->isEnabled)
{
return;
}
// Disable ourself
self->isEnabled = false;
// Disable our children
for (i = 0; i < vectorSize(self->children); i++)
{
widgetDisable(WIDGET(vectorAt(self->children, i)));
}
}
void widgetFocusImpl(widget *self)
{
// Check that we are not currently focused
if (self->hasFocus)
{
int i;
// Blur any of our currently focused child widgets
for (i = 0; i < vectorSize(self->children); i++)
{
widget *child = vectorAt(self->children, i);
if (child->hasFocus)
{
widgetBlur(child);
}
}
return;
}
// If we have a parent, focus it
if (self->parent)
{
widgetFocus(self->parent);
}
// Focus ourself
self->hasFocus = true;
// Fire our on-focus callbacks
event evt;
evt.type = EVT_FOCUS;
widgetFireCallbacks(self, &evt);
}
void widgetBlurImpl(widget *self)
{
widget *current;
// First blur any focused child widgets
while ((current = widgetGetCurrentlyFocused(self)) != self)
{
widgetBlur(current);
}
// Blur ourself
self->hasFocus = false;
// Fire off the on-blur callbacks
event evt;
evt.type = EVT_BLUR;
widgetFireCallbacks(self, &evt);
}
widget *widgetGetCurrentlyFocused(widget *self)
{
int i;
if (!self->hasFocus)
{
return NULL;
}
for (i = 0; i < vectorSize(self->children); i++)
{
widget *child = vectorAt(self->children, i);
if (child->hasFocus)
{
return widgetGetCurrentlyFocused(child);
}
}
// None of our children are focused, return ourself
return self;
}
bool widgetHandleEventImpl(widget *self, event *evt)
{
switch (evt->type)
{
case EVT_MOUSE_MOVE:
{
eventMouse evtMouse = *((eventMouse *) evt);
bool newHasMouse = pointInRect(evtMouse.loc, self->bounds);
// If we have just `got' the mouse
if (newHasMouse && !self->hasMouse)
{
// Generate a EVT_MOUSE_ENTER event
evtMouse.event.type = EVT_MOUSE_ENTER;
// Fire the event handler
widgetFireCallbacks(self, (event *) &evtMouse);
}
// If we have just lost the mouse
else if (!newHasMouse && self->hasMouse)
{
// Generate a EVT_MOUSE_LEAVE event
evtMouse.event.type = EVT_MOUSE_LEAVE;
// Fire the handler
widgetFireCallbacks(self, (event *) &evtMouse);
}
// We had and still have the mouse
else if (newHasMouse && self->hasMouse)
{
// Pass the event as-is
widgetFireCallbacks(self, (event *) &evtMouse);
}
// Update the status of the mouse
self->hasMouse = newHasMouse;
break;
}
default:
break;
}
return true;
}
bool widgetAddChild(widget *self, widget *child)
{
return WIDGET_GET_VTBL(self)->addChild(self, child);
}
void widgetRemoveChild(widget *self, widget *child)
{
WIDGET_GET_VTBL(self)->removeChild(self, child);
}
int widgetAddEventHandler(widget *self, eventType type, callback handler)
{
return WIDGET_GET_VTBL(self)->addEventHandler(self, type, handler);
}
void widgetRemoveEventHandler(widget *self, int id)
{
WIDGET_GET_VTBL(self)->removeEventHandler(self, id);
}
bool widgetFireCallbacks(widget *self, event *evt)
{
return WIDGET_GET_VTBL(self)->fireCallbacks(self, evt);
}
void widgetEnable(widget *self)
{
WIDGET_GET_VTBL(self)->enable(self);
}
void widgetDisable(widget *self)
{
WIDGET_GET_VTBL(self)->disable(self);
}
void widgetFocus(widget *self)
{
WIDGET_GET_VTBL(self)->focus(self);
}
void widgetBlur(widget *self)
{
WIDGET_GET_VTBL(self)->blur(self);
}
void widgetDoDraw(widget *self, cairo_t *cr)
{
WIDGET_GET_VTBL(self)->doDraw(self, cr);
}
bool widgetHandleEvent(widget *self, event *evt)
{
return WIDGET_GET_VTBL(self)->handleEvent(self, evt);
}
void widgetDestroy(widget *self)
{
return WIDGET_GET_VTBL(self)->destroy(self);
}

348
lib/betawidget/widget.h Normal file
View File

@ -0,0 +1,348 @@
#ifndef WIDGET_H_
#define WIDGET_H_
#include <cairo.h>
#include "vector.h"
#include "geom.h"
/*
* Forward declarations
*/
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);
typedef struct _eventTableEntry eventTableEntry;
/*
* 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_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
int key;
// Active modifier keys
bool ctrl;
bool shift;
bool alt;
};
/*
*
*/
struct _eventMisc
{
event event;
};
/*
* Event table structure
*/
struct _eventTableEntry
{
eventType type;
callback callback;
};
/*
* 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 (*removeEventHandler) (widget *self, int id);
void (*focus) (widget *self);
void (*blur) (widget *self);
void (*enable) (widget *self);
void (*disable) (widget *self);
void (*doDraw) (widget *self, cairo_t *cr);
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;
//--------------------------------------
// Public members
//--------------------------------------
/*
* The id of the widget
*/
char *id;
/*
* Arbitary user-defined data
*/
void *pUserData;
int userData;
/*
* The origin and size of the widget
*/
rect bounds;
/*
* 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;
};
/*
* Helper macros
*/
#define WIDGET(self) ((widget *) (self))
#define WIDGET_GET_VTBL(self) ((WIDGET(self))->vtbl)
/*
* 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 widgetRemoveEventHandlerImpl(widget *self, int id);
void widgetEnableImpl(widget *self);
void widgetDisableImpl(widget *self);
void widgetFocusImpl(widget *self);
void widgetBlurImpl(widget *self);
bool widgetHandleEventImpl(widget *instance, event *evt);
/*
* Public methods
*/
/**
* Draws the widget along with its child widgets.
*/
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.
*/
widget *widgetFindById(widget *self, const char *id);
/**
* Transverses up the hierarchy until it finds parent-less widget (known as
* the root widget). A pointer to this widget is returned.
*/
widget *windgetGetRoot(widget *self);
/**
*
*/
bool widgetAddChild(widget *self, widget *child);
/**
*
*/
void widgetRemoveChild(widget *self, widget *child);
/**
*
*/
int widgetAddEventHandler(widget *self, eventType type, callback handler);
/**
*
*/
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.
*/
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.
*/
void widgetDisable(widget *self);
/**
* Destroys the widget and frees *all* memory associated with it.
*/
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.
*/
widget *widgetGetCurrentlyFocused(widget *self);
/**
* If the widget is capable of holding keyboard focus and does not currently
* hold it then this method should bring the widget into focus.
*
* This method will call the blur() method of the root widget first.
*/
void widgetFocus(widget *self);
/**
* Blurs the current widget (removes keyboard focus from it) and fires the
* EVT_BLUR event handlers for affected widgets.
*/
void widgetBlur(widget *self);
/**
*
*/
bool widgetHandleEvent(widget *self, event *evt);
/**
*
*/
bool widgetFireCallbacks(widget *self, event *evt);
/*
* Protected methods
*/
/**
*
*/
void widgetDoDraw(widget *self, cairo_t *cr);
#endif /*WIDGET_H_*/