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) { // Call the destructor for the widget widgetDestroy(vectorAt(self->children, i)); // Remove it from the list of children vectorRemoveAt(self->children, i); // Re-layout the window (so long as we are part of one) if (self->size.x != -1.0f && self->size.y != -1.0f) { widgetDoLayout(widgetGetRoot(self)); } } // See if it is one of its children else if (child->parent != self) { widgetRemoveChild(vectorAt(self->children, i), child); } } }
widget *tableGetCell(const table *self, int row, int column) { int i; const int numChildren = vectorSize(WIDGET(self)->children); // Ensure that row and column are valid assert(row > 0 && row <= tableGetRowCount(self)); assert(column > 0 && column <= tableGetColumnCount(self)); // Search through the list of children until we find one matching for (i = 0; i < numChildren; i++) { // Legal as widget->children and table->childPositions are siblings const childPositionInfo *pos = vectorAt(self->childPositions, i); // See if it is a match if (row >= pos->row && row < pos->row + pos->rowspan && column >= pos->column && column < pos->column + pos->colspan) { // We've found the widget return vectorAt(WIDGET(self)->children, i); } } // If the cell is empty, return NULL return NULL; }
void widgetDisableImpl(widget *self) { int i; eventMisc evt; // If we are currently disabled, return if (!self->isEnabled) { return; } // Disable ourself self->isEnabled = false; // Fire any on-disable callbacks evt.event = widgetCreateEvent(EVT_DISABLE); widgetFireCallbacks(self, (event *) &evt); // Disable our children for (i = 0; i < vectorSize(self->children); i++) { widgetDisable(vectorAt(self->children, i)); } }
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 widgetFireCallbacksImpl(widget *self, const event *evt) { int i; bool ret; for (i = 0; i < vectorSize(self->eventVtbl); i++) { eventTableEntry *handler = vectorAt(self->eventVtbl, i); // If handler is registered to handle evt if (handler->type == evt->type) { // Fire the callback ret = handler->callback(self, evt, handler->id, handler->userData); // Update the last called time handler->lastCalled = evt->time; // Break if the handler returned false if (!ret) { break; } } } // FIXME return true; }
/** * Returns the (spanning) column with the greatest difference between * childSize.x and the sum of the columns it spans (divided by the number of * columns it spans). * * @param self The table to determine the most undersized column of. * @param rowHeight An array containing the current column heights of the column * in the table. * @param childSize The desired (min/max) size of the children in the table. * @return The offset of the most undersized column in the table in the * WIDGET(self)->children array, or -1 if there are no such rows. * @see tableGetMostOversizedRow */ static int tableGetMostOversizedColumn(const table *self, const int *columnWidth, const size *childSize) { int i; float maxDelta = 0.0f; int maxDeltaIndex = -1; const int numChildren = vectorSize(WIDGET(self)->children); for (i = 0; i < numChildren; i++) { const childPositionInfo *pos = vectorAt(self->childPositions, i); // See if the column is a spanning column if (pos->colspan != 1) { float sum = tablePartialSum(columnWidth, pos->column - 1, pos->colspan); // If the column is wider than the sum of its spanned columns if (childSize[i].x > sum) { float delta = (childSize[i].x - sum) / (float) pos->colspan; if (delta > maxDelta) { maxDelta = delta; maxDeltaIndex = i; } } } } return maxDeltaIndex; }
void widgetRemoveEventHandlerImpl(widget *self, int id) { 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) { // If there is a destructor; call it if (handler->destructor) { // Generate an EVT_DESTRUCT event eventMisc evtDestruct; evtDestruct.event = widgetCreateEvent(EVT_DESTRUCT); handler->destructor(self, (event *) &evtDestruct, handler->id, handler->userData); } // Release the handler free(handler); // Finally, remove the event handler from the table vectorRemoveAt(self->eventVtbl, i); break; } } }
/** * Returns the (spanning) row with the greatest difference between childSize.y * and the sum of the rows it spans (divided by the number of rows it spans). * * This function is integral to solving the problem about how to decide which * rows to increase the height of when handing spanning rows. This is because * the order in which we increase the size of rows affects the final layout. * * @param self The table to determine the most undersized row of. * @param rowHeight An array containing the current row heights of the rows in * the table. * @param childSize The desired (min/max) size of the children in the table. * @return The offset of the most undersized row in the table in the * WIDGET(self)->children array, or -1 if there are no such rows. */ static int tableGetMostOversizedRow(const table *self, const int *rowHeight, const size *childSize) { int i; float maxDelta = 0.0f; int maxDeltaIndex = -1; const int numChildren = vectorSize(WIDGET(self)->children); for (i = 0; i < numChildren; i++) { const childPositionInfo *pos = vectorAt(self->childPositions, i); // See if the row is a spanning row if (pos->rowspan != 1) { float sum = tablePartialSum(rowHeight, pos->row - 1, pos->rowspan); // If the row is higher than the sum of its spanned rows if (childSize[i].y > sum) { float delta = (childSize[i].y - sum) / (float) pos->rowspan; if (delta > maxDelta) { maxDelta = delta; maxDeltaIndex = i; } } } } return maxDeltaIndex; }
void widgetCompositeImpl(widget *self) { float blendColour[4]; int i; // Do not composite unless we are visible if (!self->isVisible) { return; } // Save the current model-view matrix glPushMatrix(); // Translate such that (0,0) is the top-left of ourself glTranslatef(self->offset.x, self->offset.y, 0.0f); // Scale if necessary glScalef(self->scale.x, self->scale.y, 1.0f); // Rotate ourself glRotatef(self->rotate, 0.0f, 0.0f, 1.0f); // Set our alpha (blend colour) glGetFloatv(GL_BLEND_COLOR, blendColour); glBlendColor(1.0f, 1.0f, 1.0f, blendColour[3] * self->alpha); // Composite ourself glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self->textureId); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0.0f, self->size.y); glVertex2f(0.0f, self->size.y); glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f); glTexCoord2f(self->size.x, self->size.y); glVertex2f(self->size.x, self->size.y); glTexCoord2f(self->size.x, 0.0f); glVertex2f(self->size.x, 0.0f); glEnd(); // Now our children for (i = 0; i < vectorSize(self->children); i++) { widget *child = vectorAt(self->children, i); // Composite widgetComposite(child); } // Restore the model-view matrix glPopMatrix(); // Restore the blend colour glBlendColor(1.0f, 1.0f, 1.0f, blendColour[3]); }
void widgetDraw(widget *self) { int i; // See if we need to be redrawn if (self->needsRedraw) { void *bits = cairo_image_surface_get_data(cairo_get_target(self->cr)); self->needsRedraw = false; // Mark the texture as needing uploading self->textureNeedsUploading = true; // 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); // Save (push) the current context cairo_save(self->cr); // Redaw ourself widgetDoDraw(self); // Restore the context cairo_restore(self->cr); // Update the texture if widgetEndGL has not done it for us if (self->textureNeedsUploading) { // Flush the cairo surface to ensure that all drawing is completed cairo_surface_flush(cairo_get_target(self->cr)); 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); self->textureNeedsUploading = false; } } // 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); } }
tReal tPropSurface::hubRadiusAt(tReal /*x*/) { // Spaeter mal durch interpolation eines Splines ersetzen. // return hubDiameter*0.5*fDiameter*fScale; tVector X; X = vectorAt(0,0); X = X.toCylCOS(tVector(0,0,0),tVector(1,0,0),tVector(0,1,0)); return X.y; }
void tableRemoveChildImpl(widget *self, widget *child) { // If we are not the childs parent, delegate to widgetRemoveChildImpl if (self != child->parent) { widgetRemoveChildImpl(self, child); } // We are the childs parent, so there is some bookkeeping to tend to else { int i; // Find the index of the child for (i = 0; i < vectorSize(self->children); i++) { if (child == vectorAt(self->children, i)) { break; } } // Call the child's destructor and remove it widgetDestroy(vectorAt(self->children, i)); vectorRemoveAt(self->children, i); // Remove the childs positional information free(vectorAt(TABLE(self)->childPositions, i)); vectorRemoveAt(TABLE(self)->childPositions, i); // Remove any empty rows/columns tableRemoveEmptyRows(TABLE(self)); tableRemoveEmptyColumns(TABLE(self)); // Redo the layout of the window if (self->size.x != -1 && self->size.y != -1) { widgetDoLayout(widgetGetRoot(self)); } } }
/** * Passes the event, evt, onto each child of self. * * @param self The widget to dispatch the event for. * @param evt The event to dispatch. * @return True if any child widgets of self handled the event, false if self * either has no children or if none of them handled the event. */ static bool widgetDispatchEventToChildren(widget *self, const event *evt) { int i; const int numChildren = vectorSize(self->children); bool handled = false; for (i = 0; i < numChildren; i++) { // 'We' handled the event if any of our children handled it if (widgetHandleEvent(vectorAt(self->children, i), evt)) { handled = true; } } return handled; }
bool widgetFireTimerCallbacksImpl(widget *self, const 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 evtTimer.event.type = handler->type; // 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; }
/** * 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(const 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; }
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; }
/** * Removes any rows from the table which no cells occupy. * * @param self The table to remove empty rows from. */ static void tableRemoveEmptyRows(table *self) { int i, j, k; const int columnCount = tableGetColumnCount(self); const int numChildren = vectorSize(WIDGET(self)->children); // Iterate over each row of the table for (i = 1; i <= tableGetRowCount(self); i++) { // See if any of the columns in the row are occupied for (j = 1; j <= columnCount; j++) { if (tableGetCell(self, i, j)) { break; } } // If the cells are all empty, we can remove the row if (j > columnCount) { // Find all cells in rows after the one to be removed for (k = 0; k < numChildren; k++) { childPositionInfo *pos = vectorAt(self->childPositions, k); // Decrease the row number of all rows > i by 1 if (pos->row > i) { pos->row--; } } // The table now has one fewer rows in it self->numRows--; i--; } } }
widget *widgetGetCurrentlyMousedOver(widget *self) { int i; // Make sure we have the mouse if (!self->hasMouse) { return NULL; } // See if any of our children are moused over for (i = 0; i < vectorSize(self->children); i++) { widget *child = vectorAt(self->children, i); if (child->hasMouse) { return widgetGetCurrentlyMousedOver(child); } } // None of our children have the mouse; return ourself return self; }
void widgetEnableImpl(widget *self) { int i; eventMisc evt; // First make sure our parent is enabled if (self->parent && !self->parent->isEnabled) { return; } // Enable ourself self->isEnabled = true; // Fire any on-enable callbacks evt.event = widgetCreateEvent(EVT_ENABLE); widgetFireCallbacks(self, (event *) &evt); // Enable all of our children for (i = 0; i < vectorSize(self->children); i++) { widgetEnable(vectorAt(self->children, i)); } }
void widgetFocusImpl(widget *self) { eventMisc evt; // 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 evt.event = widgetCreateEvent(EVT_FOCUS); widgetFireCallbacks(self, (event *) &evt); }
void *vectorHead(vector *v) { return vectorAt(v, v->head - 1); }
int main(int argc, char *argv[]) { SDL_Surface *screen; SDL_Event sdlEvent; bool quit = false; lua_State* L; // Make sure everything is cleaned up on quit atexit(SDL_Quit); // Init SDL if (SDL_Init(SDL_INIT_VIDEO) < -1) { fprintf(stderr, "SDL_init: %s\n", SDL_GetError()); exit(1); } // Enable UNICODE support (for key events) SDL_EnableUNICODE(true); // Enable double-buffering SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Enable sync-to-vblank SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); // 800x600 true colour screen = SDL_SetVideoMode(800, 600, 32, SDL_OPENGL); if (screen == NULL) { fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError()); exit(1); } SDL_WM_SetCaption("Warzone UI Simulator", NULL); // Init OpenGL glClearColor(0.3f, 0.3f, 0.3f, 0.0f); glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendColor(1.0f, 1.0f, 1.0f, 1.0f); glEnable(GL_TEXTURE_RECTANGLE_ARB); glViewport(0, 0, 800, 600); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, 800, 600, 0.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* * Create the GUI */ widgetSDLInit(); L = lua_open_betawidget(); createGUI(L); // Main event loop while (!quit) { int i; while (SDL_PollEvent(&sdlEvent)) { if (sdlEvent.type == SDL_QUIT) { quit = true; } else { widgetHandleSDLEvent(&sdlEvent); } } // Fire timer events widgetFireTimers(); glClear(GL_COLOR_BUFFER_BIT); for (i = 0; i < vectorSize(windowGetWindowVector()); i++) { window *wnd = vectorAt(windowGetWindowVector(), i); widgetDraw(WIDGET(wnd)); widgetComposite(WIDGET(wnd)); } SDL_GL_SwapBuffers(); } lua_close(L); widgetSDLQuit(); return EXIT_SUCCESS; }
bool tableDoLayoutImpl(widget *self) { table *selfTable = TABLE(self); const int numChildren = vectorSize(self->children); const int numRows = tableGetRowCount(selfTable); const int numColumns = tableGetColumnCount(selfTable); int i; int dx, dy; int *minColumnWidth = alloca(sizeof(int) * numColumns); int *maxColumnWidth = alloca(sizeof(int) * numColumns); int *minRowHeight = alloca(sizeof(int) * numRows); int *maxRowHeight = alloca(sizeof(int) * numRows); // Syntatic sugar int *columnWidth = minColumnWidth; int *rowHeight = minRowHeight; // Get the minimum and maximum cell sizes tableGetMinimumCellSizes(selfTable, minRowHeight, minColumnWidth); tableGetMaximumCellSizes(selfTable, maxRowHeight, maxColumnWidth); // Calculate how much space we have left to fill dx = self->size.x - tablePartialSum(minColumnWidth, 0, numColumns); dy = self->size.y - tablePartialSum(minRowHeight, 0, numRows); // Increase the column size until we run out of space for (i = 0; dx; i = (i + 1) % numColumns) { // If the column is not maxed out, increases its size by one if (columnWidth[i] < maxColumnWidth[i]) { columnWidth[i]++; dx--; } } // Increase the row size until we run out of space for (i = 0; dy; i = (i + 1) % numRows) { // If the row is not maxed out, increase its size by one if (rowHeight[i] < minRowHeight[i]) { rowHeight[i]++; dy--; } } // Now we need to position the children, taking padding into account for (i = 0; i < numChildren; i++) { // Get the child and its position info widget *child = vectorAt(self->children, i); const childPositionInfo *pos = vectorAt(selfTable->childPositions, i); size maxChildSize = widgetGetMaxSize(child); // left is the sum of all of the preceding columns int left = tablePartialSum(columnWidth, 0, pos->column); // top is the sum of all of the preceding rows int top = tablePartialSum(rowHeight, 0, pos->row); // cellWidth is the sum of the columns we span int cellWidth = tablePartialSum(columnWidth, pos->column - 1, pos->colspan); // cellHeight is the sum of the rows we span int cellHeight = tablePartialSum(rowHeight, pos->row - 1, pos->rowspan); // Final width and height of the child int w, h; // Final x,y offsets of the child int x, y; // If we are not the last row/column, subtract the row/column padding if (pos->column + pos->colspan - 1 != numColumns) { cellWidth -= selfTable->columnPadding; } if (pos->row + pos->rowspan - 1 != numRows) { cellHeight -= selfTable->rowPadding; } // Compute the final width and height of the child w = MIN(cellWidth, maxChildSize.x); h = MIN(cellHeight, maxChildSize.y); // Pad out any remaining space switch (pos->hAlignment) { case LEFT: x = left; break; case CENTRE: x = left + (cellWidth - w) / 2; break; case RIGHT: x = left + cellWidth - w; break; } switch (pos->vAlignment) { case TOP: y = top; break; case MIDDLE: y = top + (cellHeight - h) / 2; break; case BOTTOM: y = top + cellHeight - h; break; } // Resize and reposition the widget widgetResize(child, w, h); widgetReposition(child, x, y); } return true; }
/** * Computes the maximum row/column size for each row/column in the table. It is * important to note that the widths/heights are inclusive of padding. Therefore * all but the rightmost column and bottom row will have either self->rowPadding * or self->columnPadding added onto their sizes. * * @param self The table to get the minimum cell sizes of. * @param maxRowHeight The array to place the maximum row heights for the table * into; assumed to be tableGetRowCount(self) in size. * @param maxColumnWidth The array to place the maximum column widths for the * table into; assumed to be tableGetColumnCount(self) * in size. */ static void tableGetMaximumCellSizes(const table *self, int *maxRowHeight, int *maxColumnWidth) { int i; int spanningIndex; const int numChildren = vectorSize(WIDGET(self)->children); const int numRows = tableGetRowCount(self); const int numColumns = tableGetColumnCount(self); size *maxChildSize = alloca(sizeof(size) * numChildren); // Zero the min row/column sizes memset(maxRowHeight, '\0', sizeof(int) * numRows); memset(maxColumnWidth, '\0', sizeof(int) * numColumns); // Get the maximum row/column sizes for single-spanning cells for (i = 0; i < numChildren; i++) { const childPositionInfo *pos = vectorAt(self->childPositions, i); const int row = pos->row - 1; const int col = pos->column - 1; // Get the maximum size of the cell maxChildSize[i] = widgetGetMaxSize(vectorAt(WIDGET(self)->children, i)); // If the row has a rowspan of 1; see if it is the highest thus far if (pos->rowspan == 1) { maxRowHeight[row] = MAX(maxRowHeight[row], maxChildSize[i].y); } // If the column has a colspan of 1; see if it is the widest thus far if (pos->colspan == 1) { maxColumnWidth[col] = MAX(maxColumnWidth[col], maxChildSize[i].x); } } // Handle spanning rows while ((spanningIndex = tableGetMostOversizedRow(self, maxRowHeight, maxChildSize)) != -1) { int i; const childPositionInfo *pos = vectorAt(self->childPositions, spanningIndex); // Calculate how much larger we need to make the spanned rows int delta = maxChildSize[spanningIndex].y - tablePartialSum(maxRowHeight, pos->row - 1, pos->rowspan); // Loop over the rows spanned increasing their height by 1 for (i = pos->row; delta; i = pos->row + (i + 1) % pos->rowspan, delta--) { maxRowHeight[i]++; } } // Handle spanning columns while ((spanningIndex = tableGetMostOversizedColumn(self, maxColumnWidth, maxChildSize)) != -1) { int i; const childPositionInfo *pos = vectorAt(self->childPositions, spanningIndex); // Calculate how much larger we need to make the spanned columns int delta = maxChildSize[spanningIndex].x - tablePartialSum(maxColumnWidth, pos->column - 1, pos->colspan); for (i = pos->column; delta; i = pos->column + (i + 1) % pos->colspan, delta--) { maxColumnWidth[i]++; } } }
static bool widgetAnimationTimerCallback(widget *self, const event *evt, int handlerId, void *userData) { int i; vector *frames = userData; animationFrame *currFrame; // Regular timer event if (evt->type == EVT_TIMER_PERSISTENT) { bool didInterpolate = false; // Currently active keyframes to be interpolated between struct { animationFrame *pre; animationFrame *post; } interpolateFrames[ANI_TYPE_COUNT] = { { NULL, NULL } }; // Work out what frames we need to interpolate between for (i = 0; i < vectorSize(frames); i++) { currFrame = vectorAt(frames, i); /* * We are only interested in the key frames which either directly * precede now or come directly after. (As it is these frames which * will be interpolated between.) */ if (currFrame->time <= evt->time) { interpolateFrames[currFrame->type].pre = currFrame; } else if (currFrame->time > evt->time && !interpolateFrames[currFrame->type].post) { interpolateFrames[currFrame->type].post = currFrame; } } // Do the interpolation for (i = 0; i < ANI_TYPE_COUNT; i++) { // If there are frames to interpolate between then do so if (interpolateFrames[i].pre && interpolateFrames[i].post) { // Get the points to interpolate between animationFrame k1 = *interpolateFrames[i].pre; animationFrame k2 = *interpolateFrames[i].post; int time = evt->time; switch (i) { case ANI_TYPE_TRANSLATE: { // Get the new position point pos = widgetAnimationInterpolateTranslate(self, k1, k2, time); // Set the new position widgetReposition(self, pos.x, pos.y); break; } case ANI_TYPE_ROTATE: // Update the widgets rotation self->rotate = widgetAnimationInterpolateRotate(self, k1, k2, time); break; case ANI_TYPE_SCALE: // Update the widgets scale factor self->scale = widgetAnimationInterpolateScale(self, k1, k2, time); break; case ANI_TYPE_ALPHA: // Update the widgets alpha self->alpha = widgetAnimationInterpolateAlpha(self, k1, k2, time); break; } // Make a note that we did interpolate didInterpolate = true; } } // If there was no interpolation then the animation is over if (!didInterpolate) { // Remove ourself (the event handler) widgetRemoveEventHandler(self, handlerId); } } else if (evt->type == EVT_DESTRUCT) { animationFrame *lastFrame[ANI_TYPE_COUNT] = { NULL }; // Find the last frame of each animation type for (i = 0; i < vectorSize(frames); i++) { currFrame = vectorAt(frames, i); lastFrame[currFrame->type] = currFrame; } // Set the position/rotation/scale/alpha to that of the final frame for (i = 0; i < ANI_TYPE_COUNT; i++) { if (lastFrame[i]) switch (i) { case ANI_TYPE_TRANSLATE: self->offset = lastFrame[ANI_TYPE_TRANSLATE]->data.translate; break; case ANI_TYPE_ROTATE: self->rotate = lastFrame[ANI_TYPE_ROTATE]->data.rotate; break; case ANI_TYPE_SCALE: self->scale = lastFrame[ANI_TYPE_SCALE]->data.scale; break; case ANI_TYPE_ALPHA: self->alpha = lastFrame[ANI_TYPE_ALPHA]->data.alpha; break; default: break; } } // Free the frames vector vectorMapAndDestroy(frames, free); } return true; }