/* * sdlgui.cpp * * This file is taken from the ARAnyM project which builds a new and powerful * TOS/FreeMiNT compatible virtual machine running on almost any hardware. * * Copyright (c) 2001 Thomas Huth - taken from his hatari project * Copyright (c) 2002-2005 Petr Stehlik of ARAnyM dev team (see AUTHORS) * * It is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * You should have received a copy of the GNU General Public License * along with Frodo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdeps.h" #include "sdlgui.h" #include #include "font8.h" static SDL_Surface *mainsurface = NULL; #define sdlscrn mainsurface static SDL_Surface *fontgfx=NULL; static int fontwidth, fontheight; /* Height and width of the actual font */ // Stores current dialog coordinates static SDL_Rect DialogRect = {0, 0, 0, 0}; // Used by SDLGui_Get[First|Next]BackgroundRect() static SDL_Rect BackgroundRect = {0, 0, 0, 0}; static int BackgroundRectCounter; enum { SG_BCKGND_RECT_BEGIN, SG_BCKGND_RECT_TOP, SG_BCKGND_RECT_LEFT, SG_BCKGND_RECT_RIGHT, SG_BCKGND_RECT_BOTTOM, SG_BCKGND_RECT_END }; SDL_Color blackc[] = {{0, 0, 0, 0}}; SDL_Color darkgreyc[] = {{128, 128, 128, 0}}; SDL_Color greyc[] = {{192, 192, 192, 0}}; SDL_Color whitec[] = {{255, 255, 255, 0}}; enum { SG_FIRST_EDITFIELD, SG_PREVIOUS_EDITFIELD, SG_NEXT_EDITFIELD, SG_LAST_EDITFIELD }; /*-----------------------------------------------------------------------*/ /* Load an 1 plane XBM into a 8 planes SDL_Surface. */ static SDL_Surface *SDLGui_LoadXBM(int w, int h, Uint8 *srcbits) { SDL_Surface *bitmap; Uint8 *dstbits; int x, y, srcpitch; /* Allocate the bitmap */ bitmap = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0); if ( bitmap == NULL ) { // TODO panicbug("Couldn't allocate bitmap: %s", SDL_GetError()); return(NULL); } srcpitch = ((w + 7) / 8); dstbits = (Uint8 *)bitmap->pixels; int mask = 1; /* Copy the pixels */ for (y = 0 ; y < h ; y++) { for (x = 0 ; x < w ; x++) { dstbits[x] = (srcbits[x / 8] & mask) ? 1 : 0; mask <<= 1; mask |= (mask >> 8); mask &= 0xFF; } dstbits += bitmap->pitch; srcbits += srcpitch; } return(bitmap); } /*-----------------------------------------------------------------------*/ /* Initialize the GUI. */ bool SDLGui_Init(SDL_Surface *GUISurface) { mainsurface = GUISurface; /* Load the font graphics: */ fontgfx = SDLGui_LoadXBM(font8_width, font8_height, font8_bits); if (fontgfx == NULL) { // TODO panicbug("Could not create font data"); // TODO panicbug("ARAnyM GUI will not be available"); return false; } /* Set font color 0 as transparent */ SDL_SetColorKey(fontgfx, SDL_SRCCOLORKEY, 0); /* Get the font width and height: */ fontwidth = fontgfx->w/16; fontheight = fontgfx->h/16; return true; } /*-----------------------------------------------------------------------*/ /* Uninitialize the GUI. */ int SDLGui_UnInit() { if (fontgfx) { SDL_FreeSurface(fontgfx); fontgfx = NULL; } return 0; } /*-----------------------------------------------------------------------*/ /* Compute real coordinates for a given object. Note: centers dialog on screen. */ static void SDLGui_ObjCoord(SGOBJ *dlg, int objnum, SDL_Rect *rect) { rect->x = dlg[objnum].x * fontwidth; rect->y = dlg[objnum].y * fontheight; rect->w = dlg[objnum].w * fontwidth; rect->h = dlg[objnum].h * fontheight; rect->x += (sdlscrn->w - (dlg[0].w * fontwidth)) / 2; rect->y += (sdlscrn->h - (dlg[0].h * fontheight)) / 2; } /*-----------------------------------------------------------------------*/ /* Compute real coordinates for a given object. This one takes borders into account and give coordinates as seen by user */ void SDLGui_ObjFullCoord(SGOBJ *dlg, int objnum, SDL_Rect *coord) { SDLGui_ObjCoord(dlg, objnum, coord); switch (dlg[objnum].type) { case SGBOX: case SGBUTTON: { // Take border into account int border_size; if (dlg[objnum].flags & SG_SELECTABLE) { if (dlg[objnum].flags & SG_DEFAULT) border_size = 4; else border_size = 3; } else { if (dlg[objnum].flags & SG_BACKGROUND) border_size = 6; else border_size = 5; } coord->x -= border_size; coord->y -= border_size; coord->w += (border_size * 2); coord->h += (border_size * 2); } break; case SGEDITFIELD: // Allow one more pixel to the right for cursor coord->w += 1; // There is a line below coord->h += 1; break; } } /*-----------------------------------------------------------------------*/ /* Refresh display at given coordinates. Unlike SDL_UpdateRect() this function can eat coords that goes beyond screen boundaries. "rect" will be modified to represent the area actually refreshed. */ void SDLGui_UpdateRect(SDL_Rect *rect) { if (rect->x < 0) { rect->w += rect->x; rect->x = 0; } if ((rect->x + rect->w) > sdlscrn->w) rect->w = (sdlscrn->w - rect->x); if (rect->y < 0) { rect->h += rect->y; rect->y = 0; } if ((rect->y + rect->h) > sdlscrn->h) rect->h = (sdlscrn->h - rect->y); if ((rect->w > 0) && (rect->h > 0)) SDL_UpdateRects(sdlscrn, 1, rect); else { rect->x = 0; rect->y = 0; rect->w = 0; rect->h = 0; } } /*-----------------------------------------------------------------------*/ /* Maps an SDL_Color to the screen format. */ Uint32 SDLGui_MapColor(SDL_Color *color) { return SDL_MapRGB(sdlscrn->format, color->r, color->g, color->b); } /*-----------------------------------------------------------------------*/ /* Refresh display to reflect an object change. */ void SDLGui_RefreshObj(SGOBJ *dlg, int objnum) { SDL_Rect coord; SDLGui_ObjFullCoord(dlg, objnum, &coord); screenlock(); SDLGui_UpdateRect(&coord); screenunlock(); } /*-----------------------------------------------------------------------*/ /* Draw a text string. */ void SDLGui_Text(int x, int y, const char *txt, SDL_Color *col) { int i; char c; SDL_Rect sr, dr; SDL_SetColors(fontgfx, col, 1, 1); screenlock(); for (i = 0 ; txt[i] != 0 ; i++) { c = txt[i]; sr.x = fontwidth * (c % 16); sr.y = fontheight * (c / 16); sr.w = fontwidth; sr.h = fontheight; dr.x = x + (fontwidth * i); dr.y = y; dr.w = fontwidth; dr.h = fontheight; SDL_BlitSurface(fontgfx, &sr, sdlscrn, &dr); } screenunlock(); } /*-----------------------------------------------------------------------*/ /* Draw a dialog text object. */ void SDLGui_DrawText(SGOBJ *tdlg, int objnum) { SDL_Rect coord; SDL_Color *textc, *backgroundc; if (tdlg[objnum].state & SG_SELECTED) { textc = whitec; backgroundc = darkgreyc; } else if (tdlg[objnum].state & SG_DISABLED) { textc = darkgreyc; backgroundc = greyc; } else { textc = blackc; backgroundc = greyc; } SDLGui_ObjCoord(tdlg, objnum, &coord); SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(backgroundc)); SDLGui_Text(coord.x, coord.y, tdlg[objnum].txt, textc); } /*-----------------------------------------------------------------------*/ /* Draw an edit field object. */ void SDLGui_DrawEditField(SGOBJ *edlg, int objnum) { SDL_Rect coord; SDL_Color *textc; if (edlg[objnum].state & SG_DISABLED) textc = darkgreyc; else textc = blackc; SDLGui_ObjCoord(edlg, objnum, &coord); coord.w += 1; SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(greyc)); SDLGui_Text(coord.x, coord.y, edlg[objnum].txt, textc); // Draw a line below. coord.y = coord.y + coord.h; coord.h = 1; SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(darkgreyc)); } /*-----------------------------------------------------------------------*/ /* Draw or erase cursor. */ void SDLGui_DrawCursor(SGOBJ *dlg, cursor_state *cursor) { if (cursor->object != -1) { SDL_Rect coord; SDL_Color *cursorc; SDLGui_DrawEditField(dlg, cursor->object); if (cursor->blink_state) cursorc = blackc; else cursorc = greyc; SDLGui_ObjCoord(dlg, cursor->object, &coord); coord.x += (cursor->position * fontwidth); coord.w = 1; SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(cursorc)); SDLGui_RefreshObj(dlg, cursor->object); } } /*-----------------------------------------------------------------------*/ /* Draw a 3D effect around a given rectangle. Rectangle is updated to the full size of the new object. */ void SDLGui_Draw3DAround(SDL_Rect *coord, SDL_Color *upleftc, SDL_Color *downrightc, SDL_Color *cornerc, int width) { SDL_Rect rect; int i; Uint32 upleftcol = SDLGui_MapColor(upleftc); Uint32 downrightcol = SDLGui_MapColor(downrightc); Uint32 cornercol = SDLGui_MapColor(cornerc); screenlock(); for ( i = 1 ; i <= width ; i++) { rect.x = coord->x - i; rect.y = coord->y - i; rect.w = coord->w + (i * 2) - 1; rect.h = 1; SDL_FillRect(sdlscrn, &rect, upleftcol); rect.x = coord->x - i; rect.y = coord->y - i; rect.w = 1; rect.h = coord->h + (i * 2) - 1; SDL_FillRect(sdlscrn, &rect, upleftcol); rect.x = coord->x - i + 1; rect.y = coord->y + coord->h - 1 + i; rect.w = coord->w + (i * 2) - 1; rect.h = 1; SDL_FillRect(sdlscrn, &rect, downrightcol); rect.x = coord->x + coord->w - 1 + i; rect.y = coord->y - i + 1; rect.w = 1; rect.h = coord->h + (i * 2) - 1; SDL_FillRect(sdlscrn, &rect, downrightcol); rect.x = coord->x + coord->w + i - 1; rect.y = coord->y - i; rect.w = 1; rect.h = 1; SDL_FillRect(sdlscrn, &rect, cornercol); rect.x = coord->x - i; rect.y = coord->y + coord->h + i - 1; rect.w = 1; rect.h = 1; SDL_FillRect(sdlscrn, &rect, cornercol); } screenunlock(); coord->x -= width; coord->y -= width; coord->w += (width * 2); coord->h += (width * 2); } /*-----------------------------------------------------------------------*/ /* Draw a colored box around a given rectangle. Rectangle is updated to the full size of the new object. */ void SDLGui_DrawBoxAround(SDL_Rect *coord, SDL_Color *color, int width) { SDL_Rect rect; Uint32 col = SDLGui_MapColor(color); screenlock(); rect.x = coord->x - width; rect.y = coord->y - width; rect.w = coord->w + (width * 2); rect.h = width; SDL_FillRect(sdlscrn, &rect, col); rect.x = coord->x - width; rect.y = coord->y - width; rect.w = width; rect.h = coord->h + (width * 2); SDL_FillRect(sdlscrn, &rect, col); rect.x = coord->x + coord->w; rect.y = coord->y - width; rect.w = width; rect.h = coord->h + (width * 2); SDL_FillRect(sdlscrn, &rect, col); rect.x = coord->x - width; rect.y = coord->y + coord->h; rect.w = coord->w + (width * 2); rect.h = width; SDL_FillRect(sdlscrn, &rect, col); screenunlock(); coord->x -= width; coord->y -= width; coord->w += (width * 2); coord->h += (width * 2); } /*-----------------------------------------------------------------------*/ /* Draw a 3D box with given attributes. */ void SDLGui_Draw3DBox(SDL_Rect *coord, SDL_Color *backgroundc, SDL_Color *inboxc, SDL_Color *upleftc, SDL_Color *downrightc, SDL_Color *outboxc, int widthbackground, int widthinbox, int width3D1, int width3D2, int widthoutbox) { SDL_Rect rect; screenlock(); // Draw background rect.x = coord->x - widthbackground; rect.y = coord->y - widthbackground; rect.w = coord->w + (widthbackground * 2); rect.h = coord->h + (widthbackground * 2); SDL_FillRect(sdlscrn, &rect, SDLGui_MapColor(backgroundc)); screenunlock(); // Update coords coord->x -= widthbackground; coord->y -= widthbackground; coord->w += (widthbackground * 2); coord->h += (widthbackground * 2); if (widthinbox > 0) SDLGui_DrawBoxAround(coord, inboxc, widthinbox); if (width3D1 > 0) SDLGui_Draw3DAround(coord, upleftc, downrightc, backgroundc, width3D1); if (width3D2 > 0) SDLGui_Draw3DAround(coord, downrightc, upleftc, backgroundc, width3D2); if (widthoutbox > 0) SDLGui_DrawBoxAround(coord, outboxc, widthoutbox); } /*-----------------------------------------------------------------------*/ /* Draw a dialog box object. */ void SDLGui_DrawBox(SGOBJ *bdlg, int objnum) { SDL_Rect coord; SDL_Color *my_blackc; SDL_Color *upleftc, *downrightc; SDLGui_ObjCoord(bdlg, objnum, &coord); // Modify box drawing according to object state if (bdlg[objnum].state & SG_DISABLED) my_blackc = darkgreyc; else my_blackc = blackc; if (bdlg[objnum].state & SG_SELECTED) { upleftc = darkgreyc; downrightc = whitec; } else { upleftc = whitec; downrightc = darkgreyc; } // Draw box according to object flags switch (bdlg[objnum].flags & (SG_SELECTABLE | SG_DEFAULT | SG_BACKGROUND)) { case (SG_SELECTABLE | SG_DEFAULT | SG_BACKGROUND): case (SG_SELECTABLE | SG_DEFAULT): SDLGui_Draw3DBox(&coord, greyc, NULL, upleftc, downrightc, my_blackc, 1, 0, 1, 0, 2); break; case (SG_SELECTABLE | SG_BACKGROUND): case SG_SELECTABLE: SDLGui_Draw3DBox(&coord, greyc, NULL, upleftc, downrightc, my_blackc, 1, 0, 1, 0, 1); break; case (SG_DEFAULT | SG_BACKGROUND): case SG_BACKGROUND: SDLGui_Draw3DBox(&coord, greyc, my_blackc, upleftc, downrightc, darkgreyc, 0, 2, 3, 0, 1); break; case SG_DEFAULT: case 0: SDLGui_Draw3DBox(&coord, greyc, NULL, upleftc, downrightc, NULL, 3, 0, 1, 1, 0); break; } } /*-----------------------------------------------------------------------*/ /* Draw a normal button. */ void SDLGui_DrawButton(SGOBJ *bdlg, int objnum) { SDL_Rect coord; int x, y; SDL_Color *textc; SDLGui_ObjCoord(bdlg, objnum, &coord); x = coord.x + ((coord.w - (strlen(bdlg[objnum].txt) * fontwidth)) / 2); y = coord.y + ((coord.h - fontheight) / 2); if (bdlg[objnum].state & SG_SELECTED) { x += 1; y += 1; } if (bdlg[objnum].state & SG_DISABLED) textc = darkgreyc; else textc = blackc; SDLGui_DrawBox(bdlg, objnum); SDLGui_Text(x, y, bdlg[objnum].txt, textc); } /*-----------------------------------------------------------------------*/ /* Draw a dialog check box object state. */ void SDLGui_DrawCheckBoxState(SGOBJ *cdlg, int objnum) { Uint32 grey = SDLGui_MapColor(greyc); SDL_Rect coord; char str[2]; SDL_Color *textc; SDLGui_ObjCoord(cdlg, objnum, &coord); if (cdlg[objnum].flags & SG_RADIO) { if (cdlg[objnum].state & SG_SELECTED) str[0]=SGCHECKBOX_RADIO_SELECTED; else str[0]=SGCHECKBOX_RADIO_NORMAL; } else { if (cdlg[objnum].state & SG_SELECTED) str[0]=SGCHECKBOX_SELECTED; else str[0]=SGCHECKBOX_NORMAL; } if (cdlg[objnum].state & SG_DISABLED) textc = darkgreyc; else textc = blackc; str[1]='\0'; coord.w = fontwidth; coord.h = fontheight; if (cdlg[objnum].flags & SG_BUTTON_RIGHT) coord.x += ((strlen(cdlg[objnum].txt) + 1) * fontwidth); SDL_FillRect(sdlscrn, &coord, grey); SDLGui_Text(coord.x, coord.y, str, textc); } /*-----------------------------------------------------------------------*/ /* Draw a dialog check box object. */ void SDLGui_DrawCheckBox(SGOBJ *cdlg, int objnum) { SDL_Rect coord; SDL_Color *textc; SDLGui_ObjCoord(cdlg, objnum, &coord); if (!(cdlg[objnum].flags&SG_BUTTON_RIGHT)) coord.x += (fontwidth * 2); if (cdlg[objnum].state & SG_DISABLED) textc = darkgreyc; else textc = blackc; SDLGui_Text(coord.x, coord.y, cdlg[objnum].txt, textc); SDLGui_DrawCheckBoxState(cdlg, objnum); } /*-----------------------------------------------------------------------*/ /* Draw a dialog popup button object. */ void SDLGui_DrawPopupButton(SGOBJ *pdlg, int objnum) { SDL_Rect coord; const char *downstr = "\x02"; SDL_Color *textc; if (pdlg[objnum].state & SG_DISABLED) textc = darkgreyc; else textc = blackc; SDLGui_DrawBox(pdlg, objnum); SDLGui_ObjCoord(pdlg, objnum, &coord); SDLGui_Text(coord.x, coord.y, pdlg[objnum].txt, textc); SDLGui_Text(coord.x+coord.w-fontwidth, coord.y, downstr, textc); } /*-----------------------------------------------------------------------*/ /* Draw an object. */ void SDLGui_DrawObject(SGOBJ *dlg, int objnum) { switch (dlg[objnum].type) { case SGBOX: SDLGui_DrawBox(dlg, objnum); break; case SGTEXT: SDLGui_DrawText(dlg, objnum); break; case SGEDITFIELD: SDLGui_DrawEditField(dlg, objnum); break; case SGBUTTON: SDLGui_DrawButton(dlg, objnum); break; case SGCHECKBOX: SDLGui_DrawCheckBox(dlg, objnum); break; case SGPOPUP: SDLGui_DrawPopupButton(dlg, objnum); break; } } /*-----------------------------------------------------------------------*/ /* Draw a whole dialog. */ void SDLGui_DrawDialog(SGOBJ *dlg) { int i; // Store dialog coordinates SDLGui_ObjFullCoord(dlg, 0, &DialogRect); for (i = 0 ; dlg[i].type != -1 ; i++) { if (dlg[i].state & SG_HIDDEN) continue; SDLGui_DrawObject(dlg, i); } SDLGui_RefreshObj(dlg, 0); } /*-----------------------------------------------------------------------*/ /* Search default object in a dialog. */ int SDLGui_FindDefaultObj(SGOBJ *dlg) { int i = 0; while (dlg[i].type != -1) { if (dlg[i].flags & SG_DEFAULT) return i; i++; } return -1; } /*-----------------------------------------------------------------------*/ /* Search an object at given coordinates. */ int SDLGui_FindObj(SGOBJ *dlg, int fx, int fy) { SDL_Rect coord; int end, i; int ob = -1; // Search end object in dialog i = 0; while (dlg[i++].type != -1); end = i; // Now check each object for (i = end-1 ; i >= 0 ; i--) { SDLGui_ObjFullCoord(dlg, i, &coord); if(fx >= coord.x && fy >= coord.y && fx < (coord.x + coord.w) && fy < (coord.y + coord.h)) { if (dlg[i].state & (SG_HIDDEN | SG_DISABLED)) continue; ob = i; break; } } return ob; } /*-----------------------------------------------------------------------*/ /* A radio object has been selected. Let's deselect any other in his group. */ void SDLGui_SelectRadioObject(SGOBJ *dlg, int clicked_obj) { int obj; // Find first radio object in this group obj = clicked_obj; while (dlg[--obj].flags & SG_RADIO); // Update state while (dlg[++obj].flags & SG_RADIO) { // This code scan every object in the group. This allows to solve cases // where multiple objects where selected in the group by clicking one. if ((obj != clicked_obj) && (dlg[obj].state & SG_SELECTED)) { // Deselect this radio button dlg[obj].state &= ~SG_SELECTED; SDLGui_DrawObject(dlg, obj); SDLGui_RefreshObj(dlg, obj); } } } /*-----------------------------------------------------------------------*/ /* Update clicked object state depending on given mouse coordinates. Returns true if the mouse is over the object, false otherwise. */ bool SDLGui_UpdateObjState(SGOBJ *dlg, int clicked_obj, int original_state, int x, int y) { int obj; obj = SDLGui_FindObj(dlg, x, y); // Special case : user clicked on an already selected radio object // do not modify its state. // We handle it here because it allows to exit if the object is SG_EXIT or // SG_TOUCHEXIT without any additional test. if ((dlg[clicked_obj].flags & SG_RADIO) && (original_state & SG_SELECTED)) return (obj == clicked_obj); if (((obj != clicked_obj) && (dlg[clicked_obj].state != original_state)) || ((obj == clicked_obj) && (dlg[clicked_obj].state == original_state))) { if (dlg[clicked_obj].flags & SG_SELECTABLE) { dlg[clicked_obj].state ^= SG_SELECTED; SDLGui_DrawObject(dlg, clicked_obj); SDLGui_RefreshObj(dlg, clicked_obj); } } return (obj == clicked_obj); } /*-----------------------------------------------------------------------*/ /* Search edit field in a dialog. */ int SDLGui_FindEditField(SGOBJ *dlg, int objnum, int mode) { int i, j; switch (mode) { case SG_FIRST_EDITFIELD: i = 0; while (dlg[i].type != -1) { if ((dlg[i].type == SGEDITFIELD) && ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) return i; i++; } break; case SG_PREVIOUS_EDITFIELD: i = objnum - 1; while (i >= 0) { if ((dlg[i].type == SGEDITFIELD) && ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) return i; i--; } break; case SG_NEXT_EDITFIELD: i = objnum + 1; while (dlg[i].type != -1) { if ((dlg[i].type == SGEDITFIELD) && ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) return i; i++; } break; case SG_LAST_EDITFIELD: i = objnum + 1; j = -1; while (dlg[i].type != -1) { if ((dlg[i].type == SGEDITFIELD) && ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) j = i; i++; } if (j != -1) return j; break; } return objnum; } /*-----------------------------------------------------------------------*/ /* Move cursor to another edit field. */ void SDLGui_MoveCursor(SGOBJ *dlg, cursor_state *cursor, int mode) { int new_object; new_object = SDLGui_FindEditField(dlg, cursor->object, mode); if (new_object != cursor->object) { /* Erase old cursor */ cursor->blink_state = false; SDLGui_DrawCursor(dlg, cursor); cursor->object = new_object; cursor->position = strlen(dlg[new_object].txt); } else { /* We stay in the same field */ /* Move cursor to begin or end of text depending on mode */ switch (mode) { case SG_FIRST_EDITFIELD: case SG_PREVIOUS_EDITFIELD: cursor->position = 0; break; case SG_NEXT_EDITFIELD: case SG_LAST_EDITFIELD: cursor->position = strlen(dlg[new_object].txt); break; } } } /*-----------------------------------------------------------------------*/ /* Handle mouse clicks on edit fields. */ void SDLGui_ClickEditField(SGOBJ *dlg, cursor_state *cursor, int clicked_obj, int x) { SDL_Rect coord; int i, j; /* Erase old cursor */ cursor->blink_state = false; SDLGui_DrawCursor(dlg, cursor); SDLGui_ObjFullCoord(dlg, clicked_obj, &coord); i = (x - coord.x + (fontwidth / 2)) / fontwidth; j = strlen(dlg[clicked_obj].txt); cursor->object = clicked_obj; cursor->position = MIN(i, j); cursor->blink_state = true; cursor->blink_counter = 0; SDLGui_DrawCursor(dlg, cursor); } /*-----------------------------------------------------------------------*/ /* Handle mouse clicks. */ int SDLGui_MouseClick(SGOBJ *dlg, int fx, int fy, cursor_state *cursor) { int clicked_obj; int return_obj = -1; int original_state = 0; int x, y; clicked_obj = SDLGui_FindObj(dlg, fx, fy); if (clicked_obj >= 0) { original_state = dlg[clicked_obj].state; SDLGui_UpdateObjState(dlg, clicked_obj, original_state, fx, fy); if (dlg[clicked_obj].flags & SG_TOUCHEXIT) { return_obj = clicked_obj; clicked_obj = -1; } } while (clicked_obj >= 0) { SDL_Event evnt; // SDL_PumpEvents() - not necessary, the main check_event thread calls it if (SDL_PeepEvents(&evnt, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_USEREVENT))) { switch (evnt.user.code) { case SDL_USEREVENT: // a signal that resolution has changed // Restore clicked object original state dlg[clicked_obj].state = original_state; // re-draw dialog SDLGui_DrawDialog(dlg); // Exit from mouse click handling. clicked_obj = -1; break; case SDL_MOUSEBUTTONUP: x = reinterpret_cast(evnt.user.data1); y = reinterpret_cast(evnt.user.data2); if (SDLGui_UpdateObjState(dlg, clicked_obj, original_state, x, y)) { // true if mouse button is released over clicked object. // If applicable, the object has been selected by // SDLGui_UpdateObjState(). Let's do additional handling here. // Exit if object is an SG_EXIT one. if (dlg[clicked_obj].flags & SG_EXIT) return_obj = clicked_obj; // If it's a SG_RADIO object, deselect other objects in his group. if (dlg[clicked_obj].flags & SG_RADIO) SDLGui_SelectRadioObject(dlg, clicked_obj); if (dlg[clicked_obj].type == SGEDITFIELD) SDLGui_ClickEditField(dlg, cursor, clicked_obj, x); } // Exit from mouse click handling. clicked_obj = -1; break; } } else { // No special event occured. // Update object state according to mouse coordinates. SDL_GetMouseState(&x, &y); SDLGui_UpdateObjState(dlg, clicked_obj, original_state, x, y); // Wait a little to avoid eating CPU. SDL_Delay(100); } } return return_obj; } /*-----------------------------------------------------------------------*/ /* Handle key press. */ int SDLGui_KeyPress(SGOBJ *dlg, int keysym, int mod, cursor_state *cursor) { int return_obj = -1; int obj; if (cursor->object != -1) { switch(keysym) { case SDLK_RETURN: case SDLK_KP_ENTER: break; case SDLK_BACKSPACE: if (cursor->position > 0) { memmove(&dlg[cursor->object].txt[cursor->position-1], &dlg[cursor->object].txt[cursor->position], strlen(&dlg[cursor->object].txt[cursor->position])+1); cursor->position--; } break; case SDLK_DELETE: if(cursor->position < (int)strlen(dlg[cursor->object].txt)) { memmove(&dlg[cursor->object].txt[cursor->position], &dlg[cursor->object].txt[cursor->position+1], strlen(&dlg[cursor->object].txt[cursor->position+1])+1); } break; case SDLK_LEFT: if (cursor->position > 0) cursor->position--; break; case SDLK_RIGHT: if (cursor->position < (int)strlen(dlg[cursor->object].txt)) cursor->position++; break; case SDLK_DOWN: SDLGui_MoveCursor(dlg, cursor, SG_NEXT_EDITFIELD); break; case SDLK_UP: SDLGui_MoveCursor(dlg, cursor, SG_PREVIOUS_EDITFIELD); break; case SDLK_TAB: if (mod & KMOD_SHIFT) SDLGui_MoveCursor(dlg, cursor, SG_PREVIOUS_EDITFIELD); else SDLGui_MoveCursor(dlg, cursor, SG_NEXT_EDITFIELD); break; case SDLK_HOME: if (mod & KMOD_CTRL) SDLGui_MoveCursor(dlg, cursor, SG_FIRST_EDITFIELD); else cursor->position = 0; break; case SDLK_END: if (mod & KMOD_CTRL) SDLGui_MoveCursor(dlg, cursor, SG_LAST_EDITFIELD); else cursor->position = strlen(dlg[cursor->object].txt); break; default: if ((keysym >= SDLK_KP0) && (keysym <= SDLK_KP9)) { // map numpad numbers to normal numbers keysym -= (SDLK_KP0 - SDLK_0); } /* If it is a "good" key then insert it into the text field */ if ((keysym >= SDLK_SPACE) && (keysym < SDLK_KP0)) { if (strlen(dlg[cursor->object].txt) < dlg[cursor->object].w) { memmove(&dlg[cursor->object].txt[cursor->position+1], &dlg[cursor->object].txt[cursor->position], strlen(&dlg[cursor->object].txt[cursor->position])+1); if (mod & KMOD_SHIFT) dlg[cursor->object].txt[cursor->position] = toupper(keysym); else dlg[cursor->object].txt[cursor->position] = keysym; cursor->position += 1; } } break; } } switch(keysym) { case SDLK_RETURN: case SDLK_KP_ENTER: obj = SDLGui_FindDefaultObj(dlg); if (obj >= 0) { dlg[obj].state ^= SG_SELECTED; SDLGui_DrawObject(dlg, obj); SDLGui_RefreshObj(dlg, obj); if (dlg[obj].flags & (SG_EXIT | SG_TOUCHEXIT)) { return_obj = obj; SDL_Delay(300); } } break; } // Force cursor display. Should ease text input. cursor->blink_state = true; cursor->blink_counter = 0; // Redraw current edit field... SDLGui_DrawCursor(dlg, cursor); return return_obj; } /*-----------------------------------------------------------------------*/ /* Used to update screen while GUI is opened. Return a list of rectangles that covers the screen without overlaping the current dialog. */ SDL_Rect *SDLGui_GetFirstBackgroundRect(void) { // Reset counter... BackgroundRectCounter = SG_BCKGND_RECT_BEGIN; // And returns first rectangle return SDLGui_GetNextBackgroundRect(); } /*-----------------------------------------------------------------------*/ /* Returns next rectangle to be redrawn to update screen or NULL if we reached the end of the list. This code is "flying dialog" ready :) It will need some updating if we implement popup buttons handled by sdlgui, as the popup could be higher than the root box... I used some recursivity here to simplify the code. */ SDL_Rect *SDLGui_GetNextBackgroundRect(void) { SDL_Rect *return_rect = NULL; switch (BackgroundRectCounter) { case SG_BCKGND_RECT_END: // Nothing to do : return_rect is already initialized to NULL. break; case SG_BCKGND_RECT_BEGIN: if (DialogRect.w == 0) { // The dialog is not drawn yet... // Let's redraw the full screen. BackgroundRect.x = 0; BackgroundRect.y = 0; BackgroundRect.w = sdlscrn->w; BackgroundRect.h = sdlscrn->h; return_rect = &BackgroundRect; // We reached the end of the list. BackgroundRectCounter = SG_BCKGND_RECT_END; } else { BackgroundRectCounter = SG_BCKGND_RECT_TOP; return_rect = SDLGui_GetNextBackgroundRect(); } break; case SG_BCKGND_RECT_TOP: BackgroundRectCounter = SG_BCKGND_RECT_LEFT; if (DialogRect.y > 0) { BackgroundRect.x = 0; BackgroundRect.y = 0; BackgroundRect.w = sdlscrn->w; BackgroundRect.h = DialogRect.y; return_rect = &BackgroundRect; } else return_rect = SDLGui_GetNextBackgroundRect(); break; case SG_BCKGND_RECT_LEFT: BackgroundRectCounter = SG_BCKGND_RECT_RIGHT; if (DialogRect.x > 0) { BackgroundRect.x = 0; BackgroundRect.y = (DialogRect.y > 0) ? DialogRect.y : 0; BackgroundRect.w = DialogRect.x; BackgroundRect.h = ((DialogRect.y + DialogRect.h) < (int)sdlscrn->h) ? (DialogRect.h + DialogRect.y - BackgroundRect.y) : (sdlscrn->h - DialogRect.y); return_rect = &BackgroundRect; } else return_rect = SDLGui_GetNextBackgroundRect(); break; case SG_BCKGND_RECT_RIGHT: BackgroundRectCounter = SG_BCKGND_RECT_BOTTOM; if ((DialogRect.x + DialogRect.w) < (int)sdlscrn->w) { BackgroundRect.x = DialogRect.x + DialogRect.w; BackgroundRect.y = (DialogRect.y > 0) ? DialogRect.y : 0; BackgroundRect.w = sdlscrn->w - (DialogRect.x + DialogRect.w); BackgroundRect.h = ((DialogRect.y + DialogRect.h) < (int)sdlscrn->w) ? (DialogRect.h + DialogRect.y - BackgroundRect.y) : (sdlscrn->h - DialogRect.y); return_rect = &BackgroundRect; } else return_rect = SDLGui_GetNextBackgroundRect(); break; case SG_BCKGND_RECT_BOTTOM: BackgroundRectCounter = SG_BCKGND_RECT_END; if ((DialogRect.y + DialogRect.h) < (int)sdlscrn->h) { // Bottom BackgroundRect.x = 0; BackgroundRect.y = DialogRect.y + DialogRect.h; BackgroundRect.w = sdlscrn->w; BackgroundRect.h = sdlscrn->h - (DialogRect.y + DialogRect.h); return_rect = &BackgroundRect; } else return_rect = SDLGui_GetNextBackgroundRect(); break; } return return_rect; } SDL_Event getEvent(SGOBJ *dlg, cursor_state *cursor) { int i = 0; while(1) { SDL_Event evnt; // fprintf(stderr, "Debug Before Peep events\n"); if (SDL_PeepEvents(&evnt, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_USEREVENT))) { fprintf(stderr, "Debug Peep events %d\n",i++); SDL_Event e; switch(evnt.user.code) { case SDL_KEYDOWN: case SDL_KEYUP: e.type = evnt.user.code; e.key.keysym.sym = (SDLKey)reinterpret_cast(evnt.user.data1); e.key.keysym.mod = (SDLMod)reinterpret_cast(evnt.user.data2); return e; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: e.type = evnt.user.code; if (evnt.user.code == SDL_MOUSEBUTTONDOWN) fprintf(stderr, "Debug mouse down\n"); else fprintf(stderr, "Debug mouse down\n"); e.button.x = reinterpret_cast(evnt.user.data1); e.button.y = reinterpret_cast(evnt.user.data2); return e; case SDL_USEREVENT: // a signal that resolution has changed if (dlg != NULL) SDLGui_DrawDialog(dlg); // re-draw dialog break; } } else { // No special event occured. // Wait a little to avoid eating CPU. SDL_Delay(50); if (cursor != NULL) { cursor->blink_counter++; if (cursor->blink_counter >= 10) { cursor->blink_counter = 0; cursor->blink_state = !cursor->blink_state; if (dlg != NULL) SDLGui_DrawCursor(dlg, cursor); } } } } } /*-----------------------------------------------------------------------*/ /* Show and process a dialog. Returns the button number that has been pressed. Does NOT handle SDL_QUIT - you must handle it before you pass the input event to the SDL GUI. */ int SDLGui_DoDialog(SGOBJ *dlg) { int return_obj = -1; int obj; int x, y; // int keysym, mod; cursor_state cursor; // Is the left mouse button still pressed? Yes -> Handle TOUCHEXIT objects here bool stillPressed = (SDL_GetMouseState(&x, &y) & SDL_BUTTON(1)); obj = SDLGui_FindObj(dlg, x, y); if (stillPressed && (obj >= 0) && (dlg[obj].flags & SG_TOUCHEXIT)) { // Mouse button is pressed over a TOUCHEXIT Button // Toogle its state before drawing anything (it has been deselected before). dlg[obj].state ^= SG_SELECTED; return_obj = obj; } cursor.object = SDLGui_FindEditField(dlg, -1, SG_FIRST_EDITFIELD); cursor.position = (cursor.object != -1) ? strlen(dlg[cursor.object].txt) : 0; cursor.blink_counter = 0; cursor.blink_state = true; SDLGui_DrawDialog(dlg); /* The main loop */ while (return_obj < 0) { fprintf(stderr, "Debug SDL main loop\n"); SDL_Event evnt = getEvent(dlg, &cursor); fprintf(stderr, "Debug SDL main loop got event\n"); switch(evnt.type) { case SDL_KEYDOWN: return_obj = SDLGui_KeyPress(dlg, evnt.key.keysym.sym, evnt.key.keysym.mod, &cursor); break; case SDL_MOUSEBUTTONDOWN: return_obj = SDLGui_MouseClick(dlg, evnt.button.x, evnt.button.y, &cursor); break; } } fprintf(stderr, "Debug SDL main loop finished\n"); if (dlg[return_obj].type == SGBUTTON) { // Deselect button... // BUG: This should be caller responsibility dlg[return_obj].state ^= SG_SELECTED; } return return_obj; }