2008-04-24 11:07:47 -07:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "widget.h"
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
const classInfo widgetClassInfo =
|
|
|
|
{
|
|
|
|
NULL, // Root class and therefore no parent
|
|
|
|
"widget"
|
|
|
|
};
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
static widgetVtbl vtbl;
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
/**
|
|
|
|
* Creates a new cairo context and places it in *cr. The context has a format of
|
|
|
|
* format and dimensions of w * h. Should *cr not be NULL it is assumed to be an
|
|
|
|
* existing cairo context and so is destroyed.
|
|
|
|
*
|
|
|
|
* @param cr The pointer to the region of memory to create the context in.
|
|
|
|
* @param format The format of the context; such as CAIRO_FORMAT_ARGB32.
|
|
|
|
* @param w The width of the context in pixels.
|
|
|
|
* @param h The height of the context in pixels.
|
|
|
|
* @return true if the context was created successfully; false otherwise.
|
|
|
|
*/
|
|
|
|
static bool widgetCairoCreate(cairo_t **cr, cairo_format_t format, int w, int h)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
|
|
|
// See if a context already exists
|
|
|
|
if (*cr)
|
|
|
|
{
|
|
|
|
// Destroy it
|
|
|
|
cairo_destroy(*cr);
|
|
|
|
|
|
|
|
// NULL the context
|
|
|
|
*cr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the surface
|
|
|
|
surface = cairo_image_surface_create(format, w, h);
|
|
|
|
|
|
|
|
// Make sure it was created
|
|
|
|
if (!surface)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creatr a context to draw on the surface
|
|
|
|
*cr = cairo_create(surface);
|
|
|
|
|
|
|
|
// Destroy the surface (*cr still maintains a reference to it)
|
|
|
|
cairo_surface_destroy(surface);
|
|
|
|
|
|
|
|
// Make sure the context was created
|
|
|
|
if (!*cr)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-07 14:19:58 -07:00
|
|
|
/**
|
|
|
|
* Checks to see if the widget, self, has the mouse over it. This consists of
|
|
|
|
* two checks; firstly seeing if the mouse is inside of the widgets bounding
|
|
|
|
* rectangle; secondly, if the widget has a mask, seeing if the location is
|
|
|
|
* masked or not.
|
|
|
|
*
|
|
|
|
* @param self The widget we want to see if the mouse is over.
|
|
|
|
* @param location The location of the mouse, in absolute terms.
|
|
|
|
* @return True if the mouse is over the widget, false otherwise.
|
|
|
|
*/
|
|
|
|
static bool widgetHasMouse(widget *self, point location)
|
|
|
|
{
|
|
|
|
const rect bounds = widgetAbsoluteBounds(self);
|
|
|
|
|
|
|
|
// Check to see if it is in the widgets bounding box
|
|
|
|
bool hasMouse = pointInRect(location, bounds);
|
|
|
|
|
|
|
|
// If the widget has a mask; ensure the location is not masked
|
|
|
|
if (hasMouse && self->maskEnabled)
|
|
|
|
{
|
|
|
|
// Work out where the mouse is relative to the widget
|
|
|
|
const point relativeLocation = pointSub(location, bounds.topLeft);
|
|
|
|
|
|
|
|
// We have the mouse if the point is NOT masked
|
|
|
|
hasMouse = !widgetPointMasked(self, relativeLocation);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasMouse;
|
|
|
|
}
|
|
|
|
|
2008-08-08 11:15:50 -07:00
|
|
|
/**
|
|
|
|
* Returns a pointer to the event handler with an id of id. Should id be invalid
|
|
|
|
* then NULL is returned.
|
|
|
|
*
|
|
|
|
* @param self The widget whose event handler table to search.
|
|
|
|
* @param id The id of the desired event handler.
|
|
|
|
* @return A pointer to the event handler, or NULL if id is invalid.
|
|
|
|
*/
|
|
|
|
static eventTableEntry *widgetGetEventHandlerById(widget *self, int id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
eventTableEntry *entry = NULL;
|
|
|
|
|
|
|
|
// Search the event handler table
|
|
|
|
for (i = 0; i < vectorSize(self->eventVtbl); i++)
|
|
|
|
{
|
|
|
|
eventTableEntry *currEntry = vectorAt(self->eventVtbl, id);
|
|
|
|
|
|
|
|
// See if the id matches
|
|
|
|
if (currEntry->id == id)
|
|
|
|
{
|
|
|
|
entry = currEntry;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
bool widgetIsA(widget *self, const classInfo *instanceOf)
|
2008-06-23 05:18:19 -07:00
|
|
|
{
|
2008-06-24 03:11:03 -07:00
|
|
|
const classInfo *widgetClass;
|
2008-06-23 05:18:19 -07:00
|
|
|
|
|
|
|
// Transverse up the hierarchy
|
|
|
|
for (widgetClass = self->classInfo;
|
|
|
|
widgetClass->parentType;
|
|
|
|
widgetClass = widgetClass->parentType)
|
|
|
|
{
|
|
|
|
// self `is a' instanceOf
|
|
|
|
if (widgetClass == instanceOf)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
static void widgetInitVtbl(widget *self)
|
|
|
|
{
|
|
|
|
static bool initialised = false;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
if (!initialised)
|
|
|
|
{
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.addChild = widgetAddChildImpl;
|
|
|
|
vtbl.removeChild = widgetRemoveChildImpl;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.fireCallbacks = widgetFireCallbacksImpl;
|
2008-07-15 03:12:03 -07:00
|
|
|
vtbl.fireTimerCallbacks = widgetFireTimerCallbacksImpl;
|
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.addEventHandler = widgetAddEventHandlerImpl;
|
2008-07-15 03:12:03 -07:00
|
|
|
vtbl.addTimerEventHandler = widgetAddTimerEventHandlerImpl;
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.removeEventHandler = widgetRemoveEventHandlerImpl;
|
|
|
|
vtbl.handleEvent = widgetHandleEventImpl;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.focus = widgetFocusImpl;
|
|
|
|
vtbl.blur = widgetBlurImpl;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.enable = widgetEnableImpl;
|
|
|
|
vtbl.disable = widgetDisableImpl;
|
2008-08-01 14:24:19 -07:00
|
|
|
|
|
|
|
vtbl.show = widgetShowImpl;
|
|
|
|
vtbl.hide = widgetHideImpl;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.getMinSize = NULL;
|
|
|
|
vtbl.getMaxSize = NULL;
|
2008-07-02 16:18:32 -07:00
|
|
|
|
2008-08-01 10:51:07 -07:00
|
|
|
vtbl.resize = widgetResizeImpl;
|
2008-07-02 16:18:32 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.composite = widgetCompositeImpl;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.doLayout = NULL;
|
|
|
|
vtbl.doDraw = NULL;
|
|
|
|
vtbl.doDrawMask = NULL;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
vtbl.destroy = widgetDestroyImpl;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
initialised = true;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Set the classes vtable
|
|
|
|
self->vtbl = &vtbl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetInit(widget *self, const char *id)
|
2008-06-22 16:28:34 -07:00
|
|
|
{
|
2008-04-24 11:07:47 -07:00
|
|
|
// Prepare our vtable
|
|
|
|
widgetInitVtbl(self);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-23 05:18:19 -07:00
|
|
|
// Set our type
|
|
|
|
self->classInfo = &widgetClassInfo;
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Prepare our container
|
2008-07-02 16:18:32 -07:00
|
|
|
self->children = vectorCreate();
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Prepare our events table
|
2008-07-02 16:18:32 -07:00
|
|
|
self->eventVtbl = vectorCreate();
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Copy the ID of the widget
|
|
|
|
self->id = strdup(id);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Default parent is none
|
|
|
|
self->parent = NULL;
|
2008-07-02 16:18:32 -07:00
|
|
|
|
2008-07-14 01:51:55 -07:00
|
|
|
// Zero the user data
|
|
|
|
self->userData = 0;
|
|
|
|
self->pUserData = NULL;
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Create a dummy cairo context (getMin/MaxSize may depend on it)
|
2008-07-07 12:10:23 -07:00
|
|
|
self->cr = NULL;
|
|
|
|
widgetCairoCreate(&self->cr, CAIRO_FORMAT_ARGB32, 0, 0);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-18 15:21:54 -07:00
|
|
|
// Ask OpenGL for a texture id
|
|
|
|
glGenTextures(1, &self->textureId);
|
|
|
|
|
2008-06-22 10:53:08 -07:00
|
|
|
// Focus and mouse are false by default
|
2008-06-21 10:23:00 -07:00
|
|
|
self->hasFocus = false;
|
|
|
|
self->hasMouse = false;
|
2008-06-22 10:53:08 -07:00
|
|
|
self->hasMouseDown = false;
|
2008-07-02 16:18:32 -07:00
|
|
|
|
|
|
|
// By default we need drawing
|
|
|
|
self->needsRedraw = true;
|
2008-07-07 12:10:23 -07:00
|
|
|
|
2008-08-02 07:47:12 -07:00
|
|
|
// Enabled by default
|
|
|
|
self->isEnabled = true;
|
|
|
|
|
2008-08-01 14:24:19 -07:00
|
|
|
// Also by default we need to be shown (hence are invisible)
|
|
|
|
self->isVisible = false;
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
// By default the mouse-event mask is disabled
|
|
|
|
self->maskCr = NULL;
|
|
|
|
self->maskEnabled = false;
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void widgetDestroyImpl(widget *self)
|
|
|
|
{
|
2008-08-02 15:56:14 -07:00
|
|
|
eventTableEntry *currEntry;
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Release the container
|
2008-07-02 16:18:32 -07:00
|
|
|
vectorMapAndDestroy(self->children, (mapCallback) widgetDestroy);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Release the event handler table
|
2008-08-02 15:56:14 -07:00
|
|
|
while ((currEntry = vectorHead(self->eventVtbl)))
|
|
|
|
{
|
|
|
|
widgetRemoveEventHandler(self, currEntry->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
vectorDestroy(self->eventVtbl);
|
2008-07-02 16:18:32 -07:00
|
|
|
|
|
|
|
// Destroy the cairo context
|
|
|
|
cairo_destroy(self->cr);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-18 15:21:54 -07:00
|
|
|
// Destroy the texture
|
|
|
|
glDeleteTextures(1, &self->textureId);
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
// If we use a mask, destroy it
|
|
|
|
if (self->maskEnabled)
|
|
|
|
{
|
|
|
|
cairo_destroy(self->maskCr);
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Free the ID
|
|
|
|
free(self->id);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Free ourself
|
|
|
|
free(self);
|
|
|
|
}
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
void widgetDraw(widget *self)
|
2008-04-24 11:07:47 -07:00
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
// See if we need to be redrawn
|
|
|
|
if (self->needsRedraw)
|
|
|
|
{
|
2008-07-18 15:21:54 -07:00
|
|
|
void *bits = cairo_image_surface_get_data(cairo_get_target(self->cr));
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
self->needsRedraw = false;
|
|
|
|
|
2008-07-08 03:15:52 -07:00
|
|
|
// Clear the current context
|
|
|
|
cairo_set_operator(self->cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
cairo_set_source_rgba(self->cr, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
cairo_paint(self->cr);
|
|
|
|
|
|
|
|
// Restore the compositing operator back to the default
|
|
|
|
cairo_set_operator(self->cr, CAIRO_OPERATOR_OVER);
|
|
|
|
|
2008-07-19 12:34:51 -07:00
|
|
|
// Save (push) the current context
|
|
|
|
cairo_save(self->cr);
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Redaw ourself
|
|
|
|
widgetDoDraw(self);
|
2008-07-18 15:21:54 -07:00
|
|
|
|
2008-07-19 12:34:51 -07:00
|
|
|
// Restore the context
|
|
|
|
cairo_restore(self->cr);
|
|
|
|
|
2008-07-18 15:21:54 -07:00
|
|
|
// Update the texture
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self->textureId);
|
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, self->size.x,
|
|
|
|
self->size.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, bits);
|
2008-07-02 16:18:32 -07:00
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Draw our children (even if we did not need redrawing our children might)
|
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
|
|
|
widget *child = vectorAt(self->children, i);
|
|
|
|
|
|
|
|
// Ask the child to re-draw itself
|
|
|
|
widgetDraw(child);
|
|
|
|
}
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
void widgetDrawMask(widget *self)
|
|
|
|
{
|
|
|
|
// Make sure masking is enabled and that the context is valid
|
|
|
|
assert(self->maskEnabled);
|
|
|
|
assert(self->maskCr);
|
|
|
|
|
|
|
|
// Make the context opaque (so it is all masked)
|
|
|
|
cairo_set_source_rgba(self->maskCr, 0.0, 0.0, 0.0, 1.0);
|
|
|
|
cairo_paint(self->maskCr);
|
|
|
|
|
|
|
|
// Change the blending mode so that the source overwrites the destination
|
|
|
|
cairo_set_operator(self->maskCr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
|
|
|
// Change the source to transparency
|
|
|
|
cairo_set_source_rgba(self->maskCr, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
|
|
|
// Draw the mask
|
|
|
|
widgetDoDrawMask(self);
|
|
|
|
}
|
|
|
|
|
2008-04-27 08:14:42 -07:00
|
|
|
point widgetAbsolutePosition(widget *self)
|
|
|
|
{
|
|
|
|
// Get our own offset
|
|
|
|
point pos = self->offset;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-27 08:14:42 -07:00
|
|
|
// Add to this our parents offset
|
|
|
|
if (self->parent != NULL)
|
|
|
|
{
|
|
|
|
pos = pointAdd(pos, widgetAbsolutePosition(self->parent));
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-27 08:14:42 -07:00
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2008-06-21 10:23:00 -07:00
|
|
|
rect widgetAbsoluteBounds(widget *self)
|
|
|
|
{
|
|
|
|
// Get our position
|
2008-07-02 16:18:32 -07:00
|
|
|
const point p = widgetAbsolutePosition(self);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-21 10:23:00 -07:00
|
|
|
// Construct and return a rect from this
|
|
|
|
return rectFromPointAndSize(p, self->size);
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
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;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
for (current = self->parent; current->parent; current = current->parent);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
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;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
|
|
|
// Get the child widget
|
|
|
|
widget *child = vectorAt(self->children, i);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Call its findById method
|
|
|
|
widget *match = widgetFindById(child, id);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// If it matched, return
|
|
|
|
if (match)
|
|
|
|
{
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// If we found nothing return NULL
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-08-08 11:15:50 -07:00
|
|
|
void *widgetGetEventHandlerUserData(widget *self, int id)
|
|
|
|
{
|
|
|
|
eventTableEntry *entry = widgetGetEventHandlerById(self, id);
|
|
|
|
|
|
|
|
// Make sure we found something
|
|
|
|
assert(entry != NULL);
|
|
|
|
|
|
|
|
// Return the user-data
|
|
|
|
return entry->userData;
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetSetEventHandlerUserData(widget *self, int id, void *userData)
|
|
|
|
{
|
|
|
|
eventTableEntry *entry = widgetGetEventHandlerById(self, id);
|
|
|
|
|
|
|
|
// Make sure we found something
|
|
|
|
assert(entry != NULL);
|
|
|
|
|
|
|
|
// Set the user-data
|
|
|
|
entry->userData = userData;
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
bool widgetAddChildImpl(widget *self, widget *child)
|
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
// Make sure the id of the child is unquie
|
2008-06-21 10:23:00 -07:00
|
|
|
if (widgetFindById(widgetGetRoot(self), child->id) != NULL)
|
|
|
|
{
|
|
|
|
// TODO: An error/debug message is probably required
|
|
|
|
return false;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Add the widget
|
|
|
|
vectorAdd(self->children, child);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
// Re-layout ourself
|
|
|
|
if (widgetDoLayout(self))
|
|
|
|
{
|
|
|
|
// Set ourself as its parent
|
|
|
|
child->parent = self;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Not enough space to fit the widget
|
|
|
|
else
|
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
// Remove child *without* calling its destructor
|
|
|
|
vectorRemoveAt(self->children, vectorSize(self->children) - 1);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
// Restore the layout
|
|
|
|
widgetDoLayout(self);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
return false;
|
|
|
|
}
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void widgetRemoveChildImpl(widget *self, widget *child)
|
2008-06-22 16:28:34 -07:00
|
|
|
{
|
2008-04-24 11:07:47 -07:00
|
|
|
int i;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
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)
|
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
// Call the destructor for the widget
|
|
|
|
widgetDestroy(vectorAt(self->children, i));
|
|
|
|
|
|
|
|
// Remove it from the list of children
|
2008-04-24 11:07:47 -07:00
|
|
|
vectorRemoveAt(self->children, i);
|
|
|
|
}
|
|
|
|
// See if it is one of its children
|
|
|
|
else
|
|
|
|
{
|
|
|
|
widgetRemoveChild(vectorAt(self->children, i), child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-14 16:19:51 -07:00
|
|
|
int widgetAddEventHandlerImpl(widget *self, eventType type, callback handler,
|
|
|
|
void *userData)
|
2008-04-24 11:07:47 -07:00
|
|
|
{
|
|
|
|
eventTableEntry *entry = malloc(sizeof(eventTableEntry));
|
2008-07-15 03:12:03 -07:00
|
|
|
eventTableEntry *lastEntry = vectorHead(self->eventVtbl);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-15 03:12:03 -07:00
|
|
|
// Timer events should use addTimerEventHandler
|
|
|
|
assert(type != EVT_TIMER_SINGLE_SHOT && type != EVT_TIMER_PERSISTENT);
|
|
|
|
|
2008-07-14 16:41:53 -07:00
|
|
|
// Assign the handler an id which is one higher than the current highest
|
2008-07-15 03:12:03 -07:00
|
|
|
entry->id = (lastEntry) ? lastEntry->id + 1 : 1;
|
2008-06-22 16:28:34 -07:00
|
|
|
entry->type = type;
|
2008-04-24 11:07:47 -07:00
|
|
|
entry->callback = handler;
|
2008-06-22 16:28:34 -07:00
|
|
|
entry->userData = userData;
|
2008-07-15 03:12:03 -07:00
|
|
|
|
|
|
|
entry->lastCalled = 0; // We have never been called
|
|
|
|
entry->interval = -1; // We are not a timer event
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Add the handler to the table
|
|
|
|
vectorAdd(self->eventVtbl, entry);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-15 03:12:03 -07:00
|
|
|
// Return the id of the handler
|
|
|
|
return entry->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
int widgetAddTimerEventHandlerImpl(widget *self, eventType type, int interval,
|
|
|
|
callback handler, void *userData)
|
|
|
|
{
|
|
|
|
eventTableEntry *entry = malloc(sizeof(eventTableEntry));
|
|
|
|
eventTableEntry *lastEntry = vectorHead(self->eventVtbl);
|
|
|
|
|
|
|
|
// We should only be used to add timer events
|
|
|
|
assert(type == EVT_TIMER_SINGLE_SHOT || type == EVT_TIMER_PERSISTENT);
|
|
|
|
|
|
|
|
entry->id = (lastEntry) ? lastEntry->id + 1 : 1;
|
|
|
|
entry->type = type;
|
|
|
|
entry->callback = handler;
|
|
|
|
entry->userData = userData;
|
|
|
|
|
|
|
|
entry->lastCalled = 0;
|
|
|
|
entry->interval = interval;
|
|
|
|
|
|
|
|
// Add the handler to the table
|
|
|
|
vectorAdd(self->eventVtbl, entry);
|
|
|
|
|
|
|
|
// Return the id of the handler
|
|
|
|
return entry->id;
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void widgetRemoveEventHandlerImpl(widget *self, int id)
|
|
|
|
{
|
2008-07-14 16:41:53 -07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
// Search for the handler with the id
|
|
|
|
for (i = 0; i < vectorSize(self->eventVtbl); i++)
|
|
|
|
{
|
|
|
|
eventTableEntry *handler = vectorAt(self->eventVtbl, i);
|
|
|
|
|
|
|
|
// If the handler matches, remove it
|
|
|
|
if (handler->id == id)
|
|
|
|
{
|
2008-08-02 15:56:14 -07:00
|
|
|
// Generate an EVT_DESTRUCT event to allow the handler to clean-up
|
|
|
|
eventMisc evtDestruct;
|
|
|
|
evtDestruct.event.type = EVT_DESTRUCT;
|
|
|
|
|
|
|
|
handler->callback(self, (event *) &evtDestruct, handler->id,
|
|
|
|
handler->userData);
|
|
|
|
|
|
|
|
// Release the handler
|
|
|
|
free(handler);
|
|
|
|
|
|
|
|
// Finally, remove the event handler from the table
|
2008-07-14 16:41:53 -07:00
|
|
|
vectorRemoveAt(self->eventVtbl, i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool widgetFireCallbacksImpl(widget *self, event *evt)
|
|
|
|
{
|
|
|
|
int i;
|
2008-07-02 16:18:32 -07:00
|
|
|
bool ret;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
for (i = 0; i < vectorSize(self->eventVtbl); i++)
|
|
|
|
{
|
|
|
|
eventTableEntry *handler = vectorAt(self->eventVtbl, i);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// If handler is registered to handle evt
|
2008-04-24 11:07:47 -07:00
|
|
|
if (handler->type == evt->type)
|
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
// Fire the callback
|
2008-07-14 16:41:53 -07:00
|
|
|
ret = handler->callback(self, evt, handler->id, handler->userData);
|
2008-07-02 16:18:32 -07:00
|
|
|
|
2008-07-15 03:12:03 -07:00
|
|
|
// Update the last called time
|
|
|
|
handler->lastCalled = evt->time;
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Break if the handler returned false
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-22 10:55:52 -07:00
|
|
|
// FIXME
|
2008-04-24 11:07:47 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-15 03:12:03 -07:00
|
|
|
bool widgetFireTimerCallbacksImpl(widget *self, event *evt)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
bool ret;
|
|
|
|
eventTimer evtTimer = *((eventTimer *) evt);
|
|
|
|
|
|
|
|
// We should only be passed EVT_TIMER events
|
|
|
|
assert(evt->type == EVT_TIMER);
|
|
|
|
|
|
|
|
for (i = 0; i < vectorSize(self->eventVtbl); i++)
|
|
|
|
{
|
|
|
|
eventTableEntry *handler = vectorAt(self->eventVtbl, i);
|
|
|
|
|
|
|
|
// See if the handler is registered to handle timer events
|
|
|
|
if (handler->type == EVT_TIMER_SINGLE_SHOT
|
|
|
|
|| handler->type == EVT_TIMER_PERSISTENT)
|
|
|
|
{
|
|
|
|
// See if the event needs to be fired
|
|
|
|
if (evt->time >= (handler->lastCalled + handler->interval))
|
|
|
|
{
|
|
|
|
// Ensure the type of our custom event matches
|
2008-08-09 13:52:51 -07:00
|
|
|
evtTimer.event.type = handler->type;
|
2008-07-15 03:12:03 -07:00
|
|
|
|
|
|
|
// Fire the associated callback
|
|
|
|
ret = handler->callback(self, (event *) &evtTimer, handler->id,
|
|
|
|
handler->userData);
|
|
|
|
|
|
|
|
// Update the last called time
|
|
|
|
handler->lastCalled = evt->time;
|
|
|
|
|
|
|
|
// If the event is single shot then remove it
|
|
|
|
if (handler->type == EVT_TIMER_SINGLE_SHOT)
|
|
|
|
{
|
|
|
|
widgetRemoveEventHandler(self, handler->id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
void widgetEnableImpl(widget *self)
|
|
|
|
{
|
|
|
|
int i;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// First make sure our parent is enabled
|
|
|
|
if (self->parent && !self->parent->isEnabled)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Enable ourself
|
|
|
|
self->isEnabled = true;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Enable all of our children
|
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
2008-05-06 09:34:34 -07:00
|
|
|
widgetEnable(vectorAt(self->children, i));
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetDisableImpl(widget *self)
|
|
|
|
{
|
|
|
|
int i;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// If we are currently disabled, return
|
|
|
|
if (!self->isEnabled)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Disable ourself
|
|
|
|
self->isEnabled = false;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Disable our children
|
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
|
|
|
widgetDisable(WIDGET(vectorAt(self->children, i)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-01 14:24:19 -07:00
|
|
|
void widgetShowImpl(widget *self)
|
|
|
|
{
|
|
|
|
// Make ourself visible
|
|
|
|
self->isVisible = true;
|
|
|
|
|
|
|
|
// We need redrawing
|
|
|
|
self->needsRedraw = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetHideImpl(widget *self)
|
|
|
|
{
|
|
|
|
// Make ourself invisible
|
|
|
|
self->isVisible = false;
|
|
|
|
|
|
|
|
// NB: No effect on redrawing
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
void widgetFocusImpl(widget *self)
|
2008-06-22 16:28:34 -07:00
|
|
|
{
|
2008-07-16 12:14:35 -07:00
|
|
|
eventMisc evt;
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Check that we are not currently focused
|
|
|
|
if (self->hasFocus)
|
|
|
|
{
|
|
|
|
int i;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Blur any of our currently focused child widgets
|
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
|
|
|
widget *child = vectorAt(self->children, i);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
if (child->hasFocus)
|
|
|
|
{
|
|
|
|
widgetBlur(child);
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
return;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// If we have a parent, focus it
|
|
|
|
if (self->parent)
|
|
|
|
{
|
|
|
|
widgetFocus(self->parent);
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Focus ourself
|
|
|
|
self->hasFocus = true;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Fire our on-focus callbacks
|
2008-07-04 06:09:23 -07:00
|
|
|
// FIXME: We need to set the timestamp of the event
|
|
|
|
evt.event.type = EVT_FOCUS;
|
|
|
|
widgetFireCallbacks(self, (event *) &evt);
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void widgetBlurImpl(widget *self)
|
|
|
|
{
|
|
|
|
widget *current;
|
2008-07-16 12:14:35 -07:00
|
|
|
eventMisc evt;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-26 04:23:55 -07:00
|
|
|
// Make sure we have focus
|
|
|
|
if (!self->hasFocus)
|
|
|
|
{
|
|
|
|
// TODO: We should log this eventuality
|
|
|
|
return;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// First blur any focused child widgets
|
|
|
|
while ((current = widgetGetCurrentlyFocused(self)) != self)
|
|
|
|
{
|
|
|
|
widgetBlur(current);
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Blur ourself
|
|
|
|
self->hasFocus = false;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Fire off the on-blur callbacks
|
2008-07-04 06:09:23 -07:00
|
|
|
// FIXME: We need to set the timestamp of the event
|
|
|
|
evt.event.type = EVT_BLUR;
|
|
|
|
widgetFireCallbacks(self, (event *) &evt);
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
void widgetResizeImpl(widget *self, int w, int h)
|
2008-05-06 09:34:34 -07:00
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
const size minSize = widgetGetMinSize(self);
|
|
|
|
const size maxSize = widgetGetMaxSize(self);
|
|
|
|
|
2008-07-08 09:46:53 -07:00
|
|
|
assert(minSize.x <= w);
|
|
|
|
assert(minSize.y <= h);
|
|
|
|
assert(w <= maxSize.x);
|
|
|
|
assert(h <= maxSize.y);
|
2008-07-02 16:18:32 -07:00
|
|
|
|
|
|
|
self->size.x = w;
|
|
|
|
self->size.y = h;
|
|
|
|
|
|
|
|
// Re-create the cairo context at this new size
|
2008-07-07 12:10:23 -07:00
|
|
|
widgetCairoCreate(&self->cr, CAIRO_FORMAT_ARGB32, w, h);
|
|
|
|
|
|
|
|
// If a mask is enabled; re-create it also
|
|
|
|
if (self->maskEnabled)
|
|
|
|
{
|
|
|
|
widgetCairoCreate(&self->maskCr, CAIRO_FORMAT_A1, w, h);
|
|
|
|
|
|
|
|
// Re-draw the mask (only done on resize)
|
|
|
|
widgetDrawMask(self);
|
|
|
|
}
|
2008-07-02 16:18:32 -07:00
|
|
|
|
|
|
|
// Set the needs redraw flag
|
|
|
|
self->needsRedraw = true;
|
2008-07-07 15:39:52 -07:00
|
|
|
|
|
|
|
// If we have any children, we need to redo their layout
|
|
|
|
if (vectorSize(self->children))
|
|
|
|
{
|
|
|
|
widgetDoLayout(self);
|
|
|
|
}
|
2008-05-06 09:34:34 -07:00
|
|
|
}
|
|
|
|
|
2008-07-18 15:21:54 -07:00
|
|
|
void widgetCompositeImpl(widget *self)
|
2008-06-22 05:03:42 -07:00
|
|
|
{
|
|
|
|
int i;
|
2008-07-02 16:18:32 -07:00
|
|
|
|
2008-08-01 14:24:19 -07:00
|
|
|
// Do not composite unless we are visible
|
|
|
|
if (!self->isVisible)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-01 11:55:42 -07:00
|
|
|
// Translate such that (0,0) is the top-left of ourself
|
|
|
|
glTranslatef(self->offset.x, self->offset.y, 0.0f);
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Composite ourself
|
2008-07-18 15:21:54 -07:00
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self->textureId);
|
|
|
|
|
|
|
|
glBegin(GL_QUADS);
|
2008-08-02 07:47:12 -07:00
|
|
|
glTexCoord2f(0, 0);
|
|
|
|
glVertex2f(0, 0);
|
2008-07-18 15:21:54 -07:00
|
|
|
|
2008-08-02 07:47:12 -07:00
|
|
|
glTexCoord2f(0, self->size.y);
|
|
|
|
glVertex2f(0, self->size.y);
|
2008-07-18 15:21:54 -07:00
|
|
|
|
2008-08-02 07:47:12 -07:00
|
|
|
glTexCoord2f(self->size.x, self->size.y);
|
|
|
|
glVertex2f(self->size.x, self->size.y);
|
2008-07-18 15:21:54 -07:00
|
|
|
|
2008-08-02 07:47:12 -07:00
|
|
|
glTexCoord2f(self->size.x, 0);
|
|
|
|
glVertex2f(self->size.x, 0);
|
2008-07-18 15:21:54 -07:00
|
|
|
glEnd();
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Now our children
|
2008-06-22 05:03:42 -07:00
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
|
|
|
widget *child = vectorAt(self->children, i);
|
2008-07-02 16:18:32 -07:00
|
|
|
|
|
|
|
// Composite
|
2008-07-18 15:21:54 -07:00
|
|
|
widgetComposite(child);
|
2008-06-22 16:28:34 -07:00
|
|
|
}
|
2008-08-01 11:55:42 -07:00
|
|
|
|
|
|
|
// Restore the matrix
|
|
|
|
glTranslatef(-self->offset.x, -self->offset.y, 0.0f);
|
2008-06-22 05:03:42 -07:00
|
|
|
}
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
void widgetEnableMask(widget *self)
|
|
|
|
{
|
|
|
|
// Check if the mask is already enabled (worth asserting)
|
|
|
|
if (self->maskEnabled)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we implement the doDrawMask method
|
|
|
|
WIDGET_CHECK_METHOD(self, doDrawMask);
|
|
|
|
|
|
|
|
// Prepare the cairo surface
|
|
|
|
widgetCairoCreate(&self->maskCr, CAIRO_FORMAT_A1,
|
|
|
|
self->size.x, self->size.y);
|
|
|
|
|
|
|
|
// Note that the mask is now enabled
|
|
|
|
self->maskEnabled = true;
|
|
|
|
|
|
|
|
// Draw the mask
|
|
|
|
widgetDrawMask(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetDisableMask(widget *self)
|
|
|
|
{
|
|
|
|
// NO-OP if the mask is not enabled
|
|
|
|
if (!self->maskEnabled)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release the cairo context
|
|
|
|
cairo_destroy(self->maskCr);
|
|
|
|
|
|
|
|
// Note that the mask is now disabled
|
|
|
|
self->maskEnabled = false;
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
widget *widgetGetCurrentlyFocused(widget *self)
|
|
|
|
{
|
|
|
|
int i;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
if (!self->hasFocus)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
|
|
|
{
|
|
|
|
widget *child = vectorAt(self->children, i);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
if (child->hasFocus)
|
|
|
|
{
|
|
|
|
return widgetGetCurrentlyFocused(child);
|
|
|
|
}
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// None of our children are focused, return ourself
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool widgetHandleEventImpl(widget *self, event *evt)
|
2008-06-21 10:23:00 -07:00
|
|
|
{
|
|
|
|
// If the event should be passed onto our children
|
2008-06-22 16:28:34 -07:00
|
|
|
bool relevant = true;
|
|
|
|
|
2008-08-01 14:24:19 -07:00
|
|
|
// If we are disabled or invisible then only timer events are relevant
|
|
|
|
if ((!self->isEnabled || !self->isVisible)
|
|
|
|
&& evt->type != EVT_TIMER)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
switch (evt->type)
|
|
|
|
{
|
|
|
|
case EVT_MOUSE_MOVE:
|
2008-04-27 08:14:42 -07:00
|
|
|
{
|
2008-04-24 11:07:47 -07:00
|
|
|
eventMouse evtMouse = *((eventMouse *) evt);
|
2008-07-07 14:19:58 -07:00
|
|
|
bool newHasMouse = widgetHasMouse(self, evtMouse.loc);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-22 10:53:08 -07:00
|
|
|
/*
|
|
|
|
* Mouse motion events should not be dispatched if a mouse button
|
|
|
|
* is currently `down' on our parent but not ourself.
|
|
|
|
*/
|
2008-07-02 16:18:32 -07:00
|
|
|
if (self->parent
|
|
|
|
&& self->parent->hasMouseDown
|
|
|
|
&& !self->hasMouseDown)
|
2008-06-22 10:53:08 -07:00
|
|
|
{
|
|
|
|
relevant = false;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// If we have just `got' the mouse
|
|
|
|
if (newHasMouse && !self->hasMouse)
|
|
|
|
{
|
|
|
|
// Generate a EVT_MOUSE_ENTER event
|
|
|
|
evtMouse.event.type = EVT_MOUSE_ENTER;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// 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;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// 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);
|
|
|
|
}
|
2008-06-21 10:23:00 -07:00
|
|
|
// Of no interest to us (and therefore not to our children either)
|
|
|
|
else
|
|
|
|
{
|
|
|
|
relevant = false;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
// Update the status of the mouse
|
|
|
|
self->hasMouse = newHasMouse;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-21 10:23:00 -07:00
|
|
|
case EVT_MOUSE_DOWN:
|
|
|
|
case EVT_MOUSE_UP:
|
|
|
|
{
|
|
|
|
eventMouseBtn evtMouseBtn = *((eventMouseBtn *) evt);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// If the mouse is inside of the widget
|
2008-07-07 14:19:58 -07:00
|
|
|
if (widgetHasMouse(self, evtMouseBtn.loc))
|
2008-06-21 10:23:00 -07:00
|
|
|
{
|
2008-06-22 10:53:08 -07:00
|
|
|
// If it is a mouse-down event set hasMouseDown to true
|
|
|
|
if (evt->type == EVT_MOUSE_DOWN)
|
|
|
|
{
|
|
|
|
self->hasMouseDown = true;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Fire mouse down and mouse up callbacks
|
2008-06-21 10:23:00 -07:00
|
|
|
widgetFireCallbacks(self, (event *) &evtMouseBtn);
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
// Check for a click event
|
2008-06-22 10:53:08 -07:00
|
|
|
if (evt->type == EVT_MOUSE_UP && self->hasMouseDown)
|
|
|
|
{
|
|
|
|
evtMouseBtn.event.type = EVT_MOUSE_CLICK;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-22 10:53:08 -07:00
|
|
|
widgetFireCallbacks(self, (event *) &evtMouseBtn);
|
|
|
|
}
|
|
|
|
}
|
2008-07-02 16:18:32 -07:00
|
|
|
// If the mouse is no longer down
|
2008-06-22 10:53:08 -07:00
|
|
|
else if (evt->type == EVT_MOUSE_UP && self->hasMouseDown)
|
|
|
|
{
|
|
|
|
self->hasMouseDown = false;
|
2008-06-21 10:23:00 -07:00
|
|
|
}
|
2008-07-02 16:18:32 -07:00
|
|
|
// Of no interest to us or our children
|
2008-06-21 10:23:00 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
relevant = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EVT_KEY_DOWN:
|
|
|
|
case EVT_KEY_UP:
|
2008-07-09 08:46:46 -07:00
|
|
|
case EVT_TEXT:
|
2008-06-21 10:23:00 -07:00
|
|
|
{
|
|
|
|
// Only relevant if we have focus
|
|
|
|
if (self->hasFocus)
|
2008-06-22 16:28:34 -07:00
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
widgetFireCallbacks(self, evt);
|
2008-06-21 10:23:00 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
relevant = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2008-07-15 03:12:03 -07:00
|
|
|
case EVT_TIMER:
|
|
|
|
{
|
|
|
|
// fireTimerCallbacks will handle this
|
|
|
|
widgetFireTimerCallbacks(self, evt);
|
|
|
|
break;
|
|
|
|
}
|
2008-04-24 11:07:47 -07:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-21 10:23:00 -07:00
|
|
|
// If necessary pass the event onto our children
|
|
|
|
if (relevant)
|
|
|
|
{
|
|
|
|
int i;
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-06-21 10:23:00 -07:00
|
|
|
for (i = 0; i < vectorSize(self->children); i++)
|
2008-06-22 16:28:34 -07:00
|
|
|
{
|
|
|
|
widgetHandleEvent(vectorAt(self->children, i), evt);
|
|
|
|
}
|
2008-06-21 10:23:00 -07:00
|
|
|
}
|
2008-06-22 16:28:34 -07:00
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
bool widgetPointMasked(widget *self, point loc)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The format of the mask is CAIRO_FORMAT_A1, where:
|
|
|
|
* "each pixel is a 1-bit quantity holding an alpha value.
|
|
|
|
* Pixels are packed together into 32-bit quantities"
|
|
|
|
*
|
|
|
|
* TODO: An explanation worth reading.
|
|
|
|
*/
|
|
|
|
|
|
|
|
cairo_surface_t *surface = cairo_get_target(self->maskCr);
|
|
|
|
|
|
|
|
// The number of bytes to a row
|
|
|
|
const int stride = cairo_image_surface_get_stride(surface);
|
|
|
|
|
|
|
|
// The mask itself
|
|
|
|
const uint32_t *data = (uint32_t *) cairo_image_surface_get_data(surface);
|
|
|
|
|
|
|
|
// The 32-bit segment of data we are interested in
|
2008-08-02 07:47:12 -07:00
|
|
|
const uint32_t bits = data[(int) loc.y * (stride / 4) + ((int) loc.x / 32)];
|
2008-07-07 12:10:23 -07:00
|
|
|
|
|
|
|
// Where in the 32-bit segment the pixel is located
|
2008-08-02 07:47:12 -07:00
|
|
|
const uint32_t pixelMask = 1 << ((int) loc.x % 32);
|
2008-07-07 12:10:23 -07:00
|
|
|
|
|
|
|
// Check to see if the pixel is set or not
|
|
|
|
if (bits & pixelMask)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-04-25 16:20:16 -07:00
|
|
|
int widgetAddEventHandler(widget *self, eventType type,
|
|
|
|
callback handler, void *userData)
|
2008-04-24 11:07:47 -07:00
|
|
|
{
|
2008-04-25 16:20:16 -07:00
|
|
|
return WIDGET_GET_VTBL(self)->addEventHandler(self, type, handler, userData);
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
2008-07-15 03:12:03 -07:00
|
|
|
int widgetAddTimerEventHandler(widget *self, eventType type, int interval,
|
|
|
|
callback handler, void *userData)
|
|
|
|
{
|
|
|
|
return WIDGET_GET_VTBL(self)->addTimerEventHandler(self, type, interval,
|
|
|
|
handler, userData);
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-07-15 03:12:03 -07:00
|
|
|
bool widgetFireTimerCallbacks(widget *self, event *evt)
|
|
|
|
{
|
|
|
|
return WIDGET_GET_VTBL(self)->fireTimerCallbacks(self, evt);
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
void widgetEnable(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_GET_VTBL(self)->enable(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetDisable(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_GET_VTBL(self)->disable(self);
|
|
|
|
}
|
|
|
|
|
2008-08-01 14:24:19 -07:00
|
|
|
void widgetShow(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_GET_VTBL(self)->show(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetHide(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_GET_VTBL(self)->hide(self);
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
void widgetFocus(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_GET_VTBL(self)->focus(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetBlur(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_GET_VTBL(self)->blur(self);
|
|
|
|
}
|
|
|
|
|
2008-07-08 02:15:13 -07:00
|
|
|
size widgetGetMinSize(widget *self)
|
2008-05-06 09:34:34 -07:00
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
WIDGET_CHECK_METHOD(self, getMinSize);
|
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
return WIDGET_GET_VTBL(self)->getMinSize(self);
|
|
|
|
}
|
|
|
|
|
2008-07-08 02:15:13 -07:00
|
|
|
size widgetGetMaxSize(widget *self)
|
2008-05-06 09:34:34 -07:00
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
WIDGET_CHECK_METHOD(self, getMaxSize);
|
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
return WIDGET_GET_VTBL(self)->getMaxSize(self);
|
|
|
|
}
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
void widgetResize(widget *self, int x, int y)
|
2008-05-06 09:34:34 -07:00
|
|
|
{
|
2008-07-02 16:18:32 -07:00
|
|
|
WIDGET_GET_VTBL(self)->resize(self, x, y);
|
2008-05-06 09:34:34 -07:00
|
|
|
}
|
|
|
|
|
2008-07-18 15:21:54 -07:00
|
|
|
void widgetComposite(widget *self)
|
2008-06-22 05:03:42 -07:00
|
|
|
{
|
2008-07-18 15:21:54 -07:00
|
|
|
WIDGET_GET_VTBL(self)->composite(self);
|
2008-06-22 05:03:42 -07:00
|
|
|
}
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
void widgetDoDraw(widget *self)
|
2008-04-24 11:07:47 -07:00
|
|
|
{
|
2008-05-06 09:34:34 -07:00
|
|
|
WIDGET_CHECK_METHOD(self, doDraw);
|
|
|
|
|
2008-07-02 16:18:32 -07:00
|
|
|
WIDGET_GET_VTBL(self)->doDraw(self);
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|
|
|
|
|
2008-07-07 12:10:23 -07:00
|
|
|
void widgetDoDrawMask(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_CHECK_METHOD(self, doDrawMask);
|
|
|
|
|
|
|
|
WIDGET_GET_VTBL(self)->doDrawMask(self);
|
|
|
|
}
|
|
|
|
|
2008-05-06 09:34:34 -07:00
|
|
|
bool widgetDoLayout(widget *self)
|
|
|
|
{
|
|
|
|
WIDGET_CHECK_METHOD(self, doLayout);
|
|
|
|
|
|
|
|
return WIDGET_GET_VTBL(self)->doLayout(self);
|
|
|
|
}
|
|
|
|
|
2008-04-24 11:07:47 -07:00
|
|
|
bool widgetHandleEvent(widget *self, event *evt)
|
|
|
|
{
|
|
|
|
return WIDGET_GET_VTBL(self)->handleEvent(self, evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void widgetDestroy(widget *self)
|
|
|
|
{
|
2008-07-16 14:19:33 -07:00
|
|
|
WIDGET_GET_VTBL(self)->destroy(self);
|
2008-04-24 11:07:47 -07:00
|
|
|
}
|