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_invalidate(rtk_widget *w)
131 w->any.flags |= DIRTY;
134 void rtk_validate(rtk_widget *w)
136 w->any.flags &= ~DIRTY;
139 void rtk_win_layout(rtk_widget *w, int layout)
141 w->win.layout = layout;
144 void rtk_win_clear(rtk_widget *w)
148 RTK_ASSERT_TYPE(w, RTK_WIN);
150 while(w->win.clist) {
152 w->win.clist = w->win.clist->any.next;
153 rtk_free_widget(tmp);
156 w->win.clist = w->win.ctail = 0;
160 void rtk_win_add(rtk_widget *par, rtk_widget *child)
162 RTK_ASSERT_TYPE(par, RTK_WIN);
164 if(rtk_win_has(par, child)) {
169 rtk_win_rm(child->any.par, child);
173 par->win.ctail->any.next = child;
174 par->win.ctail = child;
176 par->win.clist = par->win.ctail = child;
180 child->any.par = par;
184 void rtk_win_rm(rtk_widget *par, rtk_widget *child)
186 rtk_widget *prev, dummy;
188 RTK_ASSERT_TYPE(par, RTK_WIN);
190 dummy.any.next = par->win.clist;
192 while(prev->any.next) {
193 if(prev->any.next == child) {
194 if(!child->any.next) {
195 par->win.ctail = prev;
197 prev->any.next = child->any.next;
200 prev = prev->any.next;
202 par->win.clist = dummy.any.next;
206 int rtk_win_has(rtk_widget *par, rtk_widget *child)
210 RTK_ASSERT_TYPE(par, RTK_WIN);
222 /* --- button functions --- */
223 void rtk_bn_mode(rtk_widget *w, int mode)
225 RTK_ASSERT_TYPE(w, RTK_BUTTON);
230 void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
234 RTK_ASSERT_TYPE(w, RTK_BUTTON);
237 calc_widget_rect(w, &rect);
238 rtk_resize(w, rect.width, rect.height);
242 rtk_icon *rtk_bn_get_icon(rtk_widget *w)
244 RTK_ASSERT_TYPE(w, RTK_BUTTON);
248 /* --- constructors --- */
250 rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y, int width, int height)
254 if(!(w = rtk_create_widget())) {
258 if(par) rtk_win_add(par, w);
259 rtk_set_text(w, title);
261 rtk_resize(w, width, height);
265 rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
269 if(!(w = rtk_create_widget())) {
272 w->type = RTK_BUTTON;
273 if(par) rtk_win_add(par, w);
274 rtk_set_text(w, str);
275 rtk_set_callback(w, cbfunc, 0);
279 rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
283 if(!(w = rtk_create_widget())) {
286 w->type = RTK_BUTTON;
287 if(par) rtk_win_add(par, w);
288 rtk_bn_set_icon(w, icon);
289 rtk_set_callback(w, cbfunc, 0);
293 rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
297 if(!(w = rtk_create_widget())) {
301 if(par) rtk_win_add(par, w);
302 rtk_set_text(w, text);
306 rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
310 if(!(w = rtk_create_widget())) {
313 w->type = RTK_CHECKBOX;
314 if(par) rtk_win_add(par, w);
315 rtk_set_text(w, text);
316 rtk_set_value(w, chk ? 1 : 0);
317 rtk_set_callback(w, cbfunc, 0);
321 rtk_widget *rtk_create_separator(rtk_widget *par)
325 if(!(w = rtk_create_widget())) {
329 if(par) rtk_win_add(par, w);
334 /* --- icon functions --- */
335 rtk_iconsheet *rtk_load_iconsheet(const char *fname)
339 if(!(is = malloc(sizeof *is))) {
344 if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
351 void rtk_free_iconsheet(rtk_iconsheet *is)
355 img_free_pixels(is->pixels);
359 is->icons = is->icons->next;
366 rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
370 if(!(icon = malloc(sizeof *icon))) {
373 if(!(icon->name = strdup(name))) {
379 icon->scanlen = is->width;
380 icon->pixels = is->pixels + y * is->width + x;
386 #define OFFS (BEVELSZ + PAD)
387 #define CHKBOXSZ (BEVELSZ * 2 + 8)
389 static void calc_widget_rect(rtk_widget *w, rtk_rect *rect)
391 rtk_rect txrect = {0};
397 gfx.textrect(w->any.text, &txrect);
402 rect->width = w->any.width;
403 rect->height = w->any.height;
408 rect->width = w->bn.icon->width + OFFS * 2;
409 rect->height = w->bn.icon->height + OFFS * 2;
411 rect->width = txrect.width + OFFS * 2;
412 rect->height = txrect.height + OFFS * 2;
417 rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
418 rect->height = txrect.height + OFFS * 2;
422 rect->width = txrect.width + PAD * 2;
423 rect->height = txrect.height + PAD * 2;
427 if(w->any.par->win.layout == RTK_VBOX) {
428 rect->width = w->any.par->any.width - PAD * 2;
429 rect->height = PAD * 4 + BEVELSZ * 2;
430 } else if(w->any.par->win.layout == RTK_HBOX) {
431 rect->width = PAD * 4 + BEVELSZ * 2;
432 rect->height = w->any.par->any.height - PAD * 2;
434 rect->width = rect->height = 0;
439 rect->width = rect->height = 0;
443 static int need_relayout(rtk_widget *w)
447 if(w->any.flags & GEOMCHG) {
451 if(w->any.type == RTK_WIN) {
454 if(need_relayout(c)) {
463 static void calc_layout(rtk_widget *w)
469 if(w->any.type == RTK_WIN && w->win.layout != RTK_NONE) {
477 if(w->win.layout == RTK_VBOX) {
478 y += c->any.height + PAD * 2;
480 x += c->any.width + PAD * 2;
487 calc_widget_rect(w, &rect);
488 w->any.width = rect.width;
489 w->any.height = rect.height;
491 w->any.flags &= ~GEOMCHG;
495 void rtk_draw_widget(rtk_widget *w)
499 if(need_relayout(w)) {
500 dbgmsg("calc layout %s\n", w->any.text ? w->any.text : "?");
504 dirty = w->any.flags & DIRTY;
505 if(!dirty && w->any.type != RTK_WIN) {
509 switch(w->any.type) {
536 static void widget_rect(rtk_widget *w, rtk_rect *rect)
540 rect->width = w->any.width;
541 rect->height = w->any.height;
544 static void abs_pos(rtk_widget *w, int *xpos, int *ypos)
552 abs_pos(w->any.par, &px, &py);
561 #define COL_BG 0xff666666
562 #define COL_BGHL 0xff808080
563 #define COL_LBEV 0xffaaaaaa
564 #define COL_SBEV 0xff222222
565 #define COL_TEXT 0xff000000
567 static void hline(int x, int y, int sz, uint32_t col)
574 gfx.fill(&rect, col);
577 static void vline(int x, int y, int sz, uint32_t col)
584 gfx.fill(&rect, col);
587 enum {FRM_SOLID, FRM_OUTSET, FRM_INSET};
589 static void draw_frame(rtk_rect *rect, int type)
595 tlcol = brcol = 0xff000000;
609 hline(rect->x, rect->y, rect->width, tlcol);
610 vline(rect->x, rect->y + 1, rect->height - 2, tlcol);
611 hline(rect->x, rect->y + rect->height - 1, rect->width, brcol);
612 vline(rect->x + rect->width - 1, rect->y + 1, rect->height - 2, brcol);
615 static void draw_window(rtk_widget *w)
619 int win_dirty = w->any.flags & DIRTY;
622 widget_rect(w, &rect);
623 gfx.fill(&rect, COL_BG);
636 static void draw_button(rtk_widget *w)
641 widget_rect(w, &rect);
642 abs_pos(w, &rect.x, &rect.y);
644 if(w->bn.mode == RTK_TOGGLEBN) {
645 pressed = w->any.value;
647 pressed = w->any.flags & PRESS;
650 if(rect.width > 2 && rect.height > 2) {
651 draw_frame(&rect, pressed ? FRM_INSET : FRM_OUTSET);
659 gfx.fill(&rect, w->any.flags & HOVER ? COL_BGHL : COL_BG);
661 int offs = w->any.flags & PRESS ? PAD + 1 : PAD;
662 gfx.blit(rect.x + offs, rect.y + offs, w->bn.icon);
664 gfx.fill(&rect, 0xff802020);
668 static void draw_checkbox(rtk_widget *w)
672 static void draw_separator(rtk_widget *w)
674 rtk_widget *win = w->any.par;
679 widget_rect(w, &rect);
680 abs_pos(w, &rect.x, &rect.y);
682 switch(win->win.layout) {
697 draw_frame(&rect, FRM_INSET);
701 static int hittest(rtk_widget *w, int x, int y)
703 if(x < w->any.x || y < w->any.y) return 0;
704 if(x >= w->any.x + w->any.width) return 0;
705 if(y >= w->any.y + w->any.height) return 0;
709 static void sethover(rtk_widget *w)
711 if(hover == w) return;
714 hover->any.flags &= ~HOVER;
716 if(hover->type != RTK_WIN) {
717 rtk_invalidate(hover);
723 w->any.flags |= HOVER;
725 if(w->type != RTK_WIN) {
732 static void setpress(rtk_widget *w)
734 if(pressed == w) return;
737 pressed->any.flags &= ~PRESS;
738 rtk_invalidate(pressed);
743 w->any.flags |= PRESS;
749 static void click(rtk_widget *w, int x, int y)
753 if(w->bn.mode == RTK_TOGGLEBN) {
758 w->any.cbfunc(w, w->any.cbcls);
769 int rtk_input_key(rtk_widget *w, int key, int press)
774 int rtk_input_mbutton(rtk_widget *w, int bn, int press, int x, int y)
776 if(!hittest(w, x, y)) {
781 if(hover && hittest(hover, x, y)) {
785 if(pressed && hittest(pressed, x, y)) {
786 click(pressed, x, y);
794 int rtk_input_mmotion(rtk_widget *w, int x, int y)
798 if(!hittest(w, x, y)) {
799 int res = hover ? 1 : 0;
804 if(w->type == RTK_WIN) {
807 if(hittest(c, x, y)) {
808 return rtk_input_mmotion(c, x, y);
822 void rtk_rect_union(rtk_rect *a, const rtk_rect *b)
828 x1 = a->x + a->width;
829 y1 = a->y + a->height;
831 if(b->x < x0) x0 = b->x;
832 if(b->y < y0) y0 = b->y;
833 if(b->x + b->width > x1) x1 = b->x + b->width;
834 if(b->y + b->height > y1) y1 = b->y + b->height;
842 static void invalfb(rtk_widget *w)
844 app_redisplay(w->any.x, w->any.y, w->any.width, w->any.height);