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 --- */
198 void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
202 RTK_ASSERT_TYPE(w, RTK_BUTTON);
205 calc_widget_rect(w, &rect);
206 rtk_resize(w, rect.width, rect.height);
209 rtk_icon *rtk_bn_get_icon(rtk_widget *w)
211 RTK_ASSERT_TYPE(w, RTK_BUTTON);
215 /* --- constructors --- */
217 rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y, int width, int height)
221 if(!(w = rtk_create_widget())) {
225 if(par) rtk_win_add(par, w);
226 rtk_set_text(w, title);
228 rtk_resize(w, width, height);
232 rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
236 if(!(w = rtk_create_widget())) {
239 w->type = RTK_BUTTON;
240 if(par) rtk_win_add(par, w);
241 rtk_set_text(w, str);
242 rtk_set_callback(w, cbfunc, 0);
246 rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
250 if(!(w = rtk_create_widget())) {
253 w->type = RTK_BUTTON;
254 if(par) rtk_win_add(par, w);
255 rtk_bn_set_icon(w, icon);
256 rtk_set_callback(w, cbfunc, 0);
260 rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
264 if(!(w = rtk_create_widget())) {
268 if(par) rtk_win_add(par, w);
269 rtk_set_text(w, text);
273 rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
277 if(!(w = rtk_create_widget())) {
280 w->type = RTK_CHECKBOX;
281 if(par) rtk_win_add(par, w);
282 rtk_set_text(w, text);
283 rtk_set_value(w, chk ? 1 : 0);
284 rtk_set_callback(w, cbfunc, 0);
288 rtk_widget *rtk_create_separator(rtk_widget *par)
292 if(!(w = rtk_create_widget())) {
296 if(par) rtk_win_add(par, w);
301 /* --- icon functions --- */
302 rtk_iconsheet *rtk_load_iconsheet(const char *fname)
306 if(!(is = malloc(sizeof *is))) {
311 if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
318 void rtk_free_iconsheet(rtk_iconsheet *is)
322 img_free_pixels(is->pixels);
326 is->icons = is->icons->next;
333 rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
337 if(!(icon = malloc(sizeof *icon))) {
340 if(!(icon->name = strdup(name))) {
346 icon->scanlen = is->width;
347 icon->pixels = is->pixels + y * is->width + x;
353 #define OFFS (BEVELSZ + PAD)
354 #define CHKBOXSZ (BEVELSZ * 2 + 8)
356 static void calc_widget_rect(rtk_widget *w, rtk_rect *rect)
358 rtk_rect txrect = {0};
364 gfx.textrect(w->any.text, &txrect);
369 rect->width = w->any.width;
370 rect->height = w->any.height;
375 rect->width = w->bn.icon->width + OFFS * 2;
376 rect->height = w->bn.icon->height + OFFS * 2;
378 rect->width = txrect.width + OFFS * 2;
379 rect->height = txrect.height + OFFS * 2;
384 rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
385 rect->height = txrect.height + OFFS * 2;
389 rect->width = txrect.width + PAD * 2;
390 rect->height = txrect.height + PAD * 2;
394 if(w->any.par->win.layout == RTK_VBOX) {
395 rect->width = w->any.par->any.width - PAD * 2;
396 rect->height = PAD * 4 + BEVELSZ * 2;
397 } else if(w->any.par->win.layout == RTK_HBOX) {
398 rect->width = PAD * 4 + BEVELSZ * 2;
399 rect->height = w->any.par->any.height - PAD * 2;
401 rect->width = rect->height = 0;
406 rect->width = rect->height = 0;
410 static int need_relayout(rtk_widget *w)
414 if(w->any.flags & GEOMCHG) {
418 if(w->any.type == RTK_WIN) {
421 if(need_relayout(c)) {
430 static void calc_layout(rtk_widget *w)
436 if(w->any.type == RTK_WIN && w->win.layout != RTK_NONE) {
444 if(w->win.layout == RTK_VBOX) {
445 y += c->any.height + PAD * 2;
447 x += c->any.width + PAD * 2;
454 calc_widget_rect(w, &rect);
455 w->any.width = rect.width;
456 w->any.height = rect.height;
458 w->any.flags = (w->any.flags & ~GEOMCHG) | DIRTY;
461 void rtk_draw_widget(rtk_widget *w)
463 if(need_relayout(w)) {
467 switch(w->any.type) {
488 w->any.flags &= ~DIRTY;
491 static void widget_rect(rtk_widget *w, rtk_rect *rect)
495 rect->width = w->any.width;
496 rect->height = w->any.height;
499 static void abs_pos(rtk_widget *w, int *xpos, int *ypos)
507 abs_pos(w->any.par, &px, &py);
516 #define COL_BG 0xff666666
517 #define COL_BGHL 0xff808080
518 #define COL_LBEV 0xffaaaaaa
519 #define COL_SBEV 0xff222222
520 #define COL_TEXT 0xff000000
522 static void hline(int x, int y, int sz, uint32_t col)
529 gfx.fill(&rect, col);
532 static void vline(int x, int y, int sz, uint32_t col)
539 gfx.fill(&rect, col);
542 enum {FRM_SOLID, FRM_OUTSET, FRM_INSET};
544 static void draw_frame(rtk_rect *rect, int type)
550 tlcol = brcol = 0xff000000;
564 hline(rect->x, rect->y, rect->width, tlcol);
565 vline(rect->x, rect->y + 1, rect->height - 2, tlcol);
566 hline(rect->x, rect->y + rect->height - 1, rect->width, brcol);
567 vline(rect->x + rect->width - 1, rect->y + 1, rect->height - 2, brcol);
570 static void draw_window(rtk_widget *w)
575 widget_rect(w, &rect);
576 gfx.fill(&rect, COL_BG);
585 static void draw_button(rtk_widget *w)
589 widget_rect(w, &rect);
590 abs_pos(w, &rect.x, &rect.y);
592 if(rect.width > 2 && rect.height > 2) {
593 draw_frame(&rect, w->any.flags & PRESS ? FRM_INSET : FRM_OUTSET);
601 gfx.fill(&rect, w->any.flags & HOVER ? COL_BGHL : COL_BG);
603 int offs = w->any.flags & PRESS ? PAD + 1 : PAD;
604 gfx.blit(rect.x + offs, rect.y + offs, w->bn.icon);
606 gfx.fill(&rect, 0xff802020);
610 static void draw_checkbox(rtk_widget *w)
614 static void draw_separator(rtk_widget *w)
616 rtk_widget *win = w->any.par;
621 widget_rect(w, &rect);
622 abs_pos(w, &rect.x, &rect.y);
624 switch(win->win.layout) {
639 draw_frame(&rect, FRM_INSET);
643 static int hittest(rtk_widget *w, int x, int y)
645 if(x < w->any.x || y < w->any.y) return 0;
646 if(x >= w->any.x + w->any.width) return 0;
647 if(y >= w->any.y + w->any.height) return 0;
651 static void sethover(rtk_widget *w)
653 if(hover == w) return;
656 hover->any.flags &= ~HOVER;
660 w->any.flags |= HOVER;
664 static void setpress(rtk_widget *w)
666 if(pressed == w) return;
669 pressed->any.flags &= ~PRESS;
673 w->any.flags |= PRESS;
677 static void click(rtk_widget *w, int x, int y)
684 w->any.cbfunc(w, w->any.cbcls);
693 int rtk_input_key(rtk_widget *w, int key, int press)
698 int rtk_input_mbutton(rtk_widget *w, int bn, int press, int x, int y)
700 if(!hittest(w, x, y)) {
705 if(hover && hittest(hover, x, y)) {
709 if(pressed && hittest(pressed, x, y)) {
710 click(pressed, x, y);
718 int rtk_input_mmotion(rtk_widget *w, int x, int y)
722 if(!hittest(w, x, y)) {
723 int res = hover ? 1 : 0;
730 if(w->type == RTK_WIN) {
733 if(hittest(c, x, y)) {
734 return rtk_input_mmotion(c, x, y);