#include <stdlib.h>
#include <string.h>
+#include "imago2.h"
#include "rtk_impl.h"
+static rtk_draw_ops gfx;
+
+static void calc_widget_rect(rtk_widget *w, rtk_rect *rect);
+static void draw_window(rtk_widget *w);
+static void draw_button(rtk_widget *w);
+static void draw_checkbox(rtk_widget *w);
+static void draw_separator(rtk_widget *w);
+
+
+static rtk_widget *hover, *focused, *pressed;
+
+
+void rtk_setup(rtk_draw_ops *drawop)
+{
+ gfx = *drawop;
+}
+
rtk_widget *rtk_create_widget(void)
{
rtk_widget *w;
if(!(w = calloc(1, sizeof *w))) {
return 0;
}
- w->any.visible = w->any.enabled = 1;
+ w->any.flags = VISIBLE | ENABLED | GEOMCHG | DIRTY;
return w;
}
{
w->any.x = x;
w->any.y = y;
+ w->any.flags |= GEOMCHG;
}
void rtk_pos(rtk_widget *w, int *xptr, int *yptr)
{
w->any.width = xsz;
w->any.height = ysz;
+ w->any.flags |= GEOMCHG;
}
void rtk_size(rtk_widget *w, int *xptr, int *yptr)
int rtk_set_text(rtk_widget *w, const char *str)
{
+ rtk_rect rect;
char *s = strdup(str);
if(!s) return -1;
free(w->any.text);
w->any.text = s;
+
+ calc_widget_rect(w, &rect);
+ rtk_resize(w, rect.width, rect.height);
return 0;
}
return w->any.text;
}
+void rtk_set_value(rtk_widget *w, int val)
+{
+ w->any.value = val;
+}
+
+int rtk_get_value(rtk_widget *w)
+{
+ return w->any.value;
+}
+
void rtk_set_callback(rtk_widget *w, rtk_callback cbfunc, void *cls)
{
w->any.cbfunc = cbfunc;
return 0;
}
-rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y, int w, int h)
+/* --- button functions --- */
+void rtk_bn_mode(rtk_widget *w, int mode)
{
- return 0;
+ RTK_ASSERT_TYPE(w, RTK_BUTTON);
+ w->bn.mode = mode;
+}
+
+void rtk_bn_set_icon(rtk_widget *w, rtk_icon *icon)
+{
+ rtk_rect rect;
+
+ RTK_ASSERT_TYPE(w, RTK_BUTTON);
+ w->bn.icon = icon;
+
+ calc_widget_rect(w, &rect);
+ rtk_resize(w, rect.width, rect.height);
+}
+
+rtk_icon *rtk_bn_get_icon(rtk_widget *w)
+{
+ RTK_ASSERT_TYPE(w, RTK_BUTTON);
+ return w->bn.icon;
+}
+
+/* --- constructors --- */
+
+rtk_widget *rtk_create_window(rtk_widget *par, const char *title, int x, int y, int width, int height)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget())) {
+ return 0;
+ }
+ w->type = RTK_WIN;
+ if(par) rtk_win_add(par, w);
+ rtk_set_text(w, title);
+ rtk_move(w, x, y);
+ rtk_resize(w, width, height);
+ return w;
}
rtk_widget *rtk_create_button(rtk_widget *par, const char *str, rtk_callback cbfunc)
{
- return 0;
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget())) {
+ return 0;
+ }
+ w->type = RTK_BUTTON;
+ if(par) rtk_win_add(par, w);
+ rtk_set_text(w, str);
+ rtk_set_callback(w, cbfunc, 0);
+ return w;
}
-rtk_widget *rtk_create_iconbutton(rtk_widget *par, struct image *img, rtk_callback cbfunc)
+rtk_widget *rtk_create_iconbutton(rtk_widget *par, rtk_icon *icon, rtk_callback cbfunc)
{
- return 0;
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget())) {
+ return 0;
+ }
+ w->type = RTK_BUTTON;
+ if(par) rtk_win_add(par, w);
+ rtk_bn_set_icon(w, icon);
+ rtk_set_callback(w, cbfunc, 0);
+ return w;
}
rtk_widget *rtk_create_label(rtk_widget *par, const char *text)
{
- return 0;
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget())) {
+ return 0;
+ }
+ w->type = RTK_LABEL;
+ if(par) rtk_win_add(par, w);
+ rtk_set_text(w, text);
+ return w;
}
rtk_widget *rtk_create_checkbox(rtk_widget *par, const char *text, int chk, rtk_callback cbfunc)
{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget())) {
+ return 0;
+ }
+ w->type = RTK_CHECKBOX;
+ if(par) rtk_win_add(par, w);
+ rtk_set_text(w, text);
+ rtk_set_value(w, chk ? 1 : 0);
+ rtk_set_callback(w, cbfunc, 0);
+ return w;
+}
+
+rtk_widget *rtk_create_separator(rtk_widget *par)
+{
+ rtk_widget *w;
+
+ if(!(w = rtk_create_widget())) {
+ return 0;
+ }
+ w->type = RTK_SEP;
+ if(par) rtk_win_add(par, w);
+ return w;
+}
+
+
+/* --- icon functions --- */
+rtk_iconsheet *rtk_load_iconsheet(const char *fname)
+{
+ rtk_iconsheet *is;
+
+ if(!(is = malloc(sizeof *is))) {
+ return 0;
+ }
+ is->icons = 0;
+
+ if(!(is->pixels = img_load_pixels(fname, &is->width, &is->height, IMG_FMT_RGBA32))) {
+ free(is);
+ return 0;
+ }
+ return is;
+}
+
+void rtk_free_iconsheet(rtk_iconsheet *is)
+{
+ rtk_icon *icon;
+
+ img_free_pixels(is->pixels);
+
+ while(is->icons) {
+ icon = is->icons;
+ is->icons = is->icons->next;
+ free(icon->name);
+ free(icon);
+ }
+ free(is);
+}
+
+rtk_icon *rtk_define_icon(rtk_iconsheet *is, const char *name, int x, int y, int w, int h)
+{
+ rtk_icon *icon;
+
+ if(!(icon = malloc(sizeof *icon))) {
+ return 0;
+ }
+ if(!(icon->name = strdup(name))) {
+ free(icon);
+ return 0;
+ }
+ icon->width = w;
+ icon->height = h;
+ icon->scanlen = is->width;
+ icon->pixels = is->pixels + y * is->width + x;
+ return icon;
+}
+
+#define BEVELSZ 1
+#define PAD 2
+#define OFFS (BEVELSZ + PAD)
+#define CHKBOXSZ (BEVELSZ * 2 + 8)
+
+static void calc_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+ rtk_rect txrect = {0};
+
+ rect->x = w->any.x;
+ rect->y = w->any.y;
+
+ if(w->any.text) {
+ gfx.textrect(w->any.text, &txrect);
+ }
+
+ switch(w->type) {
+ case RTK_WIN:
+ rect->width = w->any.width;
+ rect->height = w->any.height;
+ break;
+
+ case RTK_BUTTON:
+ if(w->bn.icon) {
+ rect->width = w->bn.icon->width + OFFS * 2;
+ rect->height = w->bn.icon->height + OFFS * 2;
+ } else {
+ rect->width = txrect.width + OFFS * 2;
+ rect->height = txrect.height + OFFS * 2;
+ }
+ break;
+
+ case RTK_CHECKBOX:
+ rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
+ rect->height = txrect.height + OFFS * 2;
+ break;
+
+ case RTK_LABEL:
+ rect->width = txrect.width + PAD * 2;
+ rect->height = txrect.height + PAD * 2;
+ break;
+
+ case RTK_SEP:
+ if(w->any.par->win.layout == RTK_VBOX) {
+ rect->width = w->any.par->any.width - PAD * 2;
+ rect->height = PAD * 4 + BEVELSZ * 2;
+ } else if(w->any.par->win.layout == RTK_HBOX) {
+ rect->width = PAD * 4 + BEVELSZ * 2;
+ rect->height = w->any.par->any.height - PAD * 2;
+ } else {
+ rect->width = rect->height = 0;
+ }
+ break;
+
+ default:
+ rect->width = rect->height = 0;
+ }
+}
+
+static int need_relayout(rtk_widget *w)
+{
+ rtk_widget *c;
+
+ if(w->any.flags & GEOMCHG) {
+ return 1;
+ }
+
+ if(w->any.type == RTK_WIN) {
+ c = w->win.clist;
+ while(c) {
+ if(need_relayout(c)) {
+ return 1;
+ }
+ c = c->any.next;
+ }
+ }
+ return 0;
+}
+
+static void calc_layout(rtk_widget *w)
+{
+ int x, y;
+ rtk_widget *c;
+ rtk_rect rect;
+
+ if(w->any.type == RTK_WIN && w->win.layout != RTK_NONE) {
+ x = y = PAD;
+
+ c = w->win.clist;
+ while(c) {
+ rtk_move(c, x, y);
+ calc_layout(c);
+
+ if(w->win.layout == RTK_VBOX) {
+ y += c->any.height + PAD * 2;
+ } else {
+ x += c->any.width + PAD * 2;
+ }
+
+ c = c->any.next;
+ }
+ }
+
+ calc_widget_rect(w, &rect);
+ w->any.width = rect.width;
+ w->any.height = rect.height;
+
+ w->any.flags = (w->any.flags & ~GEOMCHG) | DIRTY;
+}
+
+void rtk_draw_widget(rtk_widget *w)
+{
+ if(need_relayout(w)) {
+ calc_layout(w);
+ }
+
+ switch(w->any.type) {
+ case RTK_WIN:
+ draw_window(w);
+ break;
+
+ case RTK_BUTTON:
+ draw_button(w);
+ break;
+
+ case RTK_CHECKBOX:
+ draw_checkbox(w);
+ break;
+
+ case RTK_SEP:
+ draw_separator(w);
+ break;
+
+ default:
+ break;
+ }
+
+ w->any.flags &= ~DIRTY;
+}
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+ rect->x = w->any.x;
+ rect->y = w->any.y;
+ rect->width = w->any.width;
+ rect->height = w->any.height;
+}
+
+static void abs_pos(rtk_widget *w, int *xpos, int *ypos)
+{
+ int x, y, px, py;
+
+ x = w->any.x;
+ y = w->any.y;
+
+ if(w->any.par) {
+ abs_pos(w->any.par, &px, &py);
+ x += px;
+ y += py;
+ }
+
+ *xpos = x;
+ *ypos = y;
+}
+
+#define COL_BG 0xff666666
+#define COL_BGHL 0xff808080
+#define COL_LBEV 0xffaaaaaa
+#define COL_SBEV 0xff222222
+#define COL_TEXT 0xff000000
+
+static void hline(int x, int y, int sz, uint32_t col)
+{
+ rtk_rect rect;
+ rect.x = x;
+ rect.y = y;
+ rect.width = sz;
+ rect.height = 1;
+ gfx.fill(&rect, col);
+}
+
+static void vline(int x, int y, int sz, uint32_t col)
+{
+ rtk_rect rect;
+ rect.x = x;
+ rect.y = y;
+ rect.width = 1;
+ rect.height = sz;
+ gfx.fill(&rect, col);
+}
+
+enum {FRM_SOLID, FRM_OUTSET, FRM_INSET};
+
+static void draw_frame(rtk_rect *rect, int type)
+{
+ int tlcol, brcol;
+
+ switch(type) {
+ case FRM_SOLID:
+ tlcol = brcol = 0xff000000;
+ break;
+ case FRM_OUTSET:
+ tlcol = COL_LBEV;
+ brcol = COL_SBEV;
+ break;
+ case FRM_INSET:
+ tlcol = COL_SBEV;
+ brcol = COL_LBEV;
+ break;
+ default:
+ break;
+ }
+
+ hline(rect->x, rect->y, rect->width, tlcol);
+ vline(rect->x, rect->y + 1, rect->height - 2, tlcol);
+ hline(rect->x, rect->y + rect->height - 1, rect->width, brcol);
+ vline(rect->x + rect->width - 1, rect->y + 1, rect->height - 2, brcol);
+}
+
+static void draw_window(rtk_widget *w)
+{
+ rtk_rect rect;
+ rtk_widget *c;
+
+ widget_rect(w, &rect);
+ gfx.fill(&rect, COL_BG);
+
+ c = w->win.clist;
+ while(c) {
+ rtk_draw_widget(c);
+ c = c->any.next;
+ }
+}
+
+static void draw_button(rtk_widget *w)
+{
+ int pressed;
+ rtk_rect rect;
+
+ widget_rect(w, &rect);
+ abs_pos(w, &rect.x, &rect.y);
+
+ if(w->bn.mode == RTK_TOGGLEBN) {
+ pressed = w->any.value;
+ } else {
+ pressed = w->any.flags & PRESS;
+ }
+
+ if(rect.width > 2 && rect.height > 2) {
+ draw_frame(&rect, pressed ? FRM_INSET : FRM_OUTSET);
+
+ rect.x++;
+ rect.y++;
+ rect.width -= 2;
+ rect.height -= 2;
+ }
+
+ gfx.fill(&rect, w->any.flags & HOVER ? COL_BGHL : COL_BG);
+ if(w->bn.icon) {
+ int offs = w->any.flags & PRESS ? PAD + 1 : PAD;
+ gfx.blit(rect.x + offs, rect.y + offs, w->bn.icon);
+ } else {
+ gfx.fill(&rect, 0xff802020);
+ }
+}
+
+static void draw_checkbox(rtk_widget *w)
+{
+}
+
+static void draw_separator(rtk_widget *w)
+{
+ rtk_widget *win = w->any.par;
+ rtk_rect rect;
+
+ if(!win) return;
+
+ widget_rect(w, &rect);
+ abs_pos(w, &rect.x, &rect.y);
+
+ switch(win->win.layout) {
+ case RTK_VBOX:
+ rect.y += PAD * 2;
+ rect.height = 2;
+ break;
+
+ case RTK_HBOX:
+ rect.x += PAD * 2;
+ rect.width = 2;
+ break;
+
+ default:
+ break;
+ }
+
+ draw_frame(&rect, FRM_INSET);
+}
+
+
+static int hittest(rtk_widget *w, int x, int y)
+{
+ if(x < w->any.x || y < w->any.y) return 0;
+ if(x >= w->any.x + w->any.width) return 0;
+ if(y >= w->any.y + w->any.height) return 0;
+ return 1;
+}
+
+static void sethover(rtk_widget *w)
+{
+ if(hover == w) return;
+
+ if(hover) {
+ hover->any.flags &= ~HOVER;
+ }
+ hover = w;
+ if(w) {
+ w->any.flags |= HOVER;
+ }
+}
+
+static void setpress(rtk_widget *w)
+{
+ if(pressed == w) return;
+
+ if(pressed) {
+ pressed->any.flags &= ~PRESS;
+ }
+ pressed = w;
+ if(w) {
+ w->any.flags |= PRESS;
+ }
+}
+
+static void click(rtk_widget *w, int x, int y)
+{
+ switch(w->type) {
+ case RTK_BUTTON:
+ if(w->bn.mode == RTK_TOGGLEBN) {
+ case RTK_CHECKBOX:
+ w->any.value ^= 1;
+ }
+ if(w->any.cbfunc) {
+ w->any.cbfunc(w, w->any.cbcls);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+int rtk_input_key(rtk_widget *w, int key, int press)
+{
return 0;
}
+
+int rtk_input_mbutton(rtk_widget *w, int bn, int press, int x, int y)
+{
+ if(!hittest(w, x, y)) {
+ return 0;
+ }
+
+ if(press) {
+ if(hover && hittest(hover, x, y)) {
+ setpress(hover);
+ }
+ } else {
+ if(pressed && hittest(pressed, x, y)) {
+ click(pressed, x, y);
+ }
+ setpress(0);
+ }
+
+ return 1;
+}
+
+int rtk_input_mmotion(rtk_widget *w, int x, int y)
+{
+ rtk_widget *c;
+
+ if(!hittest(w, x, y)) {
+ int res = hover ? 1 : 0;
+ sethover(0);
+ return res;
+ }
+
+ sethover(w);
+
+ if(w->type == RTK_WIN) {
+ c = w->win.clist;
+ while(c) {
+ if(hittest(c, x, y)) {
+ return rtk_input_mmotion(c, x, y);
+ }
+ c = c->any.next;
+ }
+ }
+
+ return 1;
+}