6 static rtk_draw_ops gfx;
8 static void calc_widget_rect(rtk_widget *w, rtk_rect *rect);
9 static void draw_window(rtk_widget *w);
10 static void draw_button(rtk_widget *w);
11 static void draw_checkbox(rtk_widget *w);
12 static void draw_separator(rtk_widget *w);
15 static rtk_widget *hover, *focused, *pressed;
18 void rtk_setup(rtk_draw_ops *drawop)
23 rtk_widget *rtk_create_widget(void)
27 if(!(w = calloc(1, sizeof *w))) {
30 w->any.flags = VISIBLE | ENABLED | GEOMCHG | DIRTY;
34 void rtk_free_widget(rtk_widget *w)
38 if(w->type == RTK_WIN) {
40 rtk_widget *c = w->win.clist;
41 w->win.clist = w->win.clist->any.next;
50 int rtk_type(rtk_widget *w)
55 void rtk_move(rtk_widget *w, int x, int y)
59 w->any.flags |= GEOMCHG;
62 void rtk_pos(rtk_widget *w, int *xptr, int *yptr)
68 void rtk_resize(rtk_widget *w, int xsz, int ysz)
72 w->any.flags |= GEOMCHG;
75 void rtk_size(rtk_widget *w, int *xptr, int *yptr)
78 *yptr = w->any.height;
81 int rtk_set_text(rtk_widget *w, const char *str)
84 char *s = strdup(str);
90 calc_widget_rect(w, &rect);
91 rtk_resize(w, rect.width, rect.height);
95 const char *rtk_get_text(rtk_widget *w)
100 void rtk_set_value(rtk_widget *w, int val)
105 int rtk_get_value(rtk_widget *w)
110 void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls)
112 w->any.cbfunc = cbfunc;
116 void rtk_win_layout(rtk_widget *w, int layout)
118 w->win.layout = layout;
121 void rtk_win_clear(rtk_widget *w)
125 RTK_ASSERT_TYPE(w, RTK_WIN);
127 while(w->win.clist) {
129 w->win.clist = w->win.clist->any.next;
130 rtk_free_widget(tmp);
133 w->win.clist = w->win.ctail = 0;
136 void rtk_win_add(rtk_widget *par, rtk_widget *child)
138 RTK_ASSERT_TYPE(par, RTK_WIN);
140 if(rtk_win_has(par, child)) {
145 rtk_win_rm(child->any.par, child);
149 par->win.ctail->any.next = child;
150 par->win.ctail = child;
152 par->win.clist = par->win.ctail = child;
156 child->any.par = par;
159 void rtk_win_rm(rtk_widget *par, rtk_widget *child)
161 rtk_widget *prev, dummy;
163 RTK_ASSERT_TYPE(par, RTK_WIN);
165 dummy.any.next = par->win.clist;
167 while(prev->any.next) {
168 if(prev->any.next == child) {
169 if(!child->any.next) {
170 par->win.ctail = prev;
172 prev->any.next = child->any.next;
175 prev = prev->any.next;
177 par->win.clist = dummy.any.next;
180 int rtk_win_has(rtk_widget *par, rtk_widget *child)
184 RTK_ASSERT_TYPE(par, RTK_WIN);
196 /* --- button functions --- */
197 void rtk_bn_mode(rtk_widget *w, int mode)
199 RTK_ASSERT_TYPE(w, RTK_BUTTON);
203 void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
207 RTK_ASSERT_TYPE(w, RTK_BUTTON);
210 calc_widget_rect(w, &rect);
211 rtk_resize(w, rect.width, rect.height);
214 rtk_icon *rtk_bn_get_icon(rtk_widget *w)
216 RTK_ASSERT_TYPE(w, RTK_BUTTON);
220 /* --- constructors --- */
222 rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y, int width, int height)
226 if(!(w = rtk_create_widget())) {
230 if(par) rtk_win_add(par, w);
231 rtk_set_text(w, title);
233 rtk_resize(w, width, height);
237 rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
241 if(!(w = rtk_create_widget())) {
244 w->type = RTK_BUTTON;
245 if(par) rtk_win_add(par, w);
246 rtk_set_text(w, str);
247 rtk_set_callback(w, cbfunc, 0);
251 rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
255 if(!(w = rtk_create_widget())) {
258 w->type = RTK_BUTTON;
259 if(par) rtk_win_add(par, w);
260 rtk_bn_set_icon(w, icon);
261 rtk_set_callback(w, cbfunc, 0);
265 rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
269 if(!(w = rtk_create_widget())) {
273 if(par) rtk_win_add(par, w);
274 rtk_set_text(w, text);
278 rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
282 if(!(w = rtk_create_widget())) {
285 w->type = RTK_CHECKBOX;
286 if(par) rtk_win_add(par, w);
287 rtk_set_text(w, text);
288 rtk_set_value(w, chk ? 1 : 0);
289 rtk_set_callback(w, cbfunc, 0);
293 rtk_widget *rtk_create_separator(rtk_widget *par)
297 if(!(w = rtk_create_widget())) {
301 if(par) rtk_win_add(par, w);
306 /* --- icon functions --- */
307 rtk_iconsheet *rtk_load_iconsheet(const char *fname)
311 if(!(is = malloc(sizeof *is))) {
316 if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
323 void rtk_free_iconsheet(rtk_iconsheet *is)
327 img_free_pixels(is->pixels);
331 is->icons = is->icons->next;
338 rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
342 if(!(icon = malloc(sizeof *icon))) {
345 if(!(icon->name = strdup(name))) {
351 icon->scanlen = is->width;
352 icon->pixels = is->pixels + y * is->width + x;
358 #define OFFS (BEVELSZ + PAD)
359 #define CHKBOXSZ (BEVELSZ * 2 + 8)
361 static void calc_widget_rect(rtk_widget *w, rtk_rect *rect)
363 rtk_rect txrect = {0};
369 gfx.textrect(w->any.text, &txrect);
374 rect->width = w->any.width;
375 rect->height = w->any.height;
380 rect->width = w->bn.icon->width + OFFS * 2;
381 rect->height = w->bn.icon->height + OFFS * 2;
383 rect->width = txrect.width + OFFS * 2;
384 rect->height = txrect.height + OFFS * 2;
389 rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
390 rect->height = txrect.height + OFFS * 2;
394 rect->width = txrect.width + PAD * 2;
395 rect->height = txrect.height + PAD * 2;
399 if(w->any.par->win.layout == RTK_VBOX) {
400 rect->width = w->any.par->any.width - PAD * 2;
401 rect->height = PAD * 4 + BEVELSZ * 2;
402 } else if(w->any.par->win.layout == RTK_HBOX) {
403 rect->width = PAD * 4 + BEVELSZ * 2;
404 rect->height = w->any.par->any.height - PAD * 2;
406 rect->width = rect->height = 0;
411 rect->width = rect->height = 0;
415 static int need_relayout(rtk_widget *w)
419 if(w->any.flags & GEOMCHG) {
423 if(w->any.type == RTK_WIN) {
426 if(need_relayout(c)) {
435 static void calc_layout(rtk_widget *w)
441 if(w->any.type == RTK_WIN && w->win.layout != RTK_NONE) {
449 if(w->win.layout == RTK_VBOX) {
450 y += c->any.height + PAD * 2;
452 x += c->any.width + PAD * 2;
459 calc_widget_rect(w, &rect);
460 w->any.width = rect.width;
461 w->any.height = rect.height;
463 w->any.flags = (w->any.flags & ~GEOMCHG) | DIRTY;
466 void rtk_draw_widget(rtk_widget *w)
468 if(need_relayout(w)) {
472 switch(w->any.type) {
493 w->any.flags &= ~DIRTY;
496 static void widget_rect(rtk_widget *w, rtk_rect *rect)
500 rect->width = w->any.width;
501 rect->height = w->any.height;
504 static void abs_pos(rtk_widget *w, int *xpos, int *ypos)
512 abs_pos(w->any.par, &px, &py);
521 #define COL_BG 0xff666666
522 #define COL_BGHL 0xff808080
523 #define COL_LBEV 0xffaaaaaa
524 #define COL_SBEV 0xff222222
525 #define COL_TEXT 0xff000000
527 static void hline(int x, int y, int sz, uint32_t col)
534 gfx.fill(&rect, col);
537 static void vline(int x, int y, int sz, uint32_t col)
544 gfx.fill(&rect, col);
547 enum {FRM_SOLID, FRM_OUTSET, FRM_INSET};
549 static void draw_frame(rtk_rect *rect, int type)
555 tlcol = brcol = 0xff000000;
569 hline(rect->x, rect->y, rect->width, tlcol);
570 vline(rect->x, rect->y + 1, rect->height - 2, tlcol);
571 hline(rect->x, rect->y + rect->height - 1, rect->width, brcol);
572 vline(rect->x + rect->width - 1, rect->y + 1, rect->height - 2, brcol);
575 static void draw_window(rtk_widget *w)
580 widget_rect(w, &rect);
581 gfx.fill(&rect, COL_BG);
590 static void draw_button(rtk_widget *w)
595 widget_rect(w, &rect);
596 abs_pos(w, &rect.x, &rect.y);
598 if(w->bn.mode == RTK_TOGGLEBN) {
599 pressed = w->any.value;
601 pressed = w->any.flags & PRESS;
604 if(rect.width > 2 && rect.height > 2) {
605 draw_frame(&rect, pressed ? FRM_INSET : FRM_OUTSET);
613 gfx.fill(&rect, w->any.flags & HOVER ? COL_BGHL : COL_BG);
615 int offs = w->any.flags & PRESS ? PAD + 1 : PAD;
616 gfx.blit(rect.x + offs, rect.y + offs, w->bn.icon);
618 gfx.fill(&rect, 0xff802020);
622 static void draw_checkbox(rtk_widget *w)
626 static void draw_separator(rtk_widget *w)
628 rtk_widget *win = w->any.par;
633 widget_rect(w, &rect);
634 abs_pos(w, &rect.x, &rect.y);
636 switch(win->win.layout) {
651 draw_frame(&rect, FRM_INSET);
655 static int hittest(rtk_widget *w, int x, int y)
657 if(x < w->any.x || y < w->any.y) return 0;
658 if(x >= w->any.x + w->any.width) return 0;
659 if(y >= w->any.y + w->any.height) return 0;
663 static void sethover(rtk_widget *w)
665 if(hover == w) return;
668 hover->any.flags &= ~HOVER;
672 w->any.flags |= HOVER;
676 static void setpress(rtk_widget *w)
678 if(pressed == w) return;
681 pressed->any.flags &= ~PRESS;
685 w->any.flags |= PRESS;
689 static void click(rtk_widget *w, int x, int y)
693 if(w->bn.mode == RTK_TOGGLEBN) {
698 w->any.cbfunc(w, w->any.cbcls);
707 int rtk_input_key(rtk_widget *w, int key, int press)
712 int rtk_input_mbutton(rtk_widget *w, int bn, int press, int x, int y)
714 if(!hittest(w, x, y)) {
719 if(hover && hittest(hover, x, y)) {
723 if(pressed && hittest(pressed, x, y)) {
724 click(pressed, x, y);
732 int rtk_input_mmotion(rtk_widget *w, int x, int y)
736 if(!hittest(w, x, y)) {
737 int res = hover ? 1 : 0;
742 if(w->type == RTK_WIN) {
745 if(hittest(c, x, y)) {
746 return rtk_input_mmotion(c, x, y);