imtk

view src/imtk.c @ 4:00a4ea4ee6dc

attempt at slightly more complex widgets
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 05 Mar 2011 09:25:27 +0200
parents 038e5577d527
children 09b6e8a5dc14
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <stdarg.h>
6 #include <assert.h>
7 #ifndef __APPLE__
8 #include <GL/glut.h>
9 #else
10 #include <GLUT/glut.h>
11 #endif
12 #include "imtk.h"
14 #define CHECKBOX_SIZE 14
15 #define TEXTBOX_SIZE 100
16 #define SLIDER_SIZE 100
17 #define THUMB_WIDTH 10
18 #define THUMB_HEIGHT 20
20 enum {
21 FRAME_OUTSET,
22 FRAME_INSET
23 };
25 struct key_node {
26 int key;
27 struct key_node *next;
28 };
30 static void set_active(int id);
31 static int set_hot(int id);
32 static int hit_test(int x, int y, int w, int h);
34 static void draw_button(int id, const char *label, int x, int y);
35 static void calc_button_size(const char *label, int *wret, int *hret);
36 static void draw_checkbox(int id, const char *label, int x, int y, int state);
37 static void draw_textbox(int id, const char *text, int x, int y);
38 static void draw_slider(int id, float pos, float min, float max, int x, int y);
39 static void draw_progress(int id, float pos, int x, int y);
40 static void draw_string(int x, int y, const char *str);
41 static int string_size(const char *str);
42 static void draw_frame(int x, int y, int w, int h, int style);
45 /* default colors, can be changed with imtk_set_color */
46 static float colors[][4] = {
47 {0.0, 0.0, 0.0}, /* text color */
48 {0.7, 0.7, 0.7}, /* base color */
49 {0.85, 0.85, 0.85}, /* focus color */
50 {1.0, 1.0, 1.0}, /* lit bevel */
51 {0.3, 0.3, 0.3} /* shadowed bevel */
52 };
54 static int scr_width = 1, scr_height = 1;
55 static int mousex, mousey, prevx, mouse_bnmask;
56 static int active = -1, hot = -1, input = -1, prev_active = -1;
58 static struct key_node *key_list, *key_tail;
61 void imtk_set_color(int col, float r, float g, float b)
62 {
63 assert(col >= 0 && col < sizeof colors / sizeof *colors);
65 colors[col][0] = r;
66 colors[col][1] = g;
67 colors[col][2] = b;
68 }
70 void imtk_inp_key(int key, int state)
71 {
72 if(state == IMTK_DOWN) {
73 struct key_node *node;
75 if(!(node = malloc(sizeof *node))) {
76 return;
77 }
78 node->key = key;
79 node->next = 0;
81 if(key_list) {
82 key_tail->next = node;
83 key_tail = node;
84 } else {
85 key_list = key_tail = node;
86 }
87 }
89 glutPostRedisplay();
90 }
92 void imtk_inp_mouse(int bn, int state)
93 {
94 if(state == IMTK_DOWN) {
95 mouse_bnmask |= 1 << bn;
96 } else {
97 mouse_bnmask &= ~(1 << bn);
98 }
99 glutPostRedisplay();
100 }
102 void imtk_inp_motion(int x, int y)
103 {
104 mousex = x;
105 mousey = y;
107 glutPostRedisplay();
108 }
110 void imtk_inp_reshape(int x, int y)
111 {
112 scr_width = x;
113 scr_height = y;
114 }
116 void imtk_begin(void)
117 {
118 glMatrixMode(GL_PROJECTION);
119 glPushMatrix();
120 glLoadIdentity();
121 glTranslatef(-1, 1, 0);
122 glScalef(2.0 / scr_width, -2.0 / scr_height, 1.0);
124 glMatrixMode(GL_MODELVIEW);
125 glPushMatrix();
126 glLoadIdentity();
128 glPushAttrib(GL_ENABLE_BIT);
129 glDisable(GL_DEPTH_TEST);
130 glDisable(GL_CULL_FACE);
131 glDisable(GL_LIGHTING);
132 }
134 void imtk_end(void)
135 {
136 glPopAttrib();
138 glMatrixMode(GL_PROJECTION);
139 glPopMatrix();
140 glMatrixMode(GL_MODELVIEW);
141 glPopMatrix();
142 }
144 int imtk_button(int id, const char *label, int x, int y)
145 {
146 int w, h, res = 0;
147 int over = 0;
149 assert(id >= 0);
151 calc_button_size(label, &w, &h);
153 if(hit_test(x, y, w, h)) {
154 set_hot(id);
155 over = 1;
156 }
158 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
159 if(over) {
160 set_active(id);
161 }
162 } else { /* mouse button up */
163 if(active == id) {
164 set_active(-1);
165 if(hot == id && over) {
166 res = 1;
167 }
168 }
169 }
171 draw_button(id, label, x, y);
172 return res;
173 }
175 int imtk_checkbox(int id, const char *label, int x, int y, int state)
176 {
177 int sz = CHECKBOX_SIZE;
178 int over = 0;
180 assert(id >= 0);
182 if(hit_test(x, y, sz, sz)) {
183 set_hot(id);
184 over = 1;
185 }
187 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
188 if(over) {
189 set_active(id);
190 }
191 } else { /* mouse button up */
192 if(active == id) {
193 set_active(-1);
194 if(hot == id && over) {
195 state = !state;
196 }
197 }
198 }
200 draw_checkbox(id, label, x, y, state);
201 return state;
202 }
204 void imtk_textbox(int id, char *textbuf, size_t buf_sz, int x, int y)
205 {
206 int len, over = 0;
208 assert(id >= 0);
210 if(hit_test(x, y, TEXTBOX_SIZE, 20)) {
211 set_hot(id);
212 over = 1;
213 }
215 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
216 if(over) {
217 set_active(id);
218 }
219 } else {
220 if(active == id) {
221 set_active(-1);
222 if(hot == id && over) {
223 input = id;
224 }
225 }
226 }
228 if(input == id) {
229 len = strlen(textbuf);
230 while(key_list) {
231 struct key_node *node = key_list;
232 key_list = key_list->next;
234 if(isprint(node->key)) {
235 if(len < buf_sz) {
236 textbuf[len++] = (char)node->key;
237 }
238 } else {
239 switch(node->key) {
240 case '\b':
241 if(len > 0) {
242 textbuf[--len] = 0;
243 }
244 break;
246 default:
247 break;
248 }
249 }
251 free(node);
252 }
253 key_list = key_tail = 0;
254 }
256 draw_textbox(id, textbuf, x, y);
257 }
259 float imtk_slider(int id, float pos, float min, float max, int x, int y)
260 {
261 int thumb_x, thumb_y, over = 0;
262 float range = max - min;
264 assert(id >= 0);
266 pos = (pos - min) / range;
267 if(pos < 0.0) pos = 0.0;
268 if(pos > 1.0) pos = 1.0;
270 thumb_x = x + SLIDER_SIZE * pos - THUMB_WIDTH / 2;
271 thumb_y = y - THUMB_HEIGHT / 2;
273 if(hit_test(thumb_x, thumb_y, THUMB_WIDTH, THUMB_HEIGHT)) {
274 set_hot(id);
275 over = 1;
276 }
278 if(mouse_bnmask & (1 << IMTK_LEFT_BUTTON)) {
279 if(over && hot == id) {
280 if(active != id) {
281 prevx = mousex;
282 }
283 set_active(id);
284 }
285 } else {
286 if(active == id) {
287 set_active(-1);
288 }
289 }
291 if(active == id) {
292 float dx = (float)(mousex - prevx) / (float)SLIDER_SIZE;
293 pos += dx;
294 prevx = mousex;
296 if(pos < 0.0) pos = 0.0;
297 if(pos > 1.0) pos = 1.0;
298 }
300 draw_slider(id, pos, min, max, x, y);
301 return pos * range + min;
302 }
304 void imtk_progress(int id, float pos, int x, int y)
305 {
306 draw_progress(id, pos, x, y);
307 }
309 int imtk_listbox(int id, const char *list, int sel, int x, int y)
310 {
311 int i;
312 assert(id >= 0);
314 if(!list) {
315 return -1;
316 }
318 if(id & 1) {
319 id++;
320 }
322 for(i=0; *list; i++) {
323 if(imtk_button(id + i * 2 + 1, list, x, y + i * 20)) {
324 sel = i;
325 }
326 list += strlen(list) + 1;
327 }
328 return sel;
329 }
331 int imtk_combobox(int id, char *textbuf, size_t buf_sz, const char *list, int sel, int x, int y)
332 {
333 imtk_textbox(id + 1, textbuf, buf_sz, x, y);
334 imtk_button(id + 3, "V", x + TEXTBOX_SIZE, y);
336 if(prev_active == id + 3) {
337 sel = imtk_listbox(id + 5, list, sel, x, y + 20);
338 }
339 return sel;
340 }
342 char *imtk_create_list(const char *first, ...)
343 {
344 int sz;
345 char *buf, *item;
346 va_list ap;
348 if(!first) {
349 return 0;
350 }
352 sz = strlen(first) + 2;
353 if(!(buf = malloc(sz))) {
354 return 0;
355 }
356 memcpy(buf, first, sz - 2);
357 buf[sz - 1] = buf[sz - 2] = 0;
359 va_start(ap, first);
360 while((item = va_arg(ap, char*))) {
361 int len = strlen(item);
362 char *tmp = realloc(buf, sz + len + 1);
363 if(!tmp) {
364 free(buf);
365 return 0;
366 }
367 buf = tmp;
369 memcpy(buf + sz - 1, item, len);
370 sz += len + 1;
371 buf[sz - 1] = buf[sz - 2] = 0;
372 }
373 va_end(ap);
375 return buf;
376 }
378 void imtk_free_list(char *list)
379 {
380 free(list);
381 }
383 static void set_active(int id)
384 {
385 if(id == -1 || hot == id) {
386 prev_active = active;
387 active = id;
388 }
389 }
391 static int set_hot(int id)
392 {
393 if(active == -1) {
394 hot = id;
395 return 1;
396 }
397 return 0;
398 }
400 static int hit_test(int x, int y, int w, int h)
401 {
402 return mousex >= x && mousex < (x + w) &&
403 mousey >= y && mousey < (y + h);
404 }
406 static void draw_button(int id, const char *label, int x, int y)
407 {
408 int width, height;
410 calc_button_size(label, &width, &height);
412 glBegin(GL_QUADS);
413 if(hit_test(x, y, width, height)) {
414 glColor3fv(colors[IMTK_FOCUS_COLOR]);
415 } else {
416 glColor3fv(colors[IMTK_BASE_COLOR]);
417 }
418 glVertex2f(x, y);
419 glVertex2f(x + width, y);
420 glVertex2f(x + width, y + height);
421 glVertex2f(x, y + height);
422 glEnd();
424 draw_frame(x, y, width, height, active == id ? FRAME_INSET : FRAME_OUTSET);
426 glColor3fv(colors[IMTK_TEXT_COLOR]);
427 draw_string(x + 20, y + 15, label);
428 }
430 static void calc_button_size(const char *label, int *wret, int *hret)
431 {
432 int strsz = string_size(label);
433 if(wret) *wret = strsz + 40;
434 if(hret) *hret = 20;
435 }
437 static void draw_checkbox(int id, const char *label, int x, int y, int state)
438 {
439 static const int sz = CHECKBOX_SIZE;
441 if(hit_test(x, y, sz, sz)) {
442 glColor3fv(colors[IMTK_FOCUS_COLOR]);
443 } else {
444 glColor3fv(colors[IMTK_BASE_COLOR]);
445 }
447 glBegin(GL_QUADS);
448 glVertex2f(x, y);
449 glVertex2f(x + sz, y);
450 glVertex2f(x + sz, y + sz);
451 glVertex2f(x, y + sz);
452 glEnd();
454 draw_frame(x, y, sz, sz, FRAME_INSET);
456 glColor3fv(colors[IMTK_TEXT_COLOR]);
457 if(state) {
458 glPushAttrib(GL_LINE_BIT);
459 glLineWidth(2);
461 glBegin(GL_LINES);
462 glVertex2f(x + 2, y + 2);
463 glVertex2f(x + sz - 2, y + sz - 2);
464 glVertex2f(x + sz - 2, y + 2);
465 glVertex2f(x + 2, y + sz - 2);
466 glEnd();
468 glPopAttrib();
469 }
471 draw_string(x + sz + 5, y + sz - 2, label);
472 }
474 static void draw_textbox(int id, const char *text, int x, int y)
475 {
476 int strsz = string_size(text);
478 if(hit_test(x, y, TEXTBOX_SIZE, 20)) {
479 glColor3fv(colors[IMTK_FOCUS_COLOR]);
480 } else {
481 glColor3fv(colors[IMTK_BASE_COLOR]);
482 }
484 glBegin(GL_QUADS);
485 glVertex2f(x, y);
486 glVertex2f(x + TEXTBOX_SIZE, y);
487 glVertex2f(x + TEXTBOX_SIZE, y + 20);
488 glVertex2f(x, y + 20);
489 glEnd();
491 glColor3fv(colors[IMTK_TEXT_COLOR]);
493 if(input == id) {
494 glBegin(GL_LINES);
495 glVertex2f(x + strsz + 2, y + 2);
496 glVertex2f(x + strsz + 2, y + 18);
497 glEnd();
498 }
500 draw_string(x + 2, y + 15, text);
502 draw_frame(x, y, TEXTBOX_SIZE, 20, FRAME_INSET);
503 }
505 static void draw_slider(int id, float pos, float min, float max, int x, int y)
506 {
507 float range = max - min;
508 int thumb_x, thumb_y;
509 char buf[32];
511 thumb_x = x + SLIDER_SIZE * pos - THUMB_WIDTH / 2;
512 thumb_y = y - THUMB_HEIGHT / 2;
514 /* draw trough */
515 glBegin(GL_QUADS);
516 glColor3fv(colors[IMTK_BASE_COLOR]);
517 glVertex2f(x, y - 2);
518 glVertex2f(x + SLIDER_SIZE, y - 2);
519 glVertex2f(x + SLIDER_SIZE, y + 2);
520 glVertex2f(x, y + 2);
521 glEnd();
522 draw_frame(x, y - 2, SLIDER_SIZE, 4, FRAME_INSET);
524 if(hit_test(thumb_x, thumb_y, THUMB_WIDTH, THUMB_HEIGHT)) {
525 glColor3fv(colors[IMTK_FOCUS_COLOR]);
526 } else {
527 glColor3fv(colors[IMTK_BASE_COLOR]);
528 }
530 /* draw handle */
531 glBegin(GL_QUADS);
532 glVertex2f(thumb_x, thumb_y);
533 glVertex2f(thumb_x + THUMB_WIDTH, thumb_y);
534 glVertex2f(thumb_x + THUMB_WIDTH, thumb_y + THUMB_HEIGHT);
535 glVertex2f(thumb_x, thumb_y + THUMB_HEIGHT);
536 glEnd();
537 draw_frame(thumb_x, thumb_y, THUMB_WIDTH, THUMB_HEIGHT, FRAME_OUTSET);
539 /* draw display */
540 sprintf(buf, "%.3f", pos * range + min);
541 glColor3fv(colors[IMTK_TEXT_COLOR]);
542 draw_string(x + SLIDER_SIZE + THUMB_WIDTH / 2 + 2, y + 4, buf);
543 }
545 static void draw_progress(int id, float pos, int x, int y)
546 {
547 int bar_size = SLIDER_SIZE * pos;
549 if(pos < 0.0) pos = 0.0;
550 if(pos > 1.0) pos = 1.0;
552 /* through */
553 glBegin(GL_QUADS);
554 glColor3fv(colors[IMTK_BASE_COLOR]);
555 glVertex2f(x - 1, y - 1);
556 glVertex2f(x + SLIDER_SIZE + 1, y - 1);
557 glVertex2f(x + SLIDER_SIZE + 1, y + 17);
558 glVertex2f(x - 1, y + 17);
559 glEnd();
560 draw_frame(x - 1, y - 1, SLIDER_SIZE + 2, 17, FRAME_INSET);
562 if(pos > 0.0) {
563 /* bar */
564 glBegin(GL_QUADS);
565 glColor3fv(colors[IMTK_BASE_COLOR]);
566 glVertex2f(x, y);
567 glVertex2f(x + bar_size, y);
568 glVertex2f(x + bar_size, y + 15);
569 glVertex2f(x, y + 15);
570 glEnd();
571 draw_frame(x, y, bar_size, 15, FRAME_OUTSET);
572 }
573 }
575 static void draw_string(int x, int y, const char *str)
576 {
577 glRasterPos2i(x, y);
578 while(*str) {
579 glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *str++);
580 }
581 }
583 static int string_size(const char *str)
584 {
585 return glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (const unsigned char*)str);
586 }
588 static void draw_frame(int x, int y, int w, int h, int style)
589 {
590 float tcol[3], bcol[3];
592 switch(style) {
593 case FRAME_INSET:
594 memcpy(tcol, colors[IMTK_BEVEL_SHAD_COLOR], sizeof tcol);
595 memcpy(bcol, colors[IMTK_BEVEL_LIT_COLOR], sizeof bcol);
596 break;
598 case FRAME_OUTSET:
599 default:
600 memcpy(tcol, colors[IMTK_BEVEL_LIT_COLOR], sizeof tcol);
601 memcpy(bcol, colors[IMTK_BEVEL_SHAD_COLOR], sizeof bcol);
602 }
604 glBegin(GL_LINES);
605 glColor3fv(tcol);
606 glVertex2f(x, y + h);
607 glVertex2f(x, y);
608 glVertex2f(x, y);
609 glVertex2f(x + w, y);
610 glColor3fv(bcol);
611 glVertex2f(x + w, y);
612 glVertex2f(x + w, y + h);
613 glVertex2f(x + w, y + h);
614 glVertex2f(x, y + h);
615 glEnd();
616 }