X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=share%2Fgui.c;h=a942126bbaffab07ce9de27bf986f32ba8ccbfe5;hb=2ee923469a4b60c92d799341aa35cb0f498d925a;hp=68a9b25afd19e0a824e5842c0d03bba8d642fb01;hpb=6090d148f456c8d0153655b14fdc95888ed993ce;p=neverball diff --git a/share/gui.c b/share/gui.c index 68a9b25..a942126 100644 --- a/share/gui.c +++ b/share/gui.c @@ -17,11 +17,15 @@ #include #include "config.h" +#include "video.h" #include "glext.h" #include "image.h" #include "vec3.h" -#include "text.h" #include "gui.h" +#include "common.h" + +#include "fs.h" +#include "fs_rwops.h" /*---------------------------------------------------------------------------*/ @@ -42,6 +46,8 @@ #define GUI_CLOCK 18 #define GUI_SPACE 20 +#define GUI_LINES 8 + struct widget { int type; @@ -63,6 +69,11 @@ struct widget const GLfloat *color1; GLfloat scale; + + int text_obj_w; + int text_obj_h; + + enum trunc trunc; }; /*---------------------------------------------------------------------------*/ @@ -82,6 +93,10 @@ static int active; static int radius; static TTF_Font *font[3] = { NULL, NULL, NULL }; +static void *fontdata; +static int fontdatalen; +static SDL_RWops *fontrwops; + static GLuint digit_text[3][11]; static GLuint digit_list[3][11]; static int digit_w[3][11]; @@ -177,10 +192,10 @@ static GLuint gui_rect(int x, int y, int w, int h, int f, int r) float Ya = y + h + ((f & GUI_NW) ? (s - r) : 0); float Yb = y + ((f & GUI_SW) ? (r - s) : 0); - glTexCoord2f((X - x) / w, 1 - (Ya - y) / h); + glTexCoord2f((X - x) / w, (Ya - y) / h); glVertex2f(X, Ya); - glTexCoord2f((X - x) / w, 1 - (Yb - y) / h); + glTexCoord2f((X - x) / w, (Yb - y) / h); glVertex2f(X, Yb); } @@ -196,10 +211,10 @@ static GLuint gui_rect(int x, int y, int w, int h, int f, int r) float Ya = y + h + ((f & GUI_NE) ? (c - r) : 0); float Yb = y + ((f & GUI_SE) ? (r - c) : 0); - glTexCoord2f((X - x) / w, 1 - (Ya - y) / h); + glTexCoord2f((X - x) / w, (Ya - y) / h); glVertex2f(X, Ya); - glTexCoord2f((X - x) / w, 1 - (Yb - y) / h); + glTexCoord2f((X - x) / w, (Yb - y) / h); glVertex2f(X, Yb); } } @@ -212,6 +227,23 @@ static GLuint gui_rect(int x, int y, int w, int h, int f, int r) /*---------------------------------------------------------------------------*/ +static const char *pick_font_path(void) +{ + const char *path; + + path = _(GUI_FACE); + + if (!fs_exists(path)) + { + fprintf(stderr, L_("Font '%s' doesn't exist, trying default font.\n"), + path); + + path = GUI_FACE; + } + + return path; +} + void gui_init(void) { const float *c0 = gui_yel; @@ -225,17 +257,43 @@ void gui_init(void) if (TTF_Init() == 0) { + const char *fontpath = pick_font_path(); + int s0 = s / 26; int s1 = s / 13; int s2 = s / 7; memset(widget, 0, sizeof (struct widget) * MAXWIDGET); - /* Load small, medium, and large typefaces. */ + /* Load the font. */ + + if ((fontdata = fs_load(fontpath, &fontdatalen))) + { + fontrwops = SDL_RWFromConstMem(fontdata, fontdatalen); + + /* Load small, medium, and large typefaces. */ + + font[GUI_SML] = TTF_OpenFontRW(fontrwops, 0, s0); + + SDL_RWseek(fontrwops, 0, SEEK_SET); + font[GUI_MED] = TTF_OpenFontRW(fontrwops, 0, s1); + + SDL_RWseek(fontrwops, 0, SEEK_SET); + font[GUI_LRG] = TTF_OpenFontRW(fontrwops, 0, s2); + + /* fontrwops remains open. */ + } + else + { + fontrwops = NULL; + + font[GUI_SML] = NULL; + font[GUI_MED] = NULL; + font[GUI_LRG] = NULL; + + fprintf(stderr, L_("Could not load font '%s'.\n"), fontpath); + } - font[GUI_SML] = TTF_OpenFont(config_data(GUI_FACE), s0); - font[GUI_MED] = TTF_OpenFont(config_data(GUI_FACE), s1); - font[GUI_LRG] = TTF_OpenFont(config_data(GUI_FACE), s2); radius = s / 60; /* Initialize digit glyphs and lists for counters and clocks. */ @@ -319,6 +377,9 @@ void gui_free(void) if (font[GUI_MED]) TTF_CloseFont(font[GUI_MED]); if (font[GUI_SML]) TTF_CloseFont(font[GUI_SML]); + if (fontrwops) SDL_RWclose(fontrwops); + if (fontdata) free(fontdata); + TTF_Quit(); } @@ -348,6 +409,10 @@ static int gui_widget(int pd, int type) widget[id].color0 = gui_wht; widget[id].color1 = gui_wht; widget[id].scale = 1.0f; + widget[id].trunc = TRUNC_NONE; + + widget[id].text_obj_w = 0; + widget[id].text_obj_h = 0; /* Insert the new widget into the parent's widget list. */ @@ -379,6 +444,104 @@ int gui_filler(int pd) { return gui_widget(pd, GUI_FILLER); } /*---------------------------------------------------------------------------*/ +struct size +{ + int w, h; +}; + + +static struct size gui_measure(const char *text, TTF_Font *font) +{ + struct size size = { 0, 0 }; + + if (font) + TTF_SizeUTF8(font, text, &size.w, &size.h); + + return size; +} + +static char *gui_trunc_head(const char *text, + const int maxwidth, + TTF_Font *font) +{ + int left, right, mid; + char *str = NULL; + + left = 0; + right = strlen(text); + + while (right - left > 1) + { + mid = (left + right) / 2; + + str = concat_string("...", text + mid, NULL); + + if (gui_measure(str, font).w <= maxwidth) + right = mid; + else + left = mid; + + free(str); + } + + return concat_string("...", text + right, NULL); +} + +static char *gui_trunc_tail(const char *text, + const int maxwidth, + TTF_Font *font) +{ + int left, right, mid; + char *str = NULL; + + left = 0; + right = strlen(text); + + while (right - left > 1) + { + mid = (left + right) / 2; + + str = malloc(mid + sizeof ("...")); + + memcpy(str, text, mid); + memcpy(str + mid, "...", sizeof ("...")); + + if (gui_measure(str, font).w <= maxwidth) + left = mid; + else + right = mid; + + free(str); + } + + str = malloc(left + sizeof ("...")); + + memcpy(str, text, left); + memcpy(str + left, "...", sizeof ("...")); + + return str; +} + +static char *gui_truncate(const char *text, + const int maxwidth, + TTF_Font *font, + enum trunc trunc) +{ + if (gui_measure(text, font).w <= maxwidth) + return strdup(text); + + switch (trunc) + { + case TRUNC_NONE: return strdup(text); break; + case TRUNC_HEAD: return gui_trunc_head(text, maxwidth, font); break; + case TRUNC_TAIL: return gui_trunc_tail(text, maxwidth, font); break; + } + + return NULL; +} + +/*---------------------------------------------------------------------------*/ + void gui_set_image(int id, const char *file) { if (glIsTexture(widget[id].text_img)) @@ -396,10 +559,19 @@ void gui_set_label(int id, const char *text) if (glIsList(widget[id].text_obj)) glDeleteLists(widget[id].text_obj, 1); + text = gui_truncate(text, widget[id].w - radius, + font[widget[id].size], + widget[id].trunc); + widget[id].text_img = make_image_from_font(NULL, NULL, &w, &h, text, font[widget[id].size]); widget[id].text_obj = gui_list(-w / 2, -h / 2, w, h, widget[id].color0, widget[id].color1); + + widget[id].text_obj_w = w; + widget[id].text_obj_h = h; + + free((void *) text); } void gui_set_count(int id, int value) @@ -415,33 +587,65 @@ void gui_set_clock(int id, int value) void gui_set_color(int id, const float *c0, const float *c1) { - widget[id].color0 = c0 ? c0 : gui_yel; - widget[id].color1 = c1 ? c1 : gui_red; + if (id) + { + c0 = c0 ? c0 : gui_yel; + c1 = c1 ? c1 : gui_red; + + if (widget[id].color0 != c0 || widget[id].color1 != c1) + { + widget[id].color0 = c0; + widget[id].color1 = c1; + + if (glIsList(widget[id].text_obj)) + { + int w, h; + + glDeleteLists(widget[id].text_obj, 1); + + w = widget[id].text_obj_w; + h = widget[id].text_obj_h; + + widget[id].text_obj = gui_list(-w / 2, -h / 2, w, h, + widget[id].color0, + widget[id].color1); + } + } + } } void gui_set_multi(int id, const char *text) { const char *p; - char s[8][MAXSTR]; - int i, j, jd; + char s[GUI_LINES][MAXSTR]; + int i, sc, lc, jd; size_t n = 0; + /* Count available labels. */ + + for (lc = 0, jd = widget[id].car; jd; lc++, jd = widget[jd].cdr); + /* Copy each delimited string to a line buffer. */ - for (p = text, j = 0; *p && j < 8; j++) + for (p = text, sc = 0; *p && sc < lc; sc++) { - strncpy(s[j], p, (n = strcspn(p, "\\"))); - s[j][n] = 0; + strncpy(s[sc], p, (n = strcspn(p, "\\"))); + s[sc][n] = 0; if (*(p += n) == '\\') p++; } /* Set the label value for each line. */ - for (i = j - 1, jd = widget[id].car; i >= 0 && jd; i--, jd = widget[jd].cdr) - gui_set_label(jd, s[i]); + for (i = lc - 1, jd = widget[id].car; i >= 0; i--, jd = widget[jd].cdr) + gui_set_label(jd, i < sc ? s[i] : ""); +} + +void gui_set_trunc(int id, enum trunc trunc) +{ + widget[id].trunc = trunc; } /*---------------------------------------------------------------------------*/ @@ -554,6 +758,7 @@ int gui_space(int pd) } /*---------------------------------------------------------------------------*/ + /* * Create a multi-line text box using a vertical array of labels. * Parse the text for '\' characters and treat them as line-breaks. @@ -569,15 +774,15 @@ int gui_multi(int pd, const char *text, int size, int rect, const float *c0, { const char *p; - char s[8][MAXSTR]; - int r[8]; + char s[GUI_LINES][MAXSTR]; + int r[GUI_LINES]; int i, j; size_t n = 0; /* Copy each delimited string to a line buffer. */ - for (p = text, j = 0; *p && j < 8; j++) + for (p = text, j = 0; *p && j < GUI_LINES; j++) { strncpy(s[j], p, (n = strcspn(p, "\\"))); s[j][n] = 0; @@ -696,19 +901,25 @@ static void gui_button_up(int id) { /* Store width and height for later use in text rendering. */ - widget[id].x = widget[id].w; - widget[id].y = widget[id].h; + widget[id].text_obj_w = widget[id].w; + widget[id].text_obj_h = widget[id].h; if (widget[id].w < widget[id].h && widget[id].w > 0) widget[id].w = widget[id].h; - /* Padded text elements look a little nicer. */ if (widget[id].w < config_get_d(CONFIG_WIDTH)) widget[id].w += radius; if (widget[id].h < config_get_d(CONFIG_HEIGHT)) widget[id].h += radius; + + /* A button should be at least wide enough to accomodate the rounding. */ + + if (widget[id].w < 2 * radius) + widget[id].w = 2 * radius; + if (widget[id].h < 2 * radius) + widget[id].h = 2 * radius; } static void gui_widget_up(int id) @@ -720,6 +931,7 @@ static void gui_widget_up(int id) case GUI_VARRAY: gui_varray_up(id); break; case GUI_HSTACK: gui_hstack_up(id); break; case GUI_VSTACK: gui_vstack_up(id); break; + case GUI_FILLER: break; default: gui_button_up(id); break; } } @@ -859,8 +1071,8 @@ static void gui_button_dn(int id, int x, int y, int w, int h) { /* Recall stored width and height for text rendering. */ - int W = widget[id].x; - int H = widget[id].y; + int W = widget[id].text_obj_w; + int H = widget[id].text_obj_h; int R = widget[id].rect; const float *c0 = widget[id].color0; @@ -1258,7 +1470,7 @@ void gui_paint(int id) { if (id) { - config_push_ortho(); + video_push_ortho(); { glEnable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); @@ -1269,12 +1481,14 @@ void gui_paint(int id) glEnable(GL_TEXTURE_2D); gui_paint_text(id); + + glColor4fv(gui_wht); } glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); } - config_pop_matrix(); + video_pop_matrix(); } } @@ -1609,38 +1823,25 @@ static int gui_wrap_D(int id, int dd) /*---------------------------------------------------------------------------*/ -/* Flag the axes to prevent uncontrolled scrolling. */ - -static int xflag = 1; -static int yflag = 1; - -void gui_stuck() -{ - /* Force the user to recenter the joystick before the next GUI action. */ - - xflag = 0; - yflag = 0; -} - -int gui_stick(int id, int x, int y) +int gui_stick(int id, int a, float v, int bump) { int jd = 0; + if (!bump) + return 0; + /* Find a new active widget in the direction of joystick motion. */ - if (x && -JOY_MID <= x && x <= +JOY_MID) - xflag = 1; - else if (x < -JOY_MID && xflag && (jd = gui_wrap_L(id, active))) - xflag = 0; - else if (x > +JOY_MID && xflag && (jd = gui_wrap_R(id, active))) - xflag = 0; - - if (y && -JOY_MID <= y && y <= +JOY_MID) - yflag = 1; - else if (y < -JOY_MID && yflag && (jd = gui_wrap_U(id, active))) - yflag = 0; - else if (y > +JOY_MID && yflag && (jd = gui_wrap_D(id, active))) - yflag = 0; + if (config_tst_d(CONFIG_JOYSTICK_AXIS_X, a)) + { + if (v < 0) jd = gui_wrap_L(id, active); + if (v > 0) jd = gui_wrap_R(id, active); + } + else if (config_tst_d(CONFIG_JOYSTICK_AXIS_Y, a)) + { + if (v < 0) jd = gui_wrap_U(id, active); + if (v > 0) jd = gui_wrap_D(id, active); + } /* If the active widget has changed, return the new active id. */