adding a bunch of code (vesa, keyb, mouse, etc) to the menu
[cdmenu] / menu / src / rtk_draw.c
diff --git a/menu/src/rtk_draw.c b/menu/src/rtk_draw.c
new file mode 100644 (file)
index 0000000..ad58bad
--- /dev/null
@@ -0,0 +1,702 @@
+#include <string.h>
+#include "app.h"
+#include "rtk.h"
+#include "rtk_impl.h"
+
+rtk_draw_ops rtk_gfx;
+#define gfx rtk_gfx
+
+static int fontheight;
+
+enum {
+       FRM_SOLID,
+       FRM_OUTSET,
+       FRM_INSET,
+       FRM_FILLBG = 0x100
+};
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect);
+static void abs_widget_rect(rtk_widget *w, rtk_rect *rect);
+static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol);
+static void draw_frame(rtk_rect *rect, int type, int sz);
+
+static void draw_window(rtk_widget *w);
+static void draw_label(rtk_widget *w);
+static void draw_button(rtk_widget *w);
+static void draw_checkbox(rtk_widget *w);
+static void draw_textbox(rtk_widget *w);
+static void draw_slider(rtk_widget *w);
+static void draw_separator(rtk_widget *w);
+static void draw_drawbox(rtk_widget *w);
+
+
+#define BEVELSZ                1
+#define PAD                    (w->pad)
+#define OFFS           (BEVELSZ + PAD)
+#define CHKBOXSZ       (BEVELSZ * 2 + 8)
+
+#define WINFRM_SZ      2
+#define WINFRM_TBAR    16
+
+void rtk_init_drawing(void)
+{
+       const char *s = "QI|9g/";
+       rtk_rect r;
+
+       gfx.textrect(s, &r);
+       fontheight = r.height;
+}
+
+
+void rtk_calc_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+       rtk_widget *child;
+       rtk_button *bn;
+       rtk_rect txrect = {0};
+
+       rect->x = w->x;
+       rect->y = w->y;
+       rect->width = w->width;
+       rect->height = w->height;
+
+       rtk_abs_pos(w, &w->absx, &w->absy);
+
+       if((w->flags & (AUTOWIDTH | AUTOHEIGHT)) == 0) {
+               return;
+       }
+
+       if(w->text) {
+               gfx.textrect(w->text, &txrect);
+       }
+
+       switch(w->type) {
+       case RTK_WIN:
+               if((w->flags & (AUTOWIDTH | AUTOHEIGHT))) {
+                       rtk_rect subrect;
+                       rtk_window *win = (rtk_window*)w;
+                       rtk_null_rect(&subrect);
+                       child = win->clist;
+                       while(child) {
+                               rtk_rect crect;
+                               widget_rect(child, &crect);
+                               rtk_rect_union(&subrect, &crect);
+                               child = child->next;
+                       }
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = subrect.width + PAD * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = subrect.height + PAD * 2;
+                       }
+               }
+               break;
+
+       case RTK_DRAWBOX:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = w->width;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = w->height;
+               }
+               break;
+
+       case RTK_BUTTON:
+               bn = (rtk_button*)w;
+               if(bn->icon) {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = bn->icon->width + OFFS * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = bn->icon->height + OFFS * 2;
+                       }
+               } else {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = txrect.width + OFFS * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = txrect.height + OFFS * 2;
+                       }
+               }
+               break;
+
+       case RTK_CHECKBOX:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = txrect.width + CHKBOXSZ + OFFS * 2 + PAD;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = txrect.height + OFFS * 2;
+               }
+               break;
+
+       case RTK_LABEL:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = txrect.width + PAD * 2;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = txrect.height + PAD * 2;
+               }
+               break;
+
+       case RTK_TEXTBOX:
+               if(w->flags & AUTOHEIGHT) {
+                       if(rect->height < fontheight + OFFS * 2) {
+                               rect->height = fontheight + OFFS * 2;
+                       }
+               }
+               break;
+
+       case RTK_SEP:
+               if(w->par->layout == RTK_VBOX) {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = w->par->width - PAD * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = PAD * 4 + BEVELSZ * 2;
+                       }
+               } else if(w->par->layout == RTK_HBOX) {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = PAD * 4 + BEVELSZ * 2;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = w->par->height - PAD * 2;
+                       }
+               } else {
+                       if(w->flags & AUTOWIDTH) {
+                               rect->width = 0;
+                       }
+                       if(w->flags & AUTOHEIGHT) {
+                               rect->height = 0;
+                       }
+               }
+               break;
+
+       default:
+               if(w->flags & AUTOWIDTH) {
+                       rect->width = 0;
+               }
+               if(w->flags & AUTOHEIGHT) {
+                       rect->height = 0;
+               }
+       }
+}
+
+void rtk_abs_pos(rtk_widget *w, int *xpos, int *ypos)
+{
+       int x, y, px, py;
+
+       x = w->x;
+       y = w->y;
+
+       if(w->par) {
+               rtk_abs_pos((rtk_widget*)w->par, &px, &py);
+               x += px;
+               y += py;
+       }
+
+       *xpos = x;
+       *ypos = y;
+}
+
+
+int rtk_hittest(rtk_widget *w, int x, int y)
+{
+       int x0, y0, x1, y1;
+
+       if(!(w->flags & VISIBLE)) {
+               return 0;
+       }
+
+       x0 = w->absx;
+       y0 = w->absy;
+       x1 = x0 + w->width;
+       y1 = y0 + w->height;
+
+       if(w->type == RTK_WIN && (w->flags & FRAME)) {
+               x0 -= WINFRM_SZ;
+               y0 -= WINFRM_SZ + WINFRM_TBAR;
+               x1 += WINFRM_SZ;
+               y1 += WINFRM_SZ;
+       }
+
+       if(x < x0 || y < y0) return 0;
+       if(x >= x1 || y >= y1) return 0;
+       return 1;
+}
+
+
+void rtk_invalfb(rtk_widget *w)
+{
+       rtk_rect rect;
+
+       rect.x = w->x;
+       rect.y = w->y;
+       rect.width = w->width;
+       rect.height = w->height;
+
+       rtk_abs_pos(w, &rect.x, &rect.y);
+
+       if(w->type == RTK_WIN && (w->flags & FRAME)) {
+               rect.x -= WINFRM_SZ;
+               rect.y -= WINFRM_SZ + WINFRM_TBAR;
+               rect.width += WINFRM_SZ * 2;
+               rect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+       }
+
+       app_invalidate(rect.x, rect.y, rect.width, rect.height);
+}
+
+void rtk_clearfb(rtk_widget *w)
+{
+       rtk_rect rect;
+
+       rect.x = w->x;
+       rect.y = w->y;
+       rect.width = w->width;
+       rect.height = w->height;
+
+       rtk_abs_pos(w, &rect.x, &rect.y);
+
+       if(w->type == RTK_WIN && (w->flags & FRAME)) {
+               rect.x -= WINFRM_SZ;
+               rect.y -= WINFRM_SZ + WINFRM_TBAR;
+               rect.width += WINFRM_SZ * 2;
+               rect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+       }
+
+       app_clear_rect(rect.x, rect.y, rect.width, rect.height);
+}
+
+static int need_relayout(rtk_widget *w)
+{
+       rtk_widget *c;
+
+       if(w->flags & GEOMCHG) {
+               return 1;
+       }
+
+       if(w->type == RTK_WIN) {
+               c = ((rtk_window*)w)->clist;
+               while(c) {
+                       if(need_relayout(c)) {
+                               return 1;
+                       }
+                       c = c->next;
+               }
+       }
+       return 0;
+}
+
+static void calc_layout(rtk_widget *w)
+{
+       int x, y, dx, dy;
+       rtk_widget *c;
+       rtk_window *win = (rtk_window*)w;
+       rtk_rect rect;
+
+       if(w->type == RTK_WIN) {
+               x = y = PAD;
+
+               c = win->clist;
+               while(c) {
+                       if(win->layout != RTK_NONE) {
+                               rtk_move(c, x, y);
+                               calc_layout(c);
+
+                               if(win->layout == RTK_VBOX) {
+                                       y += c->height + PAD * 2;
+                               } else if(win->layout == RTK_HBOX) {
+                                       x += c->width + PAD * 2;
+                               }
+                       } else {
+                               calc_layout(c);
+                       }
+
+                       c = c->next;
+               }
+       }
+
+       rtk_calc_widget_rect(w, &rect);
+       dx = rect.width - w->width;
+       dy = rect.height - w->height;
+       w->width = rect.width;
+       w->height = rect.height;
+
+       if((dx | dy) == 0) {
+               w->flags &= ~GEOMCHG;
+       }
+       rtk_invalidate(w);
+}
+
+static int calc_substr_width(const char *str, int start, int end)
+{
+       rtk_rect rect;
+       int len;
+       char *buf;
+
+       if(end <= start) {
+               return 0;
+       }
+       if(end <= 0) {
+               end = strlen(str);
+       }
+
+       len = end - start;
+       buf = alloca(len + 1);
+
+       memcpy(buf, str + start, len);
+       buf[len] = 0;
+
+       gfx.textrect(buf, &rect);
+       return rect.width;
+}
+
+void rtk_draw_widget(rtk_widget *w)
+{
+       int dirty;
+
+       if(!(w->flags & VISIBLE)) {
+               return;
+       }
+
+       if(need_relayout(w)) {
+               calc_layout(w);
+       }
+
+       dirty = w->flags & DIRTY;
+       if(!dirty && w->type != RTK_WIN) {
+               return;
+       }
+
+       switch(w->type) {
+       case RTK_WIN:
+               draw_window(w);
+               break;
+
+       case RTK_LABEL:
+               draw_label(w);
+               break;
+
+       case RTK_BUTTON:
+               draw_button(w);
+               break;
+
+       case RTK_CHECKBOX:
+               draw_checkbox(w);
+               break;
+
+       case RTK_TEXTBOX:
+               draw_textbox(w);
+               break;
+
+       case RTK_SLIDER:
+               draw_slider(w);
+               break;
+
+       case RTK_SEP:
+               draw_separator(w);
+               break;
+
+       case RTK_DRAWBOX:
+               draw_drawbox(w);
+               break;
+
+       default:
+               break;
+       }
+
+       if(w->drawcb) {
+               w->drawcb(w, w->drawcls);
+       }
+
+       if(w->flags & DBGRECT) {
+               rtk_rect r;
+               abs_widget_rect(w, &r);
+               uicolor(0xffff0000, 0xffff0000, 0xffff0000);
+               draw_frame(&r, FRM_SOLID, 1);
+       }
+
+       if(dirty) {
+               rtk_validate(w);
+               rtk_invalfb(w);
+       }
+}
+
+static void widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+       rect->x = w->x;
+       rect->y = w->y;
+       rect->width = w->width;
+       rect->height = w->height;
+}
+
+static void abs_widget_rect(rtk_widget *w, rtk_rect *rect)
+{
+       rect->x = w->absx;
+       rect->y = w->absy;
+       rect->width = w->width;
+       rect->height = w->height;
+}
+
+#define COL_BG                                 0xff666666
+#define COL_BGHL                               0xff808080
+#define COL_LBEV                               0xffaaaaaa
+#define COL_SBEV                               0xff222222
+#define COL_TEXT                               0xff000000
+#define COL_WINFRM_FOCUS               0xff6688cc
+#define COL_WINFRM_LIT_FOCUS   0xff88aaff
+#define COL_WINFRM_SHAD_FOCUS  0xff224466
+#define COL_WINFRM                             0xff667788
+#define COL_WINFRM_LIT                 0xff8899aa
+#define COL_WINFRM_SHAD                        0xff224455
+#define COL_TBOX                               0xffeeccbb
+
+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 {UICOL_BG, UICOL_LBEV, UICOL_SBEV};
+static uint32_t uicol[3];
+
+static void uicolor(uint32_t col, uint32_t lcol, uint32_t scol)
+{
+       uicol[UICOL_BG] = col;
+       uicol[UICOL_LBEV] = lcol;
+       uicol[UICOL_SBEV] = scol;
+}
+
+static void draw_frame(rtk_rect *rect, int type, int sz)
+{
+       int i, tlcol, brcol, fillbg;
+       rtk_rect r = *rect;
+
+       fillbg = type & FRM_FILLBG;
+       type &= ~FRM_FILLBG;
+
+       switch(type) {
+       case FRM_OUTSET:
+               tlcol = uicol[UICOL_LBEV];
+               brcol = uicol[UICOL_SBEV];
+               break;
+       case FRM_INSET:
+               tlcol = uicol[UICOL_SBEV];
+               brcol = uicol[UICOL_LBEV];
+               break;
+       case FRM_SOLID:
+       default:
+               tlcol = brcol = uicol[UICOL_BG];
+       }
+
+       for(i=0; i<sz; i++) {
+               if(r.width < 2 || r.height < 2) break;
+
+               hline(r.x, r.y, r.width, tlcol);
+               vline(r.x, r.y + 1, r.height - 2, tlcol);
+               hline(r.x, r.y + r.height - 1, r.width, brcol);
+               vline(r.x + r.width - 1, r.y + 1, r.height - 2, brcol);
+
+               r.x++;
+               r.y++;
+               r.width -= 2;
+               r.height -= 2;
+       }
+
+       if(fillbg) {
+               gfx.fill(&r, uicol[UICOL_BG]);
+       }
+}
+
+static void draw_window(rtk_widget *w)
+{
+       rtk_rect rect, frmrect, tbrect;
+       rtk_widget *c;
+       rtk_window *win = (rtk_window*)w;
+       int win_dirty = w->flags & DIRTY;
+
+       if(win_dirty) {
+               abs_widget_rect(w, &rect);
+
+               if(w->flags & FRAME) {
+                       if(w->flags & FOCUS) {
+                               uicolor(COL_WINFRM_FOCUS, COL_WINFRM_LIT_FOCUS, COL_WINFRM_SHAD_FOCUS);
+                       } else {
+                               uicolor(COL_WINFRM, COL_WINFRM_LIT, COL_WINFRM_SHAD);
+                       }
+
+                       frmrect = rect;
+                       frmrect.width += WINFRM_SZ * 2;
+                       frmrect.height += WINFRM_SZ * 2 + WINFRM_TBAR;
+                       frmrect.x -= WINFRM_SZ;
+                       frmrect.y -= WINFRM_SZ + WINFRM_TBAR;
+
+                       tbrect.x = rect.x;
+                       tbrect.y = rect.y - WINFRM_TBAR;
+                       tbrect.width = rect.width;
+                       tbrect.height = WINFRM_TBAR;
+
+                       draw_frame(&frmrect, FRM_OUTSET, 1);
+                       frmrect.x++;
+                       frmrect.y++;
+                       frmrect.width -= 2;
+                       frmrect.height -= 2;
+                       draw_frame(&frmrect, FRM_INSET, 1);
+
+                       draw_frame(&tbrect, FRM_OUTSET | FRM_FILLBG, 1);
+                       tbrect.x++;
+                       tbrect.y++;
+                       tbrect.width -= 2;
+                       tbrect.height -= 2;
+
+                       gfx.drawtext(tbrect.x, tbrect.y + tbrect.height - 1, w->text);
+               }
+
+               gfx.fill(&rect, COL_BG);
+       }
+
+       c = win->clist;
+       while(c) {
+               if(win_dirty) {
+                       rtk_invalidate(c);
+               }
+               rtk_draw_widget(c);
+               c = c->next;
+       }
+}
+
+static void draw_label(rtk_widget *w)
+{
+       rtk_rect rect;
+
+       abs_widget_rect(w, &rect);
+
+       gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+}
+
+static void draw_button(rtk_widget *w)
+{
+       int pressed;
+       rtk_rect rect;
+       rtk_button *bn = (rtk_button*)w;
+
+       abs_widget_rect(w, &rect);
+
+       if(bn->mode == RTK_TOGGLEBN) {
+               pressed = w->value;
+       } else {
+               pressed = w->flags & PRESS;
+       }
+
+       uicolor(w->flags & HOVER ? COL_BGHL : COL_BG, COL_LBEV, COL_SBEV);
+
+       draw_frame(&rect, (pressed ? FRM_INSET : FRM_OUTSET) | FRM_FILLBG, 1);
+       rect.x++;
+       rect.y++;
+       rect.width -= 2;
+       rect.height -= 2;
+
+       if(bn->icon) {
+               int offs = w->flags & PRESS ? PAD + 1 : PAD;
+               gfx.blit(rect.x + offs, rect.y + offs, bn->icon);
+       } else if(w->text) {
+               gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+       }
+}
+
+static void draw_checkbox(rtk_widget *w)
+{
+}
+
+static void draw_textbox(rtk_widget *w)
+{
+       rtk_rect rect;
+       rtk_textbox *tb = (rtk_textbox*)w;
+       int curx = 0;
+
+       abs_widget_rect(w, &rect);
+
+       uicolor(COL_TBOX, COL_LBEV, COL_SBEV);
+
+       draw_frame(&rect, FRM_INSET | FRM_FILLBG, w->flags & FOCUS ? 2 : 1);
+
+       rect.x++;
+       rect.y++;
+       rect.width -= 2;
+       rect.height -= 2;
+
+       if(w->text) {
+               gfx.drawtext(rect.x + PAD, rect.y + rect.height - PAD, w->text);
+
+               if(w->flags & FOCUS) {
+                       curx = calc_substr_width(w->text, tb->scroll, tb->cursor);
+               }
+       }
+
+       /* cursor */
+       if(w->flags & FOCUS) {
+               int x = rect.x + PAD + curx - 1;
+               int y = rect.y + rect.height - PAD - fontheight;
+               vline(x, y, fontheight, 0xff000000);
+       }
+
+       rtk_invalfb(w);
+}
+
+static void draw_slider(rtk_widget *w)
+{
+}
+
+static void draw_separator(rtk_widget *w)
+{
+       rtk_window *win = (rtk_window*)w->par;
+       rtk_rect rect;
+
+       if(!win) return;
+
+       uicolor(COL_BG, COL_LBEV, COL_SBEV);
+
+       abs_widget_rect(w, &rect);
+
+       switch(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, 1);
+}
+
+static void draw_drawbox(rtk_widget *w)
+{
+       if(!w->cbfunc) {
+               rtk_rect r;
+               abs_widget_rect(w, &r);
+               gfx.fill(&r, 0xff000000);
+               return;
+       }
+
+       w->cbfunc(w, w->cbcls);
+}