7 static rtk_draw_ops gfx;
9 static void calc_widget_rect(rtk_widget *w, rtk_rect *rect);
10 static void draw_window(rtk_widget *w);
11 static void draw_button(rtk_widget *w);
12 static void draw_checkbox(rtk_widget *w);
13 static void draw_separator(rtk_widget *w);
15 static void invalfb(rtk_widget *w);
18 static rtk_widget *hover, *focused, *pressed;
21 void rtk_setup(rtk_draw_ops *drawop)
26 rtk_widget *rtk_create_widget(void)
30 if(!(w = calloc(1, sizeof *w))) {
33 w->any.flags = VISIBLE | ENABLED | GEOMCHG | DIRTY;
37 void rtk_free_widget(rtk_widget *w)
41 if(w->type == RTK_WIN) {
43 rtk_widget *c = w->win.clist;
44 w->win.clist = w->win.clist->any.next;
53 int rtk_type(rtk_widget *w)
58 void rtk_move(rtk_widget *w, int x, int y)
62 w->any.flags |= GEOMCHG | DIRTY;
65 void rtk_pos(rtk_widget *w, int *xptr, int *yptr)
71 void rtk_resize(rtk_widget *w, int xsz, int ysz)
75 w->any.flags |= GEOMCHG | DIRTY;
78 void rtk_size(rtk_widget *w, int *xptr, int *yptr)
81 *yptr = w->any.height;
84 void rtk_get_rect(rtk_widget *w, rtk_rect *r)
88 r->width = w->any.width;
89 r->height = w->any.height;
92 int rtk_set_text(rtk_widget *w, const char *str)
95 char *s = strdup(str);
101 calc_widget_rect(w, &rect);
102 rtk_resize(w, rect.width, rect.height);
107 const char *rtk_get_text(rtk_widget *w)
112 void rtk_set_value(rtk_widget *w, int val)
118 int rtk_get_value(rtk_widget *w)
123 void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls)
125 w->any.cbfunc = cbfunc;
129 void rtk_show(rtk_widget *w)
131 w->any.flags |= VISIBLE;
135 void rtk_hide(rtk_widget *w)
137 w->any.flags &= ~VISIBLE;
141 int rtk_visible(const rtk_widget *w)
143 return w->any.flags & VISIBLE;
146 void rtk_invalidate(rtk_widget *w)
148 w->any.flags |= DIRTY;
151 void rtk_validate(rtk_widget *w)
153 w->any.flags &= ~DIRTY;
156 void rtk_win_layout(rtk_widget *w, int layout)
158 w->win.layout = layout;
161 void rtk_win_clear(rtk_widget *w)
165 RTK_ASSERT_TYPE(w, RTK_WIN);
167 while(w->win.clist) {
169 w->win.clist = w->win.clist->any.next;
170 rtk_free_widget(tmp);
173 w->win.clist = w->win.ctail = 0;
177 void rtk_win_add(rtk_widget *par, rtk_widget *child)
179 RTK_ASSERT_TYPE(par, RTK_WIN);
181 if(rtk_win_has(par, child)) {
186 rtk_win_rm(child->any.par, child);
190 par->win.ctail->any.next = child;
191 par->win.ctail = child;
193 par->win.clist = par->win.ctail = child;
197 child->any.par = par;
201 void rtk_win_rm(rtk_widget *par, rtk_widget *child)
203 rtk_widget *prev, dummy;
205 RTK_ASSERT_TYPE(par, RTK_WIN);
207 dummy.any.next = par->win.clist;
209 while(prev->any.next) {
210 if(prev->any.next == child) {
211 if(!child->any.next) {
212 par->win.ctail = prev;
214 prev->any.next = child->any.next;
217 prev = prev->any.next;
219 par->win.clist = dummy.any.next;
223 int rtk_win_has(rtk_widget *par, rtk_widget *child)
227 RTK_ASSERT_TYPE(par, RTK_WIN);
239 /* --- button functions --- */
240 void rtk_bn_mode(rtk_widget *w, int mode)
242 RTK_ASSERT_TYPE(w, RTK_BUTTON);
247 void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
251 RTK_ASSERT_TYPE(w, RTK_BUTTON);
254 calc_widget_rect(w, &rect);
255 rtk_resize(w, rect.width, rect.height);
259 rtk_icon *rtk_bn_get_icon(rtk_widget *w)
261 RTK_ASSERT_TYPE(w, RTK_BUTTON);
265 /* --- constructors --- */
267 rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y,
268 int width, int height, unsigned int flags)
272 if(!(w = rtk_create_widget())) {
276 if(par) rtk_win_add(par, w);
277 rtk_set_text(w, title);
279 rtk_resize(w, width, height);
281 w->any.flags |= flags << 16;
285 rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
289 if(!(w = rtk_create_widget())) {
292 w->type = RTK_BUTTON;
293 if(par) rtk_win_add(par, w);
294 rtk_set_text(w, str);
295 rtk_set_callback(w, cbfunc, 0);
299 rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
303 if(!(w = rtk_create_widget())) {
306 w->type = RTK_BUTTON;
307 if(par) rtk_win_add(par, w);
308 rtk_bn_set_icon(w, icon);
309 rtk_set_callback(w, cbfunc, 0);
313 rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
317 if(!(w = rtk_create_widget())) {
321 if(par) rtk_win_add(par, w);
322 rtk_set_text(w, text);
326 rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
330 if(!(w = rtk_create_widget())) {
333 w->type = RTK_CHECKBOX;
334 if(par) rtk_win_add(par, w);
335 rtk_set_text(w, text);
336 rtk_set_value(w, chk ? 1 : 0);
337 rtk_set_callback(w, cbfunc, 0);
341 rtk_widget *rtk_create_separator(rtk_widget *par)
345 if(!(w = rtk_create_widget())) {
349 if(par) rtk_win_add(par, w);
354 /* --- icon functions --- */
355 rtk_iconsheet *rtk_load_iconsheet(const char *fname)
359 if(!(is = malloc(sizeof *is))) {
364 if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
371 void rtk_free_iconsheet(rtk_iconsheet *is)
375 img_free_pixels(is->pixels);
379 is->icons = is->icons->next;
386 rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
390 if(!(icon = malloc(sizeof *icon))) {
393 if(!(icon->name = strdup(name))) {
399 icon->scanlen = is->width;
400 icon->pixels = is->pixels + y * is->width + x;
406 #define OFFS (BEVELSZ + PAD)
407 #define CHKBOXSZ (BEVELSZ * 2 + 8)
409 static void calc_widget_rect(rtk_widget *w, rtk_rect *rect)
411 rtk_rect txrect = {0};
417 gfx.textrect(w->any.text, &txrect);
422 rect->width = w->any.width;
423 rect->height = w->any.height;
428 rect->width = w->bn.icon->width + OFFS * 2;
429 rect->height = w->bn.icon->height + OFFS * 2;
431 rect->width = txrect.width + OFFS * 2;
432 rect->height = txrect.height + OFFS * 2;
437 rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
438 rect->height = txrect.height + OFFS * 2;
442 rect->width = txrect.width + PAD * 2;
443 rect->height = txrect.height + PAD * 2;
447 if(w->any.par->win.layout == RTK_VBOX) {
448 rect->width = w->any.par->any.width - PAD * 2;
449 rect->height = PAD * 4 + BEVELSZ * 2;
450 } else if(w->any.par->win.layout == RTK_HBOX) {
451 rect->width = PAD * 4 + BEVELSZ * 2;
452 rect->height = w->any.par->any.height - PAD * 2;
454 rect->width = rect->height = 0;
459 rect->width = rect->height = 0;
463 static int need_relayout(rtk_widget *w)
467 if(w->any.flags & GEOMCHG) {
471 if(w->any.type == RTK_WIN) {
474 if(need_relayout(c)) {
483 static void calc_layout(rtk_widget *w)
489 if(w->any.type == RTK_WIN && w->win.layout != RTK_NONE) {
497 if(w->win.layout == RTK_VBOX) {
498 y += c->any.height + PAD * 2;
500 x += c->any.width + PAD * 2;
507 calc_widget_rect(w, &rect);
508 w->any.width = rect.width;
509 w->any.height = rect.height;
511 w->any.flags &= ~GEOMCHG;
515 void rtk_draw_widget(rtk_widget *w)
519 if(!(w->any.flags & VISIBLE)) {
523 if(need_relayout(w)) {
527 dirty = w->any.flags & DIRTY;
528 if(!dirty && w->any.type != RTK_WIN) {
532 switch(w->any.type) {
559 static void widget_rect(rtk_widget *w, rtk_rect *rect)
563 rect->width = w->any.width;
564 rect->height = w->any.height;
567 static void abs_pos(rtk_widget *w, int *xpos, int *ypos)
575 abs_pos(w->any.par, &px, &py);
584 #define COL_BG 0xff666666
585 #define COL_BGHL 0xff808080
586 #define COL_LBEV 0xffaaaaaa
587 #define COL_SBEV 0xff222222
588 #define COL_TEXT 0xff000000
589 #define COL_WINFRM 0xff2244aa
590 #define COL_WINFRM_LIT 0xff4466ff
591 #define COL_WINFRM_SHAD 0xff051166
593 static void hline(int x, int y, int sz, uint32_t col)
600 gfx.fill(&rect, col);
603 static void vline(int x, int y, int sz, uint32_t col)
610 gfx.fill(&rect, col);
613 enum {FRM_SOLID, FRM_OUTSET, FRM_INSET};
615 enum {UICOL_BG, UICOL_LBEV, UICOL_SBEV};
616 static uint32_t uicol[3];
618 static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol)
620 uicol[UICOL_BG] = col;
621 uicol[UICOL_LBEV] = lcol;
622 uicol[UICOL_SBEV] = scol;
625 static void draw_frame(rtk_rect *rect, int type)
631 tlcol = brcol = 0xff000000;
634 tlcol = uicol[UICOL_LBEV];
635 brcol = uicol[UICOL_SBEV];
638 tlcol = uicol[UICOL_SBEV];
639 brcol = uicol[UICOL_LBEV];
645 hline(rect->x, rect->y, rect->width, tlcol);
646 vline(rect->x, rect->y + 1, rect->height - 2, tlcol);
647 hline(rect->x, rect->y + rect->height - 1, rect->width, brcol);
648 vline(rect->x + rect->width - 1, rect->y + 1, rect->height - 2, brcol);
652 #define WINFRM_TBAR 16
654 static void draw_window(rtk_widget *w)
656 rtk_rect rect, frmrect, tbrect;
658 int win_dirty = w->any.flags & DIRTY;
661 widget_rect(w, &rect);
663 if(w->any.flags & FRAME) {
664 uicolor(COL_WINFRM, COL_WINFRM_LIT, COL_WINFRM_SHAD);
667 frmrect.width += WINFRM_SZ * 2;
668 frmrect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
669 frmrect.x -= WINFRM_SZ;
670 frmrect.y -= WINFRM_SZ + WINFRM_TBAR;
673 tbrect.y = rect.y - WINFRM_TBAR;
674 tbrect.width = rect.width;
675 tbrect.height = WINFRM_TBAR;
677 draw_frame(&frmrect, FRM_OUTSET);
682 draw_frame(&frmrect, FRM_INSET);
684 draw_frame(&tbrect, FRM_OUTSET);
689 gfx.fill(&tbrect, COL_WINFRM);
691 gfx.drawtext(tbrect.x, tbrect.y + tbrect.height - 1, w->any.text);
694 gfx.fill(&rect, COL_BG);
707 static void draw_button(rtk_widget *w)
712 widget_rect(w, &rect);
713 abs_pos(w, &rect.x, &rect.y);
715 if(w->bn.mode == RTK_TOGGLEBN) {
716 pressed = w->any.value;
718 pressed = w->any.flags & PRESS;
721 uicolor(COL_BG, COL_LBEV, COL_SBEV);
723 if(rect.width > 2 && rect.height > 2) {
724 draw_frame(&rect, pressed ? FRM_INSET : FRM_OUTSET);
732 gfx.fill(&rect, w->any.flags & HOVER ? COL_BGHL : COL_BG);
734 int offs = w->any.flags & PRESS ? PAD + 1 : PAD;
735 gfx.blit(rect.x + offs, rect.y + offs, w->bn.icon);
737 gfx.fill(&rect, 0xff802020);
741 static void draw_checkbox(rtk_widget *w)
745 static void draw_separator(rtk_widget *w)
747 rtk_widget *win = w->any.par;
752 uicolor(COL_BG, COL_LBEV, COL_SBEV);
754 widget_rect(w, &rect);
755 abs_pos(w, &rect.x, &rect.y);
757 switch(win->win.layout) {
772 draw_frame(&rect, FRM_INSET);
776 static int hittest(rtk_widget *w, int x, int y)
778 if(x < w->any.x || y < w->any.y) return 0;
779 if(x >= w->any.x + w->any.width) return 0;
780 if(y >= w->any.y + w->any.height) return 0;
784 static void sethover(rtk_widget *w)
786 if(hover == w) return;
789 hover->any.flags &= ~HOVER;
791 if(hover->type != RTK_WIN) {
792 rtk_invalidate(hover);
798 w->any.flags |= HOVER;
800 if(w->type != RTK_WIN) {
807 static void setpress(rtk_widget *w)
809 if(pressed == w) return;
812 pressed->any.flags &= ~PRESS;
813 rtk_invalidate(pressed);
818 w->any.flags |= PRESS;
824 static void click(rtk_widget *w, int x, int y)
828 if(w->bn.mode == RTK_TOGGLEBN) {
833 w->any.cbfunc(w, w->any.cbcls);
844 int rtk_input_key(rtk_widget *w, int key, int press)
849 int rtk_input_mbutton(rtk_widget *w, int bn, int press, int x, int y)
851 if(!hittest(w, x, y)) {
856 if(hover && hittest(hover, x, y)) {
860 if(pressed && hittest(pressed, x, y)) {
861 click(pressed, x, y);
869 int rtk_input_mmotion(rtk_widget *w, int x, int y)
873 if(!hittest(w, x, y)) {
874 int res = hover ? 1 : 0;
879 if(w->type == RTK_WIN) {
882 if(hittest(c, x, y)) {
883 return rtk_input_mmotion(c, x, y);
896 void rtk_fix_rect(rtk_rect *rect)
903 if(rect->width < 0) {
909 if(rect->height < 0) {
922 void rtk_rect_union(rtk_rect *a, const rtk_rect *b)
928 x1 = a->x + a->width;
929 y1 = a->y + a->height;
931 if(b->x < x0) x0 = b->x;
932 if(b->y < y0) y0 = b->y;
933 if(b->x + b->width > x1) x1 = b->x + b->width;
934 if(b->y + b->height > y1) y1 = b->y + b->height;
942 static void invalfb(rtk_widget *w)
944 app_redisplay(w->any.x, w->any.y, w->any.width, w->any.height);