glamtk

view src/imtk.c @ 3:038e5577d527

added slider and progress bar widgets
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 31 Dec 2010 18:10:13 +0200
parents 3d661dd17af3
children 00a4ea4ee6dc
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <assert.h>
6 #ifndef __APPLE__
7 #include <GL/glut.h>
8 #else
9 #include <GLUT/glut.h>
10 #endif
11 #include "imtk.h"
13 #define CHECKBOX_SIZE 14
14 #define TEXTBOX_SIZE 100
15 #define SLIDER_SIZE 100
16 #define THUMB_WIDTH 10
17 #define THUMB_HEIGHT 20
19 enum {
20 FRAME_OUTSET,
21 FRAME_INSET
22 };
24 struct key_node {
25 int key;
26 struct key_node *next;
27 };
29 static void set_active(int id);
30 static int set_hot(int id);
31 static int hit_test(int x, int y, int w, int h);
33 static void draw_button(int id, const char *label, int x, int y);
34 static void calc_button_size(const char *label, int *wret, int *hret);
35 static void draw_checkbox(int id, const char *label, int x, int y, int state);
36 static void draw_textbox(int id, const char *text, int x, int y);
37 static void draw_slider(int id, float pos, float min, float max, int x, int y);
38 static void draw_progress(int id, float pos, int x, int y);
39 static void draw_string(int x, int y, const char *str);
40 static int string_size(const char *str);
41 static void draw_frame(int x, int y, int w, int h, int style);
44 /* default colors, can be changed with imtk_set_color */
45 static float colors[][4] = {
46 {0.0, 0.0, 0.0}, /* text color */
47 {0.7, 0.7, 0.7}, /* base color */
48 {0.85, 0.85, 0.85}, /* focus color */
49 {1.0, 1.0, 1.0}, /* lit bevel */
50 {0.3, 0.3, 0.3} /* shadowed bevel */
51 };
53 static int scr_width = 1, scr_height = 1;
54 static int mousex, mousey, prevx, mouse_bnmask;
55 static int active = -1, hot = -1, input = -1;
57 static struct key_node *key_list, *key_tail;
60 void imtk_set_color(int col, float r, float g, float b)
61 {
62 assert(col >= 0 && col < sizeof colors / sizeof *colors);
64 colors[col][0] = r;
65 colors[col][1] = g;
66 colors[col][2] = b;
67 }
69 void imtk_inp_key(int key, int state)
70 {
71 if(state == IMTK_DOWN) {
72 struct key_node *node;
74 if(!(node = malloc(sizeof *node))) {
75 return;
76 }
77 node->key = key;
78 node->next = 0;
80 if(key_list) {
81 key_tail->next = node;
82 key_tail = node;
83 } else {
84 key_list = key_tail = node;
85 }
86 }
88 glutPostRedisplay();
89 }
91 void imtk_inp_mouse(int bn, int state)
92 {
93 if(state == IMTK_DOWN) {
94 mouse_bnmask |= 1 << bn;
95 } else {
96 mouse_bnmask &= ~(1 << bn);
97 }
98 glutPostRedisplay();
99 }
101 void imtk_inp_motion(int x, int y)
102 {
103 mousex = x;
104 mousey = y;
106 glutPostRedisplay();
107 }
109 void imtk_inp_reshape(int x, int y)
110 {
111 scr_width = x;
112 scr_height = y;
113 }
115 void imtk_begin(void)
116 {
117 glMatrixMode(GL_PROJECTION);
118 glPushMatrix();
119 glLoadIdentity();
120 glTranslatef(-1, 1, 0);
121 glScalef(2.0 / scr_width, -2.0 / scr_height, 1.0);
123 glMatrixMode(GL_MODELVIEW);
124 glPushMatrix();
125 glLoadIdentity();
127 glPushAttrib(GL_ENABLE_BIT);
128 glDisable(GL_DEPTH_TEST);
129 glDisable(GL_CULL_FACE);
130 glDisable(GL_LIGHTING);
131 }
133 void imtk_end(void)
134 {
135 glPopAttrib();
137 glMatrixMode(GL_PROJECTION);
138 glPopMatrix();
139 glMatrixMode(GL_MODELVIEW);
140 glPopMatrix();
141 }
143 int imtk_button(int id, const char *label, int x, int y)
144 {
145 int w, h, res = 0;
146 int over = 0;
148 assert(id >= 0);
150 calc_button_size(label, &w, &h);
152 if(hit_test(x, y, w, h)) {
153 set_hot(id);
154 over = 1;
155 }
157 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
158 if(over) {
159 set_active(id);
160 }
161 } else { /* mouse button up */
162 if(active == id) {
163 set_active(-1);
164 if(hot == id && over) {
165 res = 1;
166 }
167 }
168 }
170 draw_button(id, label, x, y);
171 return res;
172 }
174 int imtk_checkbox(int id, const char *label, int x, int y, int state)
175 {
176 int sz = CHECKBOX_SIZE;
177 int over = 0;
179 assert(id >= 0);
181 if(hit_test(x, y, sz, sz)) {
182 set_hot(id);
183 over = 1;
184 }
186 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
187 if(over) {
188 set_active(id);
189 }
190 } else { /* mouse button up */
191 if(active == id) {
192 set_active(-1);
193 if(hot == id && over) {
194 state = !state;
195 }
196 }
197 }
199 draw_checkbox(id, label, x, y, state);
200 return state;
201 }
203 void imtk_textbox(int id, char *textbuf, size_t buf_sz, int x, int y)
204 {
205 int len, over = 0;
207 assert(id >= 0);
209 if(hit_test(x, y, TEXTBOX_SIZE, 20)) {
210 set_hot(id);
211 over = 1;
212 }
214 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
215 if(over) {
216 set_active(id);
217 }
218 } else {
219 if(active == id) {
220 set_active(-1);
221 if(hot == id && over) {
222 input = id;
223 }
224 }
225 }
227 if(input == id) {
228 len = strlen(textbuf);
229 while(key_list) {
230 struct key_node *node = key_list;
231 key_list = key_list->next;
233 if(isprint(node->key)) {
234 if(len < buf_sz) {
235 textbuf[len++] = (char)node->key;
236 }
237 } else {
238 switch(node->key) {
239 case '\b':
240 if(len > 0) {
241 textbuf[--len] = 0;
242 }
243 break;
245 default:
246 break;
247 }
248 }
250 free(node);
251 }
252 key_list = key_tail = 0;
253 }
255 draw_textbox(id, textbuf, x, y);
256 }
258 float imtk_slider(int id, float pos, float min, float max, int x, int y)
259 {
260 int thumb_x, thumb_y, over = 0;
261 float range = max - min;
263 assert(id >= 0);
265 pos = (pos - min) / range;
266 if(pos < 0.0) pos = 0.0;
267 if(pos > 1.0) pos = 1.0;
269 thumb_x = x + SLIDER_SIZE * pos - THUMB_WIDTH / 2;
270 thumb_y = y - THUMB_HEIGHT / 2;
272 if(hit_test(thumb_x, thumb_y, THUMB_WIDTH, THUMB_HEIGHT)) {
273 set_hot(id);
274 over = 1;
275 }
277 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
278 if(over && hot == id) {
279 if(active != id) {
280 prevx = mousex;
281 }
282 set_active(id);
283 }
284 } else {
285 if(active == id) {
286 set_active(-1);
287 }
288 }
290 if(active == id) {
291 float dx = (float)(mousex - prevx) / (float)SLIDER_SIZE;
292 pos += dx;
293 prevx = mousex;
295 if(pos < 0.0) pos = 0.0;
296 if(pos > 1.0) pos = 1.0;
297 }
299 draw_slider(id, pos, min, max, x, y);
300 return pos * range + min;
301 }
303 void imtk_progress(int id, float pos, int x, int y)
304 {
305 draw_progress(id, pos, x, y);
306 }
308 static void set_active(int id)
309 {
310 if(id == -1 || hot == id) {
311 active = id;
312 }
313 }
315 static int set_hot(int id)
316 {
317 if(active == -1) {
318 hot = id;
319 return 1;
320 }
321 return 0;
322 }
324 static int hit_test(int x, int y, int w, int h)
325 {
326 return mousex >= x && mousex < (x + w) &&
327 mousey >= y && mousey < (y + h);
328 }
330 static void draw_button(int id, const char *label, int x, int y)
331 {
332 int width, height;
334 calc_button_size(label, &width, &height);
336 glBegin(GL_QUADS);
337 if(hit_test(x, y, width, height)) {
338 glColor3fv(colors[IMTK_FOCUS_COLOR]);
339 } else {
340 glColor3fv(colors[IMTK_BASE_COLOR]);
341 }
342 glVertex2f(x, y);
343 glVertex2f(x + width, y);
344 glVertex2f(x + width, y + height);
345 glVertex2f(x, y + height);
346 glEnd();
348 draw_frame(x, y, width, height, active == id ? FRAME_INSET : FRAME_OUTSET);
350 glColor3fv(colors[IMTK_TEXT_COLOR]);
351 draw_string(x + 20, y + 15, label);
352 }
354 static void calc_button_size(const char *label, int *wret, int *hret)
355 {
356 int strsz = string_size(label);
357 if(wret) *wret = strsz + 40;
358 if(hret) *hret = 20;
359 }
361 static void draw_checkbox(int id, const char *label, int x, int y, int state)
362 {
363 static const int sz = CHECKBOX_SIZE;
365 if(hit_test(x, y, sz, sz)) {
366 glColor3fv(colors[IMTK_FOCUS_COLOR]);
367 } else {
368 glColor3fv(colors[IMTK_BASE_COLOR]);
369 }
371 glBegin(GL_QUADS);
372 glVertex2f(x, y);
373 glVertex2f(x + sz, y);
374 glVertex2f(x + sz, y + sz);
375 glVertex2f(x, y + sz);
376 glEnd();
378 draw_frame(x, y, sz, sz, FRAME_INSET);
380 glColor3fv(colors[IMTK_TEXT_COLOR]);
381 if(state) {
382 glPushAttrib(GL_LINE_BIT);
383 glLineWidth(2);
385 glBegin(GL_LINES);
386 glVertex2f(x + 2, y + 2);
387 glVertex2f(x + sz - 2, y + sz - 2);
388 glVertex2f(x + sz - 2, y + 2);
389 glVertex2f(x + 2, y + sz - 2);
390 glEnd();
392 glPopAttrib();
393 }
395 draw_string(x + sz + 5, y + sz - 2, label);
396 }
398 static void draw_textbox(int id, const char *text, int x, int y)
399 {
400 int strsz = string_size(text);
402 if(hit_test(x, y, TEXTBOX_SIZE, 20)) {
403 glColor3fv(colors[IMTK_FOCUS_COLOR]);
404 } else {
405 glColor3fv(colors[IMTK_BASE_COLOR]);
406 }
408 glBegin(GL_QUADS);
409 glVertex2f(x, y);
410 glVertex2f(x + TEXTBOX_SIZE, y);
411 glVertex2f(x + TEXTBOX_SIZE, y + 20);
412 glVertex2f(x, y + 20);
413 glEnd();
415 glColor3fv(colors[IMTK_TEXT_COLOR]);
417 if(input == id) {
418 glBegin(GL_LINES);
419 glVertex2f(x + strsz + 2, y + 2);
420 glVertex2f(x + strsz + 2, y + 18);
421 glEnd();
422 }
424 draw_string(x + 2, y + 15, text);
426 draw_frame(x, y, TEXTBOX_SIZE, 20, FRAME_INSET);
427 }
429 static void draw_slider(int id, float pos, float min, float max, int x, int y)
430 {
431 float range = max - min;
432 int thumb_x, thumb_y;
433 char buf[32];
435 thumb_x = x + SLIDER_SIZE * pos - THUMB_WIDTH / 2;
436 thumb_y = y - THUMB_HEIGHT / 2;
438 /* draw trough */
439 glBegin(GL_QUADS);
440 glColor3fv(colors[IMTK_BASE_COLOR]);
441 glVertex2f(x, y - 2);
442 glVertex2f(x + SLIDER_SIZE, y - 2);
443 glVertex2f(x + SLIDER_SIZE, y + 2);
444 glVertex2f(x, y + 2);
445 glEnd();
446 draw_frame(x, y - 2, SLIDER_SIZE, 4, FRAME_INSET);
448 if(hit_test(thumb_x, thumb_y, THUMB_WIDTH, THUMB_HEIGHT)) {
449 glColor3fv(colors[IMTK_FOCUS_COLOR]);
450 } else {
451 glColor3fv(colors[IMTK_BASE_COLOR]);
452 }
454 /* draw handle */
455 glBegin(GL_QUADS);
456 glVertex2f(thumb_x, thumb_y);
457 glVertex2f(thumb_x + THUMB_WIDTH, thumb_y);
458 glVertex2f(thumb_x + THUMB_WIDTH, thumb_y + THUMB_HEIGHT);
459 glVertex2f(thumb_x, thumb_y + THUMB_HEIGHT);
460 glEnd();
461 draw_frame(thumb_x, thumb_y, THUMB_WIDTH, THUMB_HEIGHT, FRAME_OUTSET);
463 /* draw display */
464 sprintf(buf, "%.3f", pos * range + min);
465 glColor3fv(colors[IMTK_TEXT_COLOR]);
466 draw_string(x + SLIDER_SIZE + THUMB_WIDTH / 2 + 2, y + 4, buf);
467 }
469 static void draw_progress(int id, float pos, int x, int y)
470 {
471 int bar_size = SLIDER_SIZE * pos;
473 if(pos < 0.0) pos = 0.0;
474 if(pos > 1.0) pos = 1.0;
476 /* through */
477 glBegin(GL_QUADS);
478 glColor3fv(colors[IMTK_BASE_COLOR]);
479 glVertex2f(x - 1, y - 1);
480 glVertex2f(x + SLIDER_SIZE + 1, y - 1);
481 glVertex2f(x + SLIDER_SIZE + 1, y + 17);
482 glVertex2f(x - 1, y + 17);
483 glEnd();
484 draw_frame(x - 1, y - 1, SLIDER_SIZE + 2, 17, FRAME_INSET);
486 if(pos > 0.0) {
487 /* bar */
488 glBegin(GL_QUADS);
489 glColor3fv(colors[IMTK_BASE_COLOR]);
490 glVertex2f(x, y);
491 glVertex2f(x + bar_size, y);
492 glVertex2f(x + bar_size, y + 15);
493 glVertex2f(x, y + 15);
494 glEnd();
495 draw_frame(x, y, bar_size, 15, FRAME_OUTSET);
496 }
497 }
499 static void draw_string(int x, int y, const char *str)
500 {
501 glRasterPos2i(x, y);
502 while(*str) {
503 glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *str++);
504 }
505 }
507 static int string_size(const char *str)
508 {
509 return glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (const unsigned char*)str);
510 }
512 static void draw_frame(int x, int y, int w, int h, int style)
513 {
514 float tcol[3], bcol[3];
516 switch(style) {
517 case FRAME_INSET:
518 memcpy(tcol, colors[IMTK_BEVEL_SHAD_COLOR], sizeof tcol);
519 memcpy(bcol, colors[IMTK_BEVEL_LIT_COLOR], sizeof bcol);
520 break;
522 case FRAME_OUTSET:
523 default:
524 memcpy(tcol, colors[IMTK_BEVEL_LIT_COLOR], sizeof tcol);
525 memcpy(bcol, colors[IMTK_BEVEL_SHAD_COLOR], sizeof bcol);
526 }
528 glBegin(GL_LINES);
529 glColor3fv(tcol);
530 glVertex2f(x, y + h);
531 glVertex2f(x, y);
532 glVertex2f(x, y);
533 glVertex2f(x + w, y);
534 glColor3fv(bcol);
535 glVertex2f(x + w, y);
536 glVertex2f(x + w, y + h);
537 glVertex2f(x + w, y + h);
538 glVertex2f(x, y + h);
539 glEnd();
540 }