#include <stdio.h>
#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"
/*---------------------------------------------------------------------------*/
#define GUI_CLOCK 18
#define GUI_SPACE 20
+#define GUI_LINES 8
+
struct widget
{
int type;
const GLfloat *color1;
GLfloat scale;
+
+ int text_obj_w;
+ int text_obj_h;
+
+ enum trunc trunc;
};
/*---------------------------------------------------------------------------*/
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];
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);
}
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);
}
}
/*---------------------------------------------------------------------------*/
+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;
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. */
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();
}
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. */
/*---------------------------------------------------------------------------*/
+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))
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)
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;
}
/*---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------*/
+
/*
* Create a multi-line text box using a vertical array of labels.
* Parse the text for '\' characters and treat them as line-breaks.
{
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;
{
/* 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)
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;
}
}
{
/* 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;
{
if (id)
{
- config_push_ortho();
+ video_push_ortho();
{
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
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();
}
}
/*---------------------------------------------------------------------------*/
-/* 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. */