#include <stdio.h>
#include "config.h"
+#include "video.h"
#include "glext.h"
#include "image.h"
#include "vec3.h"
#include "gui.h"
+#include "common.h"
+
+#include "fs.h"
+#include "fs_rwops.h"
+
+/*---------------------------------------------------------------------------*/
+
+/* Very pure colors for the GUI. I was watching BANZAI when I designed this. */
+
+const GLubyte gui_wht[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; /* White */
+const GLubyte gui_yel[4] = { 0xFF, 0xFF, 0x00, 0xFF }; /* Yellow */
+const GLubyte gui_red[4] = { 0xFF, 0x00, 0x00, 0xFF }; /* Red */
+const GLubyte gui_grn[4] = { 0x00, 0xFF, 0x00, 0xFF }; /* Green */
+const GLubyte gui_blu[4] = { 0x00, 0x00, 0xFF, 0xFF }; /* Blue */
+const GLubyte gui_blk[4] = { 0x00, 0x00, 0x00, 0xFF }; /* Black */
+const GLubyte gui_gry[4] = { 0x55, 0x55, 0x55, 0xFF }; /* Gray */
+const GLubyte gui_shd[4] = { 0x00, 0x00, 0x00, 0x80 }; /* Shadow */
/*---------------------------------------------------------------------------*/
-#define MAXWIDGET 256
+#define WIDGET_MAX 256
-#define GUI_TYPE 0xFFFE
+#define GUI_TYPE 0xFFFC
-#define GUI_FREE 0
-#define GUI_STATE 1
-#define GUI_HARRAY 2
-#define GUI_VARRAY 4
-#define GUI_HSTACK 6
-#define GUI_VSTACK 8
-#define GUI_FILLER 10
-#define GUI_IMAGE 12
-#define GUI_LABEL 14
-#define GUI_COUNT 16
-#define GUI_CLOCK 18
-#define GUI_SPACE 20
-#define GUI_PAUSE 22
+#define GUI_FREE 0
+
+#define GUI_STATE 1
+#define GUI_FILL 2
+
+#define GUI_FLAGS 2
+
+#define GUI_HARRAY (1 << GUI_FLAGS)
+#define GUI_VARRAY (2 << GUI_FLAGS)
+#define GUI_HSTACK (3 << GUI_FLAGS)
+#define GUI_VSTACK (4 << GUI_FLAGS)
+#define GUI_FILLER (5 << GUI_FLAGS)
+#define GUI_IMAGE (6 << GUI_FLAGS)
+#define GUI_LABEL (7 << GUI_FLAGS)
+#define GUI_COUNT (8 << GUI_FLAGS)
+#define GUI_CLOCK (9 << GUI_FLAGS)
+#define GUI_SPACE (10 << GUI_FLAGS)
+
+#define GUI_LINES 8
+
+/*---------------------------------------------------------------------------*/
struct widget
{
int size;
int rect;
+ const GLubyte *color0;
+ const GLubyte *color1;
+
int x, y;
int w, h;
int car;
int cdr;
- GLuint text_img;
- GLuint text_obj;
- GLuint rect_obj;
+ GLuint image;
+ GLfloat scale;
- const GLfloat *color0;
- const GLfloat *color1;
+ int text_w;
+ int text_h;
- GLfloat scale;
+ enum trunc trunc;
};
/*---------------------------------------------------------------------------*/
-const GLfloat gui_wht[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
-const GLfloat gui_yel[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
-const GLfloat gui_red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
-const GLfloat gui_grn[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
-const GLfloat gui_blu[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
-const GLfloat gui_blk[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
-const GLfloat gui_gry[4] = { 0.3f, 0.3f, 0.3f, 1.0f };
+/* GUI widget state */
-/*---------------------------------------------------------------------------*/
-
-static struct widget widget[MAXWIDGET];
+static struct widget widget[WIDGET_MAX];
static int active;
static int radius;
static TTF_Font *font[3] = { NULL, NULL, NULL };
-static int scale[3] = { 1, 1, 1 };
-static GLuint digit_text[3][11];
-static GLuint digit_list[3][11];
-static int digit_w[3][11];
-static int digit_h[3][11];
+/* Digit widgets for the HUD. */
+
+static int digit_id[3][11];
-static int pause_id;
+/* Font data access. */
+
+static void *fontdata;
+static int fontdatalen;
+static SDL_RWops *fontrwops;
/*---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------*/
-/*
- * Initialize a display list containing a rectangle (x, y, w, h) to
- * which a rendered-font texture may be applied. Colors c0 and c1
- * determine the top-to-bottom color gradiant of the text.
- */
-static GLuint gui_list(int x, int y,
- int w, int h, const float *c0, const float *c1)
+/* Vertex buffer definitions for widget rendering. */
+
+#define RECT_LEN 36
+#define TEXT_LEN 8
+#define WIDGET_LEN (RECT_LEN + TEXT_LEN)
+
+struct vert
{
- GLuint list = glGenLists(1);
+ GLubyte c[4];
+ GLfloat u[2];
+ GLshort p[2];
+};
- GLfloat s0, t0;
- GLfloat s1, t1;
+static struct vert vert_buf[WIDGET_MAX * WIDGET_LEN];
+static GLuint vert_obj = 0;
- int W, H, d = h / 16;
+/*---------------------------------------------------------------------------*/
- /* Assume the applied texture size is rect size rounded to power-of-two. */
+static void set_vert(struct vert *v, int x, int y,
+ GLfloat s, GLfloat t, const GLubyte *c)
+{
+ v->c[0] = c[0];
+ v->c[1] = c[1];
+ v->c[2] = c[2];
+ v->c[3] = c[3];
+ v->u[0] = s;
+ v->u[1] = t;
+ v->p[0] = x;
+ v->p[1] = y;
+}
- image_size(&W, &H, w, h);
+/*---------------------------------------------------------------------------*/
- s0 = 0.5f * (W - w) / W;
- t0 = 0.5f * (H - h) / H;
- s1 = 1.0f - s0;
- t1 = 1.0f - t0;
+static void draw_enable(GLboolean c, GLboolean u, GLboolean p)
+{
+ glBindBuffer_(GL_ARRAY_BUFFER, vert_obj);
- glNewList(list, GL_COMPILE);
+ if (c)
{
- glBegin(GL_QUADS);
- {
- glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
- glTexCoord2f(s0, t1); glVertex2i(x + d, y - d);
- glTexCoord2f(s1, t1); glVertex2i(x + w + d, y - d);
- glTexCoord2f(s1, t0); glVertex2i(x + w + d, y + h - d);
- glTexCoord2f(s0, t0); glVertex2i(x + d, y + h - d);
-
- glColor4fv(c0);
- glTexCoord2f(s0, t1); glVertex2i(x, y);
- glTexCoord2f(s1, t1); glVertex2i(x + w, y);
-
- glColor4fv(c1);
- glTexCoord2f(s1, t0); glVertex2i(x + w, y + h);
- glTexCoord2f(s0, t0); glVertex2i(x, y + h);
- }
- glEnd();
+ glEnableClientState(GL_COLOR_ARRAY);
+ glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (struct vert),
+ (GLvoid *) offsetof (struct vert, c));
}
- glEndList();
+ if (u)
+ {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, sizeof (struct vert),
+ (GLvoid *) offsetof (struct vert, u));
+ }
+ if (p)
+ {
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer (2, GL_SHORT, sizeof (struct vert),
+ (GLvoid *) offsetof (struct vert, p));
+ }
+}
- return list;
+static void draw_rect(int id)
+{
+ glDrawArrays(GL_TRIANGLE_STRIP, id * WIDGET_LEN, RECT_LEN);
}
-/*
- * Initialize a display list containing a rounded-corner rectangle (x,
- * y, w, h). Generate texture coordinates to properly apply a texture
- * map to the rectangle as though the corners were not rounded.
- */
+static void draw_text(int id)
+{
+ glDrawArrays(GL_TRIANGLE_STRIP, id * WIDGET_LEN + RECT_LEN, TEXT_LEN);
+}
+
+static void draw_disable(void)
+{
+ glBindBuffer_(GL_ARRAY_BUFFER, 0);
-static GLuint gui_rect(int x, int y, int w, int h, int f, int r)
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void gui_rect(int id, int x, int y, int w, int h, int f, int r)
{
- GLuint list = glGenLists(1);
+ struct vert *v = vert_buf + id * WIDGET_LEN;
+ struct vert *p = v;
+
+ /* Generate vertex data for the widget's rounded rectangle. */
int n = 8;
int i;
- glNewList(list, GL_COMPILE);
+ /* Left side... */
+
+ for (i = 0; i <= n; i++)
{
- glBegin(GL_QUAD_STRIP);
- {
- /* Left side... */
+ float a = 0.5f * V_PI * (float) i / (float) n;
+ float s = r * fsinf(a);
+ float c = r * fcosf(a);
- for (i = 0; i <= n; i++)
- {
- float a = 0.5f * V_PI * (float) i / (float) n;
- float s = r * fsinf(a);
- float c = r * fcosf(a);
+ float X = x + r - c;
+ float Ya = y + h + ((f & GUI_NW) ? (s - r) : 0);
+ float Yb = y + ((f & GUI_SW) ? (r - s) : 0);
- float X = x + r - c;
- float Ya = y + h + ((f & GUI_NW) ? (s - r) : 0);
- float Yb = y + ((f & GUI_SW) ? (r - s) : 0);
+ set_vert(p++, X, Ya, (X - x) / w, (Ya - y) / h, gui_wht);
+ set_vert(p++, X, Yb, (X - x) / w, (Yb - y) / h, gui_wht);
+ }
- glTexCoord2f((X - x) / w, 1 - (Ya - y) / h);
- glVertex2f(X, Ya);
+ /* Right side... */
- glTexCoord2f((X - x) / w, 1 - (Yb - y) / h);
- glVertex2f(X, Yb);
- }
+ for (i = 0; i <= n; i++)
+ {
+ float a = 0.5f * V_PI * (float) i / (float) n;
+ float s = r * fsinf(a);
+ float c = r * fcosf(a);
- /* ... Right side. */
+ float X = x + w - r + s;
+ float Ya = y + h + ((f & GUI_NE) ? (c - r) : 0);
+ float Yb = y + ((f & GUI_SE) ? (r - c) : 0);
- for (i = 0; i <= n; i++)
- {
- float a = 0.5f * V_PI * (float) i / (float) n;
- float s = r * fsinf(a);
- float c = r * fcosf(a);
+ set_vert(p++, X, Ya, (X - x) / w, (Ya - y) / h, gui_wht);
+ set_vert(p++, X, Yb, (X - x) / w, (Yb - y) / h, gui_wht);
+ }
- float X = x + w - r + s;
- float Ya = y + h + ((f & GUI_NE) ? (c - r) : 0);
- float Yb = y + ((f & GUI_SE) ? (r - c) : 0);
+ /* Copy this off to the VBO. */
- glTexCoord2f((X - x) / w, 1 - (Ya - y) / h);
- glVertex2f(X, Ya);
+ glBindBuffer_ (GL_ARRAY_BUFFER, vert_obj);
+ glBufferSubData_(GL_ARRAY_BUFFER,
+ id * WIDGET_LEN * sizeof (struct vert),
+ RECT_LEN * sizeof (struct vert), v);
+}
+
+static void gui_text(int id, int x, int y,
+ int w, int h, const GLubyte *c0, const GLubyte *c1)
+{
+ struct vert *v = vert_buf + id * WIDGET_LEN + RECT_LEN;
+
+ /* Assume the applied texture size is rect size rounded to power-of-two. */
+
+ int W;
+ int H;
+
+ image_size(&W, &H, w, h);
+
+ if (w > 0 && h > 0 && W > 0 && H > 0)
+ {
+ const int d = h / 16; /* Shadow offset */
+
+ const int ww = ((W - w) % 2) ? w + 1 : w;
+ const int hh = ((H - h) % 2) ? h + 1 : h;
+
+ const GLfloat s0 = 0.5f * (W - ww) / W;
+ const GLfloat t0 = 0.5f * (H - hh) / H;
+ const GLfloat s1 = 1.0f - s0;
+ const GLfloat t1 = 1.0f - t0;
+
+ /* Generate vertex data for the colored text and its shadow. */
+
+ set_vert(v + 0, x + d, y + hh - d, s0, t0, gui_shd);
+ set_vert(v + 1, x + d, y - d, s0, t1, gui_shd);
+ set_vert(v + 2, x + ww + d, y + hh - d, s1, t0, gui_shd);
+ set_vert(v + 3, x + ww + d, y - d, s1, t1, gui_shd);
+
+ set_vert(v + 4, x, y + hh, s0, t0, c1);
+ set_vert(v + 5, x, y, s0, t1, c0);
+ set_vert(v + 6, x + ww, y + hh, s1, t0, c1);
+ set_vert(v + 7, x + ww, y, s1, t1, c0);
- glTexCoord2f((X - x) / w, 1 - (Yb - y) / h);
- glVertex2f(X, Yb);
- }
- }
- glEnd();
}
- glEndList();
+ else memset(v, 0, TEXT_LEN * sizeof (struct vert));
- return list;
+ /* Copy this off to the VBO. */
+
+ glBindBuffer_ (GL_ARRAY_BUFFER, vert_obj);
+ glBufferSubData_(GL_ARRAY_BUFFER,
+ (id * WIDGET_LEN + RECT_LEN) * sizeof (struct vert),
+ TEXT_LEN * sizeof (struct vert), v);
}
/*---------------------------------------------------------------------------*/
-void gui_init(void)
+static const char *pick_font_path(void)
{
- const float *c0 = gui_yel;
- const float *c1 = gui_red;
+ 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)
+{
int w = config_get_d(CONFIG_WIDTH);
int h = config_get_d(CONFIG_HEIGHT);
- int i, j, s = (h < w) ? h : w;
+ int s = (h < w) ? h : w;
+ int i, j;
/* Initialize font rendering. */
if (TTF_Init() == 0)
{
+ const char *fontpath = pick_font_path();
+
int s0 = s / 26;
int s1 = s / 13;
int s2 = s / 7;
- int m;
- /* Make sure text size doesn't exceed the maximum texture size. */
+ memset(widget, 0, sizeof (struct widget) * WIDGET_MAX);
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m);
+ /* Load the font. */
- scale[0] = 1;
- scale[1] = 1;
- scale[2] = 1;
+ if ((fontdata = fs_load(fontpath, &fontdatalen)))
+ {
+ fontrwops = SDL_RWFromConstMem(fontdata, fontdatalen);
- while (s0 > m) { s0 /= 2; scale[0] *= 2; }
- while (s1 > m) { s1 /= 2; scale[1] *= 2; }
- while (s2 > m) { s2 /= 2; scale[2] *= 2; }
+ /* Load small, medium, and large typefaces. */
- memset(widget, 0, sizeof (struct widget) * MAXWIDGET);
+ font[GUI_SML] = TTF_OpenFontRW(fontrwops, 0, s0);
- /* Load small, medium, and large typefaces. */
+ SDL_RWseek(fontrwops, 0, SEEK_SET);
+ font[GUI_MED] = TTF_OpenFontRW(fontrwops, 0, s1);
- 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;
+ SDL_RWseek(fontrwops, 0, SEEK_SET);
+ font[GUI_LRG] = TTF_OpenFontRW(fontrwops, 0, s2);
- /* Initialize the global pause GUI. */
+ /* fontrwops remains open. */
+ }
+ else
+ {
+ fontrwops = NULL;
- if ((pause_id = gui_pause(0)))
- gui_layout(pause_id, 0, 0);
+ font[GUI_SML] = NULL;
+ font[GUI_MED] = NULL;
+ font[GUI_LRG] = NULL;
- /* Initialize digit glyphs and lists for counters and clocks. */
+ fprintf(stderr, L_("Could not load font '%s'.\n"), fontpath);
+ }
- for (i = 0; i < 3; i++)
- {
- char text[2];
+ radius = s / 60;
+ }
- /* Draw digits 0 throught 9. */
+ /* Initialize the VBOs. */
- for (j = 0; j < 10; j++)
- {
- text[0] = '0' + (char) j;
- text[1] = 0;
-
- digit_text[i][j] = make_image_from_font(NULL, NULL,
- &digit_w[i][j],
- &digit_h[i][j],
- text, font[i], scale[i]);
- digit_list[i][j] = gui_list(-digit_w[i][j] / 2,
- -digit_h[i][j] / 2,
- +digit_w[i][j],
- +digit_h[i][j], c0, c1);
- }
+ memset(vert_buf, 0, sizeof (vert_buf));
- /* Draw the colon for the clock. */
+ glGenBuffers_(1, &vert_obj);
+ glBindBuffer_(GL_ARRAY_BUFFER, vert_obj);
+ glBufferData_(GL_ARRAY_BUFFER, sizeof (vert_buf), vert_buf, GL_STATIC_DRAW);
+ glBindBuffer_(GL_ARRAY_BUFFER, 0);
- digit_text[i][j] = make_image_from_font(NULL, NULL,
- &digit_w[i][10],
- &digit_h[i][10],
- ":", font[i], scale[i]);
- digit_list[i][j] = gui_list(-digit_w[i][10] / 2,
- -digit_h[i][10] / 2,
- +digit_w[i][10],
- +digit_h[i][10], c0, c1);
- }
+ /* Cache digit glyphs for HUD rendering. */
+
+ for (i = 0; i < 3; i++)
+ {
+ digit_id[i][ 0] = gui_label(0, "0", i, 0, 0, 0);
+ digit_id[i][ 1] = gui_label(0, "1", i, 0, 0, 0);
+ digit_id[i][ 2] = gui_label(0, "2", i, 0, 0, 0);
+ digit_id[i][ 3] = gui_label(0, "3", i, 0, 0, 0);
+ digit_id[i][ 4] = gui_label(0, "4", i, 0, 0, 0);
+ digit_id[i][ 5] = gui_label(0, "5", i, 0, 0, 0);
+ digit_id[i][ 6] = gui_label(0, "6", i, 0, 0, 0);
+ digit_id[i][ 7] = gui_label(0, "7", i, 0, 0, 0);
+ digit_id[i][ 8] = gui_label(0, "8", i, 0, 0, 0);
+ digit_id[i][ 9] = gui_label(0, "9", i, 0, 0, 0);
+ digit_id[i][10] = gui_label(0, ":", i, 0, 0, 0);
}
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 11; ++j)
+ gui_layout(digit_id[i][j], 0, 0);
+
active = 0;
}
void gui_free(void)
{
- int i, j, id;
+ int id;
- /* Release any remaining widget texture and display list indices. */
+ /* Release the VBOs. */
- for (id = 1; id < MAXWIDGET; id++)
- {
- if (glIsTexture(widget[id].text_img))
- glDeleteTextures(1, &widget[id].text_img);
-
- if (glIsList(widget[id].text_obj))
- glDeleteLists(widget[id].text_obj, 1);
- if (glIsList(widget[id].rect_obj))
- glDeleteLists(widget[id].rect_obj, 1);
-
- widget[id].type = GUI_FREE;
- widget[id].text_img = 0;
- widget[id].text_obj = 0;
- widget[id].rect_obj = 0;
- widget[id].cdr = 0;
- widget[id].car = 0;
- }
+ if (glIsBuffer_(vert_obj))
+ glDeleteBuffers_(1, &vert_obj);
- /* Release all digit textures and display lists. */
+ /* Release any remaining widget texture and display list indices. */
- for (i = 0; i < 3; i++)
- for (j = 0; j < 11; j++)
- {
- if (glIsTexture(digit_text[i][j]))
- glDeleteTextures(1, &digit_text[i][j]);
+ for (id = 1; id < WIDGET_MAX; id++)
+ {
+ if (glIsTexture(widget[id].image))
+ glDeleteTextures(1, &widget[id].image);
- if (glIsList(digit_list[i][j]))
- glDeleteLists(digit_list[i][j], 1);
- }
+ widget[id].type = GUI_FREE;
+ widget[id].image = 0;
+ widget[id].cdr = 0;
+ widget[id].car = 0;
+ }
/* Release all loaded fonts and finalize font rendering. */
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();
}
/* Find an unused entry in the widget table. */
- for (id = 1; id < MAXWIDGET; id++)
+ for (id = 1; id < WIDGET_MAX; id++)
if (widget[id].type == GUI_FREE)
{
/* Set the type and default properties. */
- widget[id].type = type;
- widget[id].token = 0;
- widget[id].value = 0;
- widget[id].size = 0;
- widget[id].rect = GUI_NW | GUI_SW | GUI_NE | GUI_SE;
- widget[id].w = 0;
- widget[id].h = 0;
- widget[id].text_img = 0;
- widget[id].text_obj = 0;
- widget[id].rect_obj = 0;
- widget[id].color0 = gui_wht;
- widget[id].color1 = gui_wht;
- widget[id].scale = 1.0f;
-
- /* Insert the new widget into the parents's widget list. */
+ widget[id].type = type;
+ widget[id].token = 0;
+ widget[id].value = 0;
+ widget[id].size = 0;
+ widget[id].rect = GUI_NW | GUI_SW | GUI_NE | GUI_SE;
+ widget[id].w = 0;
+ widget[id].h = 0;
+ widget[id].image = 0;
+ widget[id].color0 = gui_wht;
+ widget[id].color1 = gui_wht;
+ widget[id].scale = 1.0f;
+ widget[id].trunc = TRUNC_NONE;
+ widget[id].text_w = 0;
+ widget[id].text_h = 0;
+
+ /* Insert the new widget into the parent's widget list. */
if (pd)
{
return id;
}
- fprintf(stderr, _("Out of widget IDs\n"));
+ fprintf(stderr, "Out of widget IDs\n");
return 0;
}
/*---------------------------------------------------------------------------*/
+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))
- glDeleteTextures(1, &widget[id].text_img);
+ if (glIsTexture(widget[id].image))
+ glDeleteTextures(1, &widget[id].image);
- widget[id].text_img = make_image_from_file(NULL, NULL, NULL, NULL, file);
+ widget[id].image = make_image_from_file(file);
}
void gui_set_label(int id, const char *text)
{
- int w, h;
+ int w = 0;
+ int h = 0;
+
+ if (glIsTexture(widget[id].image))
+ glDeleteTextures(1, &widget[id].image);
+
+ text = gui_truncate(text, widget[id].w - radius,
+ font[widget[id].size],
+ widget[id].trunc);
- if (glIsTexture(widget[id].text_img))
- glDeleteTextures(1, &widget[id].text_img);
- if (glIsList(widget[id].text_obj))
- glDeleteLists(widget[id].text_obj, 1);
+ widget[id].image = make_image_from_font(NULL, NULL, &w, &h,
+ text, font[widget[id].size]);
+ widget[id].text_w = w;
+ widget[id].text_h = h;
- widget[id].text_img = make_image_from_font(NULL, NULL, &w, &h,
- text, font[widget[id].size],
- scale[widget[id].size]);
- widget[id].text_obj = gui_list(-w / 2, -h / 2, w, h,
- widget[id].color0, widget[id].color1);
+ gui_text(id, -w / 2, -h / 2, w, h, widget[id].color0, widget[id].color1);
+
+ free((void *) text); /* Really? */
}
void gui_set_count(int id, int value)
widget[id].value = value;
}
-void gui_set_color(int id, const float *c0,
- const float *c1)
+void gui_set_color(int id, const GLubyte *c0,
+ const GLubyte *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)
+ {
+ int w = widget[id].text_w;
+ int h = widget[id].text_h;
+
+ widget[id].color0 = c0;
+ widget[id].color1 = c1;
+
+ gui_text(id, -w / 2, -h / 2, w, h, c0, c1);
+ }
+ }
}
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;
+}
+
+void gui_set_fill(int id)
+{
+ widget[id].type |= GUI_FILL;
}
/*---------------------------------------------------------------------------*/
if ((id = gui_widget(pd, GUI_IMAGE)))
{
- widget[id].text_img = make_image_from_file(NULL, NULL,
- NULL, NULL, file);
- widget[id].w = w;
- widget[id].h = h;
+ widget[id].image = make_image_from_file(file);
+ widget[id].w = w;
+ widget[id].h = h;
}
return id;
}
if ((id = gui_widget(pd, GUI_STATE)))
{
- widget[id].text_img = make_image_from_font(NULL, NULL,
+ widget[id].image = make_image_from_font(NULL, NULL,
&widget[id].w,
&widget[id].h,
- text, font[size],
- scale[size]);
+ text, font[size]);
widget[id].size = size;
widget[id].token = token;
widget[id].value = value;
return id;
}
-int gui_label(int pd, const char *text, int size, int rect, const float *c0,
- const float *c1)
+int gui_label(int pd, const char *text, int size, int rect, const GLubyte *c0,
+ const GLubyte *c1)
{
int id;
if ((id = gui_widget(pd, GUI_LABEL)))
{
- widget[id].text_img = make_image_from_font(NULL, NULL,
- &widget[id].w,
- &widget[id].h,
- text, font[size],
- scale[size]);
+ widget[id].image = make_image_from_font(NULL, NULL,
+ &widget[id].w,
+ &widget[id].h,
+ text, font[size]);
widget[id].size = size;
widget[id].color0 = c0 ? c0 : gui_yel;
widget[id].color1 = c1 ? c1 : gui_red;
if ((id = gui_widget(pd, GUI_COUNT)))
{
for (i = value; i; i /= 10)
- widget[id].w += digit_w[size][0];
+ widget[id].w += widget[digit_id[size][0]].text_w;
- widget[id].h = digit_h[size][0];
+ widget[id].h = widget[digit_id[size][0]].text_h;
widget[id].value = value;
widget[id].size = size;
widget[id].color0 = gui_yel;
if ((id = gui_widget(pd, GUI_CLOCK)))
{
- widget[id].w = digit_w[size][0] * 6;
- widget[id].h = digit_h[size][0];
+ widget[id].w = widget[digit_id[size][0]].text_w * 6;
+ widget[id].h = widget[digit_id[size][0]].text_h;
widget[id].value = value;
widget[id].size = size;
widget[id].color0 = gui_yel;
return id;
}
-int gui_pause(int pd)
-{
- const char *text = _("Paused");
- int id;
-
- if ((id = gui_widget(pd, GUI_PAUSE)))
- {
- widget[id].text_img = make_image_from_font(NULL, NULL,
- &widget[id].w,
- &widget[id].h,
- text, font[GUI_LRG],
- scale[GUI_LRG]);
- widget[id].color0 = gui_wht;
- widget[id].color1 = gui_wht;
- widget[id].value = 0;
- widget[id].size = GUI_LRG;
- widget[id].rect = GUI_ALL;
- }
- return id;
-}
-
/*---------------------------------------------------------------------------*/
+
/*
* Create a multi-line text box using a vertical array of labels.
* Parse the text for '\' characters and treat them as line-breaks.
- * Preserve the rect specifation across the entire array.
+ * Preserve the rect specification across the entire array.
*/
-int gui_multi(int pd, const char *text, int size, int rect, const float *c0,
- const float *c1)
+int gui_multi(int pd, const char *text, int size, int rect, const GLubyte *c0,
+ const GLubyte *c1)
{
int id = 0;
{
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;
}
}
-static void gui_paused_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;
-
- /* The pause widget fills the screen. */
-
- widget[id].w = config_get_d(CONFIG_WIDTH);
- widget[id].h = config_get_d(CONFIG_HEIGHT);
-}
-
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_w = widget[id].w;
+ widget[id].text_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_PAUSE: gui_paused_up(id); break;
+ case GUI_FILLER: break;
default: gui_button_up(id); break;
}
}
for (jd = widget[id].car; jd; jd = widget[jd].cdr)
if ((widget[jd].type & GUI_TYPE) == GUI_FILLER)
c += 1;
+ else if (widget[jd].type & GUI_FILL)
+ {
+ c += 1;
+ jw += widget[jd].w;
+ }
else
jw += widget[jd].w;
{
if ((widget[jd].type & GUI_TYPE) == GUI_FILLER)
gui_widget_dn(jd, jx, y, (w - jw) / c, h);
+ else if (widget[jd].type & GUI_FILL)
+ gui_widget_dn(jd, jx, y, widget[jd].w + (w - jw) / c, h);
else
gui_widget_dn(jd, jx, y, widget[jd].w, h);
for (jd = widget[id].car; jd; jd = widget[jd].cdr)
if ((widget[jd].type & GUI_TYPE) == GUI_FILLER)
c += 1;
+ else if (widget[jd].type & GUI_FILL)
+ {
+ c += 1;
+ jh += widget[jd].h;
+ }
else
jh += widget[jd].h;
{
if ((widget[jd].type & GUI_TYPE) == GUI_FILLER)
gui_widget_dn(jd, x, jy, w, (h - jh) / c);
+ else if (widget[jd].type & GUI_FILL)
+ gui_widget_dn(jd, x, jy, w, widget[jd].h + (h - jh) / c);
else
gui_widget_dn(jd, x, jy, w, widget[jd].h);
{
/* Recall stored width and height for text rendering. */
- int W = widget[id].x;
- int H = widget[id].y;
+ int W = widget[id].text_w;
+ int H = widget[id].text_h;
int R = widget[id].rect;
- int r = ((widget[id].type & GUI_TYPE) == GUI_PAUSE ? radius * 4 : radius);
- const float *c0 = widget[id].color0;
- const float *c1 = widget[id].color1;
+ const GLubyte *c0 = widget[id].color0;
+ const GLubyte *c1 = widget[id].color1;
widget[id].x = x;
widget[id].y = y;
widget[id].w = w;
widget[id].h = h;
- /* Create display lists for the text area and rounded rectangle. */
+ /* Create vertex array data for the text area and rounded rectangle. */
- widget[id].text_obj = gui_list(-W / 2, -H / 2, W, H, c0, c1);
- widget[id].rect_obj = gui_rect(-w / 2, -h / 2, w, h, R, r);
+ gui_rect(id, -w / 2, -h / 2, w, h, R, radius);
+ gui_text(id, -W / 2, -H / 2, W, H, c0, c1);
}
static void gui_widget_dn(int id, int x, int y, int w, int h)
/* Hilite the widget under the cursor, if any. */
- /* gui_point(id, -1, -1); */
+ gui_point(id, -1, -1);
}
int gui_search(int id, int x, int y)
/* Release any GL resources held by this widget. */
- if (glIsTexture(widget[id].text_img))
- glDeleteTextures(1, &widget[id].text_img);
-
- if (glIsList(widget[id].text_obj))
- glDeleteLists(widget[id].text_obj, 1);
- if (glIsList(widget[id].rect_obj))
- glDeleteLists(widget[id].rect_obj, 1);
+ if (glIsTexture(widget[id].image))
+ glDeleteTextures(1, &widget[id].image);
/* Mark this widget unused. */
- widget[id].type = GUI_FREE;
- widget[id].text_img = 0;
- widget[id].text_obj = 0;
- widget[id].rect_obj = 0;
- widget[id].cdr = 0;
- widget[id].car = 0;
+ widget[id].type = GUI_FREE;
+ widget[id].image = 0;
+ widget[id].cdr = 0;
+ widget[id].car = 0;
}
return 0;
}
glTranslatef((GLfloat) (widget[id].x + widget[id].w / 2),
(GLfloat) (widget[id].y + widget[id].h / 2), 0.f);
- glColor4fv(back[i]);
- glCallList(widget[id].rect_obj);
+ glColor4f(back[i][0], back[i][1], back[i][2], back[i][3]);
+ draw_rect(id);
}
glPopMatrix();
GLfloat cy = widget[id].y + widget[id].h / 2.0f;
GLfloat ck = widget[id].scale;
- glTranslatef(+cx, +cy, 0.0f);
- glScalef(ck, ck, ck);
- glTranslatef(-cx, -cy, 0.0f);
+ if (1.0 < ck || ck < 1.0)
+ {
+ glTranslatef(+cx, +cy, 0.0f);
+ glScalef(ck, ck, ck);
+ glTranslatef(-cx, -cy, 0.0f);
+ }
/* Recursively paint all subwidgets. */
widget[id].scale,
widget[id].scale);
- glBindTexture(GL_TEXTURE_2D, widget[id].text_img);
- glColor4fv(gui_wht);
- glCallList(widget[id].rect_obj);
+ glBindTexture(GL_TEXTURE_2D, widget[id].image);
+ glColor4ub(gui_wht[0], gui_wht[1], gui_wht[2], gui_wht[3]);
+ draw_rect(id);
}
glPopMatrix();
}
glPushMatrix();
{
- glColor4fv(gui_wht);
-
/* Translate to the widget center, and apply the pulse scale. */
glTranslatef((GLfloat) (widget[id].x + widget[id].w / 2),
if (widget[id].value > 0)
{
- /* Translate left by half the total width of the rendered value. */
+ /* Translate right by half the total width of the rendered value. */
+
+ GLfloat w = -widget[digit_id[i][0]].text_w * 0.5f;
for (j = widget[id].value; j; j /= 10)
- glTranslatef((GLfloat) +digit_w[i][j % 10] / 2.0f, 0.0f, 0.0f);
+ w += widget[digit_id[i][j % 10]].text_w * 0.5f;
- glTranslatef((GLfloat) -digit_w[i][0] / 2.0f, 0.0f, 0.0f);
+ glTranslatef(w, 0.0f, 0.0f);
- /* Render each digit, moving right after each. */
+ /* Render each digit, moving left after each. */
for (j = widget[id].value; j; j /= 10)
{
- glBindTexture(GL_TEXTURE_2D, digit_text[i][j % 10]);
- glCallList(digit_list[i][j % 10]);
- glTranslatef((GLfloat) -digit_w[i][j % 10], 0.0f, 0.0f);
+ int id = digit_id[i][j % 10];
+
+ glBindTexture(GL_TEXTURE_2D, widget[id].image);
+ draw_text(id);
+ glTranslatef((GLfloat) -widget[id].text_w, 0.0f, 0.0f);
}
}
else if (widget[id].value == 0)
{
/* If the value is zero, just display a zero in place. */
- glBindTexture(GL_TEXTURE_2D, digit_text[i][0]);
- glCallList(digit_list[i][0]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][0]].image);
+ draw_text(digit_id[i][0]);
}
}
glPopMatrix();
int ht = ((widget[id].value % 6000) % 100) / 10;
int ho = ((widget[id].value % 6000) % 100) % 10;
- GLfloat dx_large = (GLfloat) digit_w[i][0];
- GLfloat dx_small = (GLfloat) digit_w[i][0] * 0.75f;
-
+ GLfloat dx_large = (GLfloat) widget[digit_id[i][0]].text_w;
+ GLfloat dx_small = (GLfloat) widget[digit_id[i][0]].text_w * 0.75f;
+
if (widget[id].value < 0)
return;
glPushMatrix();
{
- glColor4fv(gui_wht);
-
/* Translate to the widget center, and apply the pulse scale. */
glTranslatef((GLfloat) (widget[id].x + widget[id].w / 2),
if (mt > 0)
{
- glBindTexture(GL_TEXTURE_2D, digit_text[i][mt]);
- glCallList(digit_list[i][mt]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][mt]].image);
+ draw_text(digit_id[i][mt]);
glTranslatef(dx_large, 0.0f, 0.0f);
}
- glBindTexture(GL_TEXTURE_2D, digit_text[i][mo]);
- glCallList(digit_list[i][mo]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][mo]].image);
+ draw_text(digit_id[i][mo]);
glTranslatef(dx_small, 0.0f, 0.0f);
/* Render the colon. */
- glBindTexture(GL_TEXTURE_2D, digit_text[i][10]);
- glCallList(digit_list[i][10]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][10]].image);
+ draw_text(digit_id[i][10]);
glTranslatef(dx_small, 0.0f, 0.0f);
/* Render the seconds counter. */
- glBindTexture(GL_TEXTURE_2D, digit_text[i][st]);
- glCallList(digit_list[i][st]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][st]].image);
+ draw_text(digit_id[i][st]);
glTranslatef(dx_large, 0.0f, 0.0f);
- glBindTexture(GL_TEXTURE_2D, digit_text[i][so]);
- glCallList(digit_list[i][so]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][so]].image);
+ draw_text(digit_id[i][so]);
glTranslatef(dx_small, 0.0f, 0.0f);
/* Render hundredths counter half size. */
glScalef(0.5f, 0.5f, 1.0f);
- glBindTexture(GL_TEXTURE_2D, digit_text[i][ht]);
- glCallList(digit_list[i][ht]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][ht]].image);
+ draw_text(digit_id[i][ht]);
glTranslatef(dx_large, 0.0f, 0.0f);
- glBindTexture(GL_TEXTURE_2D, digit_text[i][ho]);
- glCallList(digit_list[i][ho]);
+ glBindTexture(GL_TEXTURE_2D, widget[digit_id[i][ho]].image);
+ draw_text(digit_id[i][ho]);
}
glPopMatrix();
}
widget[id].scale,
widget[id].scale);
- glBindTexture(GL_TEXTURE_2D, widget[id].text_img);
- glCallList(widget[id].text_obj);
+ glBindTexture(GL_TEXTURE_2D, widget[id].image);
+ draw_text(id);
}
glPopMatrix();
}
{
if (id)
{
- glPushAttrib(GL_LIGHTING_BIT |
- GL_COLOR_BUFFER_BIT |
- GL_DEPTH_BUFFER_BIT);
- config_push_ortho();
+ video_push_ortho();
{
- glEnable(GL_BLEND);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glPushAttrib(GL_TEXTURE_BIT);
{
+ draw_enable(GL_FALSE, GL_FALSE, GL_TRUE);
glDisable(GL_TEXTURE_2D);
gui_paint_rect(id, 0);
- }
- glPopAttrib();
- gui_paint_text(id);
+ draw_enable(GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_TEXTURE_2D);
+ gui_paint_text(id);
+
+ draw_disable();
+ glColor4ub(gui_wht[0], gui_wht[1], gui_wht[2], gui_wht[3]);
+ }
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_LIGHTING);
+ glDisable(GL_COLOR_MATERIAL);
}
- config_pop_matrix();
- glPopAttrib();
+ video_pop_matrix();
}
}
-void gui_blank(void)
-{
- gui_paint(pause_id);
-}
-
/*---------------------------------------------------------------------------*/
void gui_dump(int id, int d)
return active = jd;
}
+void gui_focus(int i)
+{
+ active = i;
+}
+
int gui_click(void)
{
return active;
/*---------------------------------------------------------------------------*/
-static int gui_vert_test(int id, int jd)
+static int gui_vert_offset(int id, int jd)
{
- /* Determine whether widget id is in vertical contact with widget jd. */
+ /* Vertical offset between bottom of id and top of jd */
- if (id && gui_hot(id) && jd && gui_hot(jd))
- {
- int i0 = widget[id].x;
- int i1 = widget[id].x + widget[id].w;
- int j0 = widget[jd].x;
- int j1 = widget[jd].x + widget[jd].w;
-
- /* Is widget id's top edge is in contact with jd's bottom edge? */
+ return widget[id].y - (widget[jd].y + widget[jd].h);
+}
- if (widget[id].y + widget[id].h == widget[jd].y)
- {
- /* Do widgets id and jd overlap horizontally? */
+static int gui_horz_offset(int id, int jd)
+{
+ /* Horizontal offset between left of id and right of jd */
- if (j0 <= i0 && i0 < j1) return 1;
- if (j0 < i1 && i1 <= j1) return 1;
- if (i0 <= j0 && j0 < i1) return 1;
- if (i0 < j1 && j1 <= i1) return 1;
- }
- }
- return 0;
+ return widget[id].x - (widget[jd].x + widget[jd].w);
}
-static int gui_horz_test(int id, int jd)
+static int gui_vert_dist(int id, int jd)
{
- /* Determine whether widget id is in horizontal contact with widget jd. */
-
- if (id && gui_hot(id) && jd && gui_hot(jd))
- {
- int i0 = widget[id].y;
- int i1 = widget[id].y + widget[id].h;
- int j0 = widget[jd].y;
- int j1 = widget[jd].y + widget[jd].h;
+ /* Vertical distance between the tops of id and jd */
- /* Is widget id's right edge in contact with jd's left edge? */
+ return abs((widget[id].y + widget[id].h) - (widget[jd].y + widget[jd].h));
+}
- if (widget[id].x + widget[id].w == widget[jd].x)
- {
- /* Do widgets id and jd overlap vertically? */
+static int gui_horz_dist(int id, int jd)
+{
+ /* Horizontal distance between the left sides of id and jd */
- if (j0 <= i0 && i0 < j1) return 1;
- if (j0 < i1 && i1 <= j1) return 1;
- if (i0 <= j0 && j0 < i1) return 1;
- if (i0 < j1 && j1 <= i1) return 1;
- }
- }
- return 0;
+ return abs(widget[id].x - widget[jd].x);
}
/*---------------------------------------------------------------------------*/
static int gui_stick_L(int id, int dd)
{
- int jd, kd;
+ int jd, kd, hd;
+ int o, omin, d, dmin;
- /* Find a widget to the left of widget dd. */
+ /* Find the closest "hot" widget to the left of dd (and inside id) */
- if (gui_horz_test(id, dd))
+ if (id && gui_hot(id))
return id;
+ hd = 0;
+ omin = widget[dd].x - widget[id].x + 1;
+ dmin = widget[dd].y + widget[dd].h + widget[id].y + widget[id].h;
+
for (jd = widget[id].car; jd; jd = widget[jd].cdr)
- if ((kd = gui_stick_L(jd, dd)))
- return kd;
+ {
+ kd = gui_stick_L(jd, dd);
- return 0;
+ if (kd && kd != dd)
+ {
+ o = gui_horz_offset(dd, kd);
+ d = gui_vert_dist(dd, kd);
+
+ if (0 <= o && o <= omin && d <= dmin)
+ {
+ hd = kd;
+ omin = o;
+ dmin = d;
+ }
+ }
+ }
+
+ return hd;
}
static int gui_stick_R(int id, int dd)
{
- int jd, kd;
+ int jd, kd, hd;
+ int o, omin, d, dmin;
- /* Find a widget to the right of widget dd. */
+ /* Find the closest "hot" widget to the right of dd (and inside id) */
- if (gui_horz_test(dd, id))
+ if (id && gui_hot(id))
return id;
+ hd = 0;
+ omin = (widget[id].x + widget[id].w) - (widget[dd].x + widget[dd].w) + 1;
+ dmin = (widget[dd].y + widget[dd].h) + (widget[id].y + widget[id].h);
+
for (jd = widget[id].car; jd; jd = widget[jd].cdr)
- if ((kd = gui_stick_R(jd, dd)))
- return kd;
+ {
+ kd = gui_stick_R(jd, dd);
- return 0;
+ if (kd && kd != dd)
+ {
+ o = gui_horz_offset(kd, dd);
+ d = gui_vert_dist(dd, kd);
+
+ if (0 <= o && o <= omin && d <= dmin)
+ {
+ hd = kd;
+ omin = o;
+ dmin = d;
+ }
+ }
+ }
+
+ return hd;
}
static int gui_stick_D(int id, int dd)
{
- int jd, kd;
+ int jd, kd, hd;
+ int o, omin, d, dmin;
- /* Find a widget below widget dd. */
+ /* Find the closest "hot" widget below dd (and inside id) */
- if (gui_vert_test(id, dd))
+ if (id && gui_hot(id))
return id;
+ hd = 0;
+ omin = widget[dd].y - widget[id].y + 1;
+ dmin = widget[dd].x + widget[dd].w + widget[id].x + widget[id].w;
+
for (jd = widget[id].car; jd; jd = widget[jd].cdr)
- if ((kd = gui_stick_D(jd, dd)))
- return kd;
+ {
+ kd = gui_stick_D(jd, dd);
- return 0;
+ if (kd && kd != dd)
+ {
+ o = gui_vert_offset(dd, kd);
+ d = gui_horz_dist(dd, kd);
+
+ if (0 <= o && o <= omin && d <= dmin)
+ {
+ hd = kd;
+ omin = o;
+ dmin = d;
+ }
+ }
+ }
+
+ return hd;
}
static int gui_stick_U(int id, int dd)
{
- int jd, kd;
+ int jd, kd, hd;
+ int o, omin, d, dmin;
- /* Find a widget above widget dd. */
+ /* Find the closest "hot" widget above dd (and inside id) */
- if (gui_vert_test(dd, id))
+ if (id && gui_hot(id))
return id;
+ hd = 0;
+ omin = (widget[id].y + widget[id].h) - (widget[dd].y + widget[dd].h) + 1;
+ dmin = (widget[dd].x + widget[dd].w) + (widget[id].x + widget[id].w);
+
for (jd = widget[id].car; jd; jd = widget[jd].cdr)
- if ((kd = gui_stick_U(jd, dd)))
- return kd;
+ {
+ kd = gui_stick_U(jd, dd);
- return 0;
+ if (kd && kd != dd)
+ {
+ o = gui_vert_offset(kd, dd);
+ d = gui_horz_dist(dd, kd);
+
+ if (0 <= o && o <= omin && d <= dmin)
+ {
+ hd = kd;
+ omin = o;
+ dmin = d;
+ }
+ }
+ }
+
+ return hd;
}
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
-int gui_stick(int id, int x, int y)
+int gui_stick(int id, int a, float v, int bump)
{
- /* Flag the axes to prevent uncontrolled scrolling. */
-
- static int xflag = 1;
- static int yflag = 1;
-
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. */