746 lines
20 KiB
C
746 lines
20 KiB
C
#include "log.h"
|
|
#include "pause.h"
|
|
#include "opengl.h"
|
|
#include "toolkit.h"
|
|
|
|
typedef enum WidgetType_ {
|
|
WIDGET_NULL,
|
|
WIDGET_BUTTON,
|
|
WIDGET_TEXT,
|
|
WIDGET_IMAGE,
|
|
WIDGET_LIST
|
|
} WidgetType;
|
|
|
|
typedef enum WidgetStatus_ {
|
|
WIDGET_STATUS_NORMAL,
|
|
WIDGET_STATUS_MOUSEOVER,
|
|
WIDGET_STATUS_MOUSEDOWN,
|
|
} WidgetStatus;
|
|
|
|
typedef struct Widget_ {
|
|
char* name; // Widget name.
|
|
WidgetType type; // type..
|
|
|
|
double x,y; // Position.
|
|
double w,h; // Dimensions.
|
|
|
|
WidgetStatus status;
|
|
|
|
union {
|
|
// Widget button.
|
|
struct {
|
|
void(*fptr) (char*); // Callback.
|
|
char* display; // Stored text.
|
|
} btn;
|
|
// Widget text.
|
|
struct {
|
|
char* text; // Use printMid for centered printText if not.
|
|
glFont* font;
|
|
glColour* colour;
|
|
int centered;
|
|
} txt;
|
|
struct {
|
|
// Widget image.
|
|
glTexture* image;
|
|
} img;
|
|
struct {
|
|
// Widget list.
|
|
char** options; // Pointer to the options.
|
|
int noptions; // total number of options.
|
|
int selected; // Currently selected option.
|
|
} lst;
|
|
} dat;
|
|
} Widget;
|
|
|
|
typedef struct Window_ {
|
|
unsigned int id; // Unique identifier.
|
|
char* name;
|
|
|
|
double x,y; // Position.
|
|
double w,h; // Dimensions.
|
|
|
|
Widget* widgets; // Widget storage.
|
|
int nwidgets; // Total number of widgets.
|
|
} Window;
|
|
|
|
static unsigned int genwid = 0; // Generate unique id.
|
|
|
|
int toolkit = 0;
|
|
|
|
#define MIN_WINDOWS 3
|
|
static Window* windows = NULL;
|
|
static int nwindows = 0;
|
|
static int mwindows = 0;
|
|
|
|
static Widget* window_newWidget(Window* w);
|
|
static void widget_cleanup(Widget* widget);
|
|
static Window* window_wget(const unsigned int wid);
|
|
// Render.
|
|
static void window_render(Window* w);
|
|
static void toolkit_renderButton(Widget* btn, double bx, double by);
|
|
static void toolkit_renderText(Widget* txt, double bx, double by);
|
|
static void toolkit_renderImage(Widget* img, double bx, double by);
|
|
|
|
// Add a button that when pressed will trigger call, passing it's name as the
|
|
// only parameter.
|
|
void window_addButton(const unsigned int wid, const int x, const int y, const int w,
|
|
const int h, char* name, char* display, void (*call)(char*)) {
|
|
|
|
Window* wdw = window_wget(wid);
|
|
Widget* wgt = window_newWidget(wdw);
|
|
|
|
wgt->type = WIDGET_BUTTON;
|
|
wgt->name = strdup(name);
|
|
wgt->dat.btn.display = strdup(display);
|
|
|
|
// Set the properties.
|
|
wgt->w = (double) w;
|
|
wgt->h = (double) h;
|
|
if(x < 0) wgt->x = wdw->w - wgt->w + x;
|
|
else wgt->x = (double)x;
|
|
if(y < 0) wgt->y = wdw->h - wgt->h + y;
|
|
else wgt->y = (double)y;
|
|
wgt->dat.btn.fptr = call;
|
|
}
|
|
|
|
// Add text to the window.
|
|
void window_addText(const unsigned int wid, const int x, const int y,
|
|
const int w, const int h, const int centered, char* name,
|
|
glFont* font, glColour* colour, char* string) {
|
|
|
|
Window* wdw = window_wget(wid);
|
|
Widget* wgt = window_newWidget(wdw);
|
|
|
|
wgt->type = WIDGET_TEXT;
|
|
wgt->name = strdup(name); // Display the widgets name.
|
|
|
|
// Set the properties.
|
|
wgt->w = (double) w;
|
|
wgt->h = (double) h;
|
|
if(font == NULL) wgt->dat.txt.font = &gl_defFont;
|
|
else wgt->dat.txt.font = font;
|
|
if(x < 0) wgt->x = wdw->w - wgt->w + x;
|
|
else wgt->x = (double)x;
|
|
if(y < 0) wgt->y = wdw->h + y;
|
|
else wgt->y = (double) y;
|
|
wgt->dat.txt.colour = colour;
|
|
wgt->dat.txt.centered = centered;
|
|
wgt->dat.txt.text = strdup(string);
|
|
}
|
|
|
|
// Add a graphic to the window.
|
|
void window_addImage(const unsigned int wid, const int x, const int y,
|
|
char* name, glTexture* image) {
|
|
|
|
Window* wdw = window_wget(wid);
|
|
Widget* wgt = window_newWidget(wdw);
|
|
|
|
wgt->type = WIDGET_IMAGE;
|
|
wgt->name = strdup(name);
|
|
|
|
// Set the propertied.
|
|
wgt->dat.img.image = image;
|
|
if(x < 0) wgt->x = wdw->w - wgt->dat.img.image->sw + x;
|
|
else wgt->x = (double)x;
|
|
if(y < 0) wgt->y = wdw->h + y;
|
|
else wgt->y = (double)y;
|
|
}
|
|
|
|
void window_addList(const unsigned int wid, const int x, const int y,
|
|
const int w, const int h, char* name, char** items, int nitems, int defitem) {
|
|
|
|
Window *wdw = window_wget(wid);
|
|
Widget* wgt = window_newWidget(wdw);
|
|
|
|
wgt->type = WIDGET_LIST;
|
|
wgt->name = strdup(name);
|
|
|
|
wgt->dat.lst.options = items;
|
|
wgt->dat.lst.noptions = nitems;
|
|
wgt->dat.lst.selected = defitem; // -1 would be none.
|
|
wgt->w = (double) w;
|
|
wgt->h = (double) h;
|
|
if(x < 0) wgt->x = wdw->w - wgt->w + x;
|
|
else wgt->x = (double) x;
|
|
if(y < 0) wgt->y = wdw->h - wgt->h + y;
|
|
else wgt->y = (double) y;
|
|
}
|
|
|
|
// Return pointer to newly allocated widget.
|
|
static Widget* window_newWidget(Window* w) {
|
|
Widget* wgt = NULL;
|
|
|
|
w->widgets = realloc(w->widgets, sizeof(Widget)*(++w->nwidgets));
|
|
if(w->widgets == NULL) WARN("Out of memory");
|
|
|
|
wgt = &w->widgets[w->nwidgets - 1];
|
|
|
|
wgt->type = WIDGET_NULL;
|
|
wgt->status = WIDGET_STATUS_NORMAL;
|
|
return wgt;
|
|
}
|
|
|
|
// Return the window of id wid.
|
|
static Window* window_wget(const unsigned int wid) {
|
|
int i;
|
|
for(i = 0; i < nwindows; i++)
|
|
if(windows[i].id == wid)
|
|
return &windows[i];
|
|
DEBUG("Window '%d' not found in windows stack", wid);
|
|
return NULL;
|
|
}
|
|
|
|
// Check if a window exists.
|
|
int window_exists(const char* wdwname) {
|
|
int i;
|
|
for(i = 0; i < nwindows; i++)
|
|
if(strcmp(windows[i].name, wdwname)==0)
|
|
return 1; // Exists.
|
|
|
|
return 0; // Does not exits!
|
|
}
|
|
|
|
// Return the id of a window.
|
|
unsigned int window_get(const char* wdwname) {
|
|
int i;
|
|
for(i = 0; i < nwindows; i++)
|
|
if(strcmp(windows[i].name, wdwname)==0)
|
|
return windows[i].id;
|
|
DEBUG("Window '%s' not found in windows stack", wdwname);
|
|
return 0;
|
|
}
|
|
|
|
// Create a window.
|
|
unsigned int window_create(char* name, const int x, const int y, const int w, const int h) {
|
|
if(nwindows >= mwindows) {
|
|
// We have reached our memory limit.
|
|
windows = realloc(windows, sizeof(Window)*(++mwindows));
|
|
if(windows == NULL) WARN("Out of memory");
|
|
}
|
|
|
|
const int wid = (++genwid); // Unique id
|
|
|
|
windows[nwindows].id = wid;
|
|
windows[nwindows].name = strdup(name);
|
|
|
|
windows[nwindows].w = (double) w;
|
|
windows[nwindows].h = (double) h;
|
|
if((x == -1) && (y == -1)) {
|
|
// Center.
|
|
windows[nwindows].x = gl_screen.w/2. - windows[nwindows].w/2.;
|
|
windows[nwindows].y = gl_screen.h/2. - windows[nwindows].h/2.;
|
|
} else {
|
|
windows[nwindows].x = (double) x;
|
|
windows[nwindows].y = (double) y;
|
|
}
|
|
|
|
windows[nwindows].widgets = NULL;
|
|
windows[nwindows].nwidgets = 0;
|
|
|
|
nwindows++;
|
|
|
|
if(toolkit == 0) {
|
|
// Toolkit is enabled.
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
toolkit = 1; // Enable it.
|
|
}
|
|
|
|
return wid;
|
|
}
|
|
|
|
// Destroy a widget.
|
|
static void widget_cleanup(Widget* widget) {
|
|
if(widget->name) free(widget->name);
|
|
|
|
switch(widget->type) {
|
|
case WIDGET_BUTTON:
|
|
if(widget->dat.btn.display) free(widget->dat.btn.display);
|
|
break;
|
|
case WIDGET_TEXT:
|
|
if(widget->dat.txt.text) free(widget->dat.txt.text);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Destroy a window.
|
|
void window_destroy(const unsigned int wid) {
|
|
int i, j;
|
|
|
|
// Destroy the window.
|
|
for(i = 0; i < nwindows; i++)
|
|
if(windows[i].id == wid) {
|
|
if(windows[i].name) free(windows[i].name);
|
|
for(j = 0; j < windows[i].nwidgets; j++)
|
|
widget_cleanup(&windows[i].widgets[j]);
|
|
free(windows[i].widgets);
|
|
break;
|
|
}
|
|
// Move the other windows down a layer.
|
|
for(; i<(nwindows-1); i++)
|
|
windows[i] = windows[i+1];
|
|
|
|
nwindows--;
|
|
if(nwindows == 0) {
|
|
// No windows left.
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
toolkit = 0; // Disable the toolkit.
|
|
if(paused) unpause();
|
|
}
|
|
}
|
|
|
|
void window_destroyWidget(unsigned int wid, const char* wgtname) {
|
|
Window* w = window_wget(wid);
|
|
int i;
|
|
|
|
for(i = 0; i < w->nwidgets; i++)
|
|
if(strcmp(wgtname, w->widgets[i].name)==0)
|
|
break;
|
|
|
|
if(i >= w->nwidgets) {
|
|
DEBUG("Widget '%s' not found in window '%d'", wgtname, wid);
|
|
return;
|
|
}
|
|
|
|
widget_cleanup(&w->widgets[i]);
|
|
if(i < w->nwidgets-1)
|
|
// This isn't the last widget.
|
|
w->widgets[i] = w->widgets[i-1];
|
|
|
|
w->nwidgets--; // Not that we don't actually realloc the space..
|
|
}
|
|
|
|
// Render a window.
|
|
static void window_render(Window* w) {
|
|
int i;
|
|
double x, y;
|
|
glColour *lc, *c, *dc, *oc;
|
|
|
|
// Position.
|
|
x = w->x - (double)gl_screen.w/2.;
|
|
y = w->y - (double)gl_screen.h/2.;
|
|
|
|
// Colours.
|
|
lc = &cGrey90;
|
|
c = &cGrey70;
|
|
dc = &cGrey50;
|
|
oc = &cGrey30;
|
|
|
|
// Window shaded background.
|
|
// Main body.
|
|
glShadeModel(GL_SMOOTH);
|
|
glBegin(GL_QUADS);
|
|
COLOUR(*dc);
|
|
glVertex2d(x+21., y);
|
|
glVertex2d(x+w->w-21., y);
|
|
|
|
COLOUR(*c);
|
|
glVertex2d(x+w->w-21, y+0.6*w->h);
|
|
glVertex2d(x+21, y+0.6*w->h);
|
|
glEnd();
|
|
|
|
glShadeModel(GL_FLAT);
|
|
glBegin(GL_QUADS);
|
|
COLOUR(*c);
|
|
glVertex2d(x+21., y+0.6*w->h);
|
|
glVertex2d(x+w->w-21., y+0.6*w->h);
|
|
glVertex2d(x+w->w-21., y+w->h);
|
|
glVertex2d(x+21., y+w->h);
|
|
glEnd();
|
|
|
|
glShadeModel(GL_SMOOTH);
|
|
// Left side.
|
|
glBegin(GL_POLYGON);
|
|
COLOUR(*c);
|
|
glVertex2d(x+21., y+0.6*w->h); // Center.
|
|
COLOUR(*dc);
|
|
glVertex2d(x+21., y);
|
|
glVertex2d(x+15., y+1.);
|
|
glVertex2d(x+10., y+3.);
|
|
glVertex2d(x+6., y+6.);
|
|
glVertex2d(x+3., y+10.);
|
|
glVertex2d(x+1., y+15.);
|
|
glVertex2s(x, y+21.);
|
|
COLOUR(*c);
|
|
glVertex2d(x, y+0.6*w->h); // Front of center.
|
|
glVertex2d(x, y+w->h-21.);
|
|
glVertex2d(x+1., y+w->h-15.);
|
|
glVertex2d(x+3., y+w->h-10.);
|
|
glVertex2d(x+6., y+w->h-6.);
|
|
glVertex2d(x+10., y+w->h-3.);
|
|
glVertex2d(x+15., y+w->h-1.);
|
|
glVertex2d(x+21., y+w->h);
|
|
glEnd();
|
|
// Right side.
|
|
glBegin(GL_POLYGON);
|
|
COLOUR(*c);
|
|
glVertex2d(x+w->w-21., y+0.6*w->h); // Center.
|
|
COLOUR(*dc);
|
|
glVertex2d(x+w->w-21., y);
|
|
glVertex2d(x+w->w-15., y+1.);
|
|
glVertex2d(x+w->w-10., y+3.);
|
|
glVertex2d(x+w->w-6., y+6.);
|
|
glVertex2d(x+w->w-3., y+10.);
|
|
glVertex2d(x+w->w-1., y+15.);
|
|
glVertex2d(x+w->w, y+21.);
|
|
COLOUR(*c);
|
|
glVertex2d(x+w->w, y+0.6*w->h); // Front of center.
|
|
glVertex2d(x+w->w, y+w->h-21.);
|
|
glVertex2d(x+w->w-1., y+w->h-15.);
|
|
glVertex2d(x+w->w-3., y+w->h-10.);
|
|
glVertex2d(x+w->w-6., y+w->h-6.);
|
|
glVertex2d(x+w->w-10., y+w->h-3.);
|
|
glVertex2d(x+w->w-15., y+w->h-1.);
|
|
glVertex2d(x+w->w-21., y+w->h);
|
|
glEnd();
|
|
|
|
// Inner outline.
|
|
glShadeModel(GL_SMOOTH);
|
|
glBegin(GL_LINE_LOOP);
|
|
// Left side.
|
|
COLOUR(*c);
|
|
glVertex2d(x+21.+1., y);
|
|
glVertex2d(x+15.+1., y+1.+1.);
|
|
glVertex2d(x+10.+1., y+3.+1.);
|
|
glVertex2d(x+6.+1., y+6.+1.);
|
|
glVertex2d(x+3.+1., y+10.+1.);
|
|
glVertex2d(x+1.+1., y+15.+1.);
|
|
glVertex2d(x+1., y+21.+1.);
|
|
COLOUR(*lc);
|
|
glVertex2d(x+1., y+0.6*w->h); // Front of center.
|
|
glVertex2d(x+1., y+w->h-21.-1.);
|
|
glVertex2d(x+1.+1., y+w->h-15.-1.);
|
|
glVertex2d(x+3.+1., y+w->h-10.-1.);
|
|
glVertex2d(x+6.+1., y+w->h-6.-1.);
|
|
glVertex2d(x+10.+1., y+w->h-2.-1.);
|
|
glVertex2d(x+15.+1., y+w->h-1.-1.);
|
|
glVertex2d(x+21.+1., y+w->h-1.);
|
|
// Switch to right via top.
|
|
glVertex2d(x+w->w-21.-1., y+w->h);
|
|
glVertex2d(x+w->w-15.-1., y+w->h-1.-1.);
|
|
glVertex2d(x+w->w-10.-1., y+w->h-3.-1.);
|
|
glVertex2d(x+w->w-6.-1., y+w->h-6.-1.);
|
|
glVertex2d(x+w->w-2.-1., y+w->h-10.-1.);
|
|
glVertex2d(x+w->w-1.-1., y+w->h-15.-1.);
|
|
glVertex2d(x+w->w-.1, y+w->h-21.-1.);
|
|
glVertex2d(x+w->w-1., y+0.6*w->h); // Front of center.
|
|
COLOUR(*c);
|
|
glVertex2d(x+w->w-1., y+21.+1.);
|
|
glVertex2d(x+w->w-1.-1., y+15.+1.);
|
|
glVertex2d(x+w->w-3.-1., y+10.+1.);
|
|
glVertex2d(x+w->w-6.-1., y+6.+1.);
|
|
glVertex2d(x+w->w-10.-1., y+3.+1.);
|
|
glVertex2d(x+w->w-15.-1., y+1.+1.);
|
|
glVertex2d(x+w->w-21.-1., y+1.);
|
|
glVertex2d(x+21.+1., y+1.);
|
|
glEnd();
|
|
|
|
// Outter outline.
|
|
glShadeModel(GL_FLAT);
|
|
glBegin(GL_LINE_LOOP);
|
|
// Left side.
|
|
COLOUR(*oc);
|
|
glVertex2d(x+21., y);
|
|
glVertex2d(x+15., y+1.);
|
|
glVertex2d(x+10., y+3.);
|
|
glVertex2d(x+6., y+6.);
|
|
glVertex2d(x+3., y+10.);
|
|
glVertex2d(x+1., y+15.);
|
|
glVertex2d(x, y+21.);
|
|
glVertex2d(x, y+0.6*w->h); // Front of center.
|
|
glVertex2d(x, y+w->h-21.);
|
|
glVertex2d(x+1., y+w->h-15.);
|
|
glVertex2d(x+3., y+w->h-10.);
|
|
glVertex2d(x+6., y+w->h-6.);
|
|
glVertex2d(x+10., y+w->h-3.);
|
|
glVertex2d(x+15., y+w->h-1.);
|
|
glVertex2d(x+21., y+w->h);
|
|
// Switch to right via top.
|
|
glVertex2d(x+w->w-21., y+w->h);
|
|
glVertex2d(x+w->w-15., y+w->h-1.);
|
|
glVertex2d(x+w->w-10., y+w->h-3.);
|
|
glVertex2d(x+w->w-6., y+w->h-6.);
|
|
glVertex2d(x+w->w-3., y+w->h-10.);
|
|
glVertex2d(x+w->w-1., y+w->h-15.);
|
|
glVertex2d(x+w->w, y+w->h-21.);
|
|
glVertex2d(x+w->w, y+0.6*w->h); // Front of center.
|
|
glVertex2d(x+w->w, y+21.);
|
|
glVertex2d(x+w->w-1., y+15.);
|
|
glVertex2d(x+w->w-3., y+10.);
|
|
glVertex2d(x+w->w-6., y+6.);
|
|
glVertex2d(x+w->w-10., y+3.);
|
|
glVertex2d(x+w->w-15., y+1.);
|
|
glVertex2d(x+w->w-21., y);
|
|
glVertex2d(x+21., y); // Back to beginning.
|
|
glEnd();
|
|
|
|
// Render the window name.
|
|
gl_printMid(&gl_defFont, w->w,
|
|
x + (double)gl_screen.w/2.,
|
|
y + w->h - 20. + (double)gl_screen.h/2.,
|
|
&cBlack, w->name);
|
|
|
|
// Widgets.
|
|
for(i = 0; i < w->nwidgets; i++) {
|
|
switch(w->widgets[i].type) {
|
|
case WIDGET_NULL:
|
|
break;
|
|
case WIDGET_BUTTON:
|
|
toolkit_renderButton(&w->widgets[i], x, y);
|
|
break;
|
|
case WIDGET_TEXT:
|
|
toolkit_renderText(&w->widgets[i], x, y);
|
|
break;
|
|
case WIDGET_IMAGE:
|
|
toolkit_renderImage(&w->widgets[i], x, y);
|
|
break;
|
|
case WIDGET_LIST:
|
|
// TODO widget list rendering.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void toolkit_renderButton(Widget* btn, double bx, double by) {
|
|
glColour* c, *dc, *oc, *lc;
|
|
double x, y;
|
|
|
|
x = bx + btn->x;
|
|
y = by + btn->y;
|
|
|
|
switch(btn->status) {
|
|
// Set the color.
|
|
case WIDGET_STATUS_NORMAL:
|
|
lc = &cGrey80;
|
|
c = &cGrey60;
|
|
dc = &cGrey40;
|
|
oc = &cGrey20;
|
|
break;
|
|
case WIDGET_STATUS_MOUSEOVER:
|
|
lc = &cWhite;
|
|
c = &cGrey80;
|
|
dc = &cGrey60;
|
|
oc = &cGrey40;
|
|
break;
|
|
case WIDGET_STATUS_MOUSEDOWN:
|
|
lc = &cGreen;
|
|
c = &cGreen;
|
|
dc = &cGrey40;
|
|
oc = &cGrey20;
|
|
break;
|
|
}
|
|
|
|
// Shaded base.
|
|
glShadeModel(GL_SMOOTH);
|
|
glBegin(GL_QUADS);
|
|
COLOUR(*dc);
|
|
glVertex2d(x, y+2/3*btn->h);
|
|
glVertex2d(x+btn->w, y+2/3*btn->h);
|
|
COLOUR(*c);
|
|
glVertex2d(x+btn->w, y+0.6*btn->h);
|
|
glVertex2d(x, y+0.6*btn->h);
|
|
glEnd();
|
|
|
|
glShadeModel(GL_FLAT);
|
|
glBegin(GL_QUADS);
|
|
COLOUR(*c);
|
|
glVertex2d(x, y+0.6*btn->h);
|
|
glVertex2d(x+btn->w, y+0.6*btn->h);
|
|
glVertex2d(x+btn->w, y+btn->h);
|
|
glVertex2d(x, y+btn->h);
|
|
glEnd();
|
|
|
|
// Inner outline.
|
|
glShadeModel(GL_SMOOTH);
|
|
glBegin(GL_LINE_LOOP);
|
|
// Left.
|
|
COLOUR(*c);
|
|
glVertex2d(x, y);
|
|
COLOUR(*lc);
|
|
glVertex2d(x, y+btn->h);
|
|
// Top.
|
|
glVertex2d(x+btn->w, y+btn->h);
|
|
// Right.
|
|
COLOUR(*c);
|
|
glVertex2d(x+btn->w, y);
|
|
// Bottom.
|
|
glVertex2d(x, y);
|
|
glEnd();
|
|
|
|
// Outter outline.
|
|
glShadeModel(GL_FLAT);
|
|
glBegin(GL_LINE_LOOP);
|
|
COLOUR(cBlack);
|
|
// Left.
|
|
glVertex2d(x-1., y);
|
|
glVertex2d(x-1., y+btn->h);
|
|
// Top.
|
|
glVertex2d(x, y+btn->h+1.);
|
|
glVertex2d(x+btn->w, y+btn->h+1.);
|
|
// Right.
|
|
glVertex2d(x+btn->w+1., y+btn->h);
|
|
glVertex2d(x+btn->w+1., y);
|
|
// Bottom.
|
|
glVertex2d(x+btn->w, y-1.);
|
|
glVertex2d(x, y-1.);
|
|
glVertex2d(x-1, y);
|
|
glEnd();
|
|
|
|
gl_printMid(NULL, (int)btn->w,
|
|
bx + (double)gl_screen.w/2. + btn->x,
|
|
by + (double)gl_screen.h/2. + btn->y + (btn->h - gl_defFont.h)/2.,
|
|
&cDarkRed, btn->dat.btn.display);
|
|
}
|
|
|
|
static void toolkit_renderText(Widget* txt, double bx, double by) {
|
|
if(txt->dat.txt.centered)
|
|
gl_printMid(txt->dat.txt.font, txt->w,
|
|
bx + (double)gl_screen.w/2. + txt->x,
|
|
by + (double)gl_screen.h/2. + txt->y,
|
|
txt->dat.txt.colour, txt->dat.txt.text);
|
|
else
|
|
gl_printText(txt->dat.txt.font, txt->w, txt->h,
|
|
bx + (double)gl_screen.w/2. + txt->x,
|
|
by + (double)gl_screen.h/2. + txt->y,
|
|
txt->dat.txt.colour, txt->dat.txt.text);
|
|
}
|
|
|
|
// Render the image.
|
|
static void toolkit_renderImage(Widget* img, double bx, double by) {
|
|
glColour* lc, *c, *oc;
|
|
double x, y;
|
|
x = bx + img->x;
|
|
y = by + img->y;
|
|
|
|
lc = &cGrey90;
|
|
c = &cGrey70;
|
|
oc = &cGrey30;
|
|
|
|
// Image.
|
|
gl_blitStatic(img->dat.img.image,
|
|
x + (double)gl_screen.w/2.,
|
|
y + (double)gl_screen.h/2., NULL);
|
|
|
|
// Inner outline (outwards).
|
|
glShadeModel(GL_SMOOTH);
|
|
glBegin(GL_LINE_LOOP);
|
|
COLOUR(*lc);
|
|
// Top.
|
|
glVertex2d(x-1, y+img->dat.img.image->sh+1.);
|
|
glVertex2d(x+img->dat.img.image->sw, y+img->dat.img.image->sh+1.);
|
|
// Right.
|
|
COLOUR(*c);
|
|
glVertex2d(x+img->dat.img.image->sw, y);
|
|
// Bottom.
|
|
glVertex2d(x-1., y);
|
|
// Left.
|
|
COLOUR(*lc);
|
|
glVertex2d(x-1., y+img->dat.img.image->sh+1.);
|
|
glEnd();
|
|
|
|
// Outter outline.
|
|
glShadeModel(GL_SMOOTH);
|
|
glBegin(GL_LINE_LOOP);
|
|
COLOUR(*oc);
|
|
// Top.
|
|
glVertex2d(x-2., y+img->dat.img.image->sh+2.);
|
|
glVertex2d(x+img->dat.img.image->sw+1., y+img->dat.img.image->sh+2.);
|
|
// Right.
|
|
glVertex2d(x+img->dat.img.image->sw+1., y-1.);
|
|
// Bottom.
|
|
glVertex2d(x-2., y-1.);
|
|
// Left.
|
|
glVertex2d(x-2., y+img->dat.img.image->sh+2.);
|
|
glEnd();
|
|
}
|
|
|
|
// Render the window.
|
|
void toolkit_render(void) {
|
|
int i;
|
|
|
|
if(gl_has(OPENGL_AA_LINE)) glEnable(GL_LINE_SMOOTH);
|
|
if(gl_has(OPENGL_AA_POLYGON)) glEnable(GL_POLYGON_SMOOTH);
|
|
|
|
for(i = 0; i < nwindows; i++)
|
|
window_render(&windows[i]);
|
|
|
|
if(gl_has(OPENGL_AA_LINE)) glDisable(GL_LINE_SMOOTH);
|
|
if(gl_has(OPENGL_AA_POLYGON)) glDisable(GL_POLYGON_SMOOTH);
|
|
|
|
}
|
|
|
|
// Input.
|
|
static int mouse_down = 0;
|
|
void toolkit_mouseEvent(SDL_Event* event) {
|
|
int i;
|
|
double x, y;
|
|
Window* w;
|
|
Widget* wgt;
|
|
|
|
// Set mouse button status.
|
|
if(event->type == SDL_MOUSEBUTTONDOWN) mouse_down = 1;
|
|
else if(event->type == SDL_MOUSEBUTTONUP) mouse_down = 0;
|
|
// Ignore movements if mouse is down.
|
|
else if((event->type == SDL_MOUSEMOTION) && mouse_down) return;
|
|
|
|
// Absolute positions.
|
|
if(event->type == SDL_MOUSEMOTION) {
|
|
x = (double)event->motion.x;
|
|
y = gl_screen.h - (double)event->motion.y;
|
|
}
|
|
else if((event->type == SDL_MOUSEBUTTONDOWN) || (event->type == SDL_MOUSEBUTTONUP)) {
|
|
x = (double)event->button.x;
|
|
y = gl_screen.h - (double)event->motion.y;
|
|
}
|
|
|
|
w = &windows[nwindows-1];
|
|
|
|
if((x < w->x) || (x > (w->x + w->w)) || (y < w->y) || (y > (w->y + w->h)))
|
|
return; // Not in current window.
|
|
|
|
// Relative positions.
|
|
x -= w->x;
|
|
y -= w->y;
|
|
|
|
for(i = 0; i < w->nwidgets; i++) {
|
|
wgt = &w->widgets[i];
|
|
if((x > wgt->x) && (x < (wgt->x + wgt->w)) && (y > wgt->y) && (y < (wgt->y + wgt->h))) {
|
|
switch(event->type) {
|
|
case SDL_MOUSEMOTION:
|
|
wgt->status = WIDGET_STATUS_MOUSEOVER;
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
wgt->status = WIDGET_STATUS_MOUSEDOWN;
|
|
break;
|
|
case SDL_MOUSEBUTTONUP:
|
|
if(wgt->status == WIDGET_STATUS_MOUSEDOWN) {
|
|
if(wgt->type == WIDGET_BUTTON) (*wgt->dat.btn.fptr)(wgt->name);
|
|
}
|
|
wgt->status = WIDGET_STATUS_NORMAL;
|
|
break;
|
|
}
|
|
} else
|
|
wgt->status = WIDGET_STATUS_NORMAL;
|
|
}
|
|
}
|
|
|
|
// Init.
|
|
int toolkit_init(void) {
|
|
windows = malloc(sizeof(Window)*MIN_WINDOWS);
|
|
nwindows = 0;
|
|
mwindows = MIN_WINDOWS;
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
return 0;
|
|
}
|
|
|
|
// Exit the toolkit.
|
|
void toolkit_exit(void) {
|
|
int i;
|
|
for(i = 0; i < nwindows; i++) {
|
|
window_destroy(windows[i].id);
|
|
free(windows);
|
|
}
|
|
}
|
|
|