ref: a1a120547768ee33ddaae4ee1965dfcf85149d8a
dir: /sys/src/libcontrol/menu.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <thread.h> #include <mouse.h> #include <keyboard.h> #include <control.h> typedef struct Menu0 Menu0; /* Menu is taken by mouse.h */ struct Menu0 { Control; CImage *image; CImage *bordercolor; CImage *textcolor; CImage *selectcolor; CImage *selecttextcolor; CFont *font; char **line; int nline; int border; int align; Image *window; int visible; /* state of menu */ int selection; /* currently selected line; -1 == none */ int prevsel; /* previous selection */ int lastbut; /* previous state of mouse button */ }; enum{ EAdd, EAlign, EBorder, EBordercolor, EFocus, EFont, EFormat, EHide, EImage, ERect, EReveal, ESelectcolor, ESelecttextcolor, EShow, ESize, ETextcolor, EWindow, }; static char *cmds[] = { [EAdd] = "add", [EAlign] = "align", [EBorder] = "border", [EBordercolor] = "bordercolor", [EFocus] = "focus", [EFont] = "font", [EFormat] = "format", [EHide] = "hide", [EImage] = "image", [ERect] = "rect", [EReveal] = "reveal", [ESelectcolor] = "selectcolor", [ESelecttextcolor] = "selecttextcolor", [EShow] = "show", [ESize] = "size", [ETextcolor] = "textcolor", [EWindow] = "window", nil }; static void menushow(Menu0*); static void menuhide(Menu0*); static void menufree(Control *c) { Menu0 *m; m = (Menu0*)c; _putctlfont(m->font); _putctlimage(m->image); _putctlimage(m->textcolor); _putctlimage(m->bordercolor); _putctlimage(m->selectcolor); _putctlimage(m->selecttextcolor); } static void menushow(Menu0 *m) { Rectangle r, clipr; int i, dx, dy, w; Font *f; Point p, q; Image *im, *c; if(m->hidden || m->window == nil) return; m->visible = 1; f = m->font->font; draw(m->window, m->rect, m->image->image, nil, m->image->image->r.min); if(m->border > 0) border(m->window, m->rect, m->border, m->bordercolor->image, ZP); /* text goes here */ dx = 0; for(i=0; i<m->nline; i++){ w = stringwidth(f, m->line[i]); if(dx < w) dx = w; } dy = m->nline*f->height; clipr = insetrect(m->rect, m->border); p = _ctlalignpoint(clipr, dx, dy, m->align); im = m->textcolor->image; // if(m->pressed) // im = m->pressedtextcolor->image; for(i=0; i<m->nline; i++){ r.min = p; r.max.x = p.x+dx; r.max.y = p.y+f->height; c = im; if(i == m->selection){ draw(m->window, r, m->selectcolor->image, nil, ZP); c = m->selecttextcolor->image; } q = _ctlalignpoint(r, stringwidth(f, m->line[i]), f->height, m->align%3); _string(m->window, q, c, ZP, f, m->line[i], nil, strlen(m->line[i]), clipr, nil, ZP, SoverD); p.y += f->height; } // if(m->pressed) // draw(m->screen, m->rect, m->lighm->image, m->mask->image, m->mask->image->r.min); flushimage(display, 1); } static Point menusize(Menu0 *m) { int x, y; int i; Point p; Font *f; x = 0; y = 0; f = m->font->font; for(i=0; i<m->nline; i++){ p = stringsize(f, m->line[i]); if(p.x > x) x = p.x; y += f->height; } return Pt(x+2*m->border, y+2*m->border); } static void menuhide(Menu0 *m) { freeimage(m->window); m->window = nil; m->rect.max.y = m->rect.min.y; /* go to zero size */ m->lastbut = 0; m->visible = 0; if(m->selection >= 0) m->prevsel = m->selection; m->selection = -1; _ctlfocus(m, 0); } static void menutrack(Control *c, Mouse *ms) { Rectangle r; int s; Menu0 *m; m = (Menu0*)c; if(m->window == nil) return; if(m->lastbut && ms->buttons==0){ /* menu was released */ chanprint(m->event, "%q: value %d", m->name, m->selection); menuhide(m); return; } m->lastbut = ms->buttons; r = insetrect(m->rect, m->border); if(!ptinrect(ms->xy, r)) s = -1; else{ s = (ms->xy.y - r.min.y)/m->font->font->height; if(s < 0 || s >= m->nline) s = -1; } if(m->visible== 0 || s!=m->selection){ m->selection = s; menushow(m); } } static void menuctl(Control *c, CParse *cp) { int up, cmd, h; Rectangle r; Menu0 *m; Point diag; m = (Menu0*)c; cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); switch(cmd){ default: ctlerror("%q: unrecognized message '%s'", m->name, cp->str); break; case EAdd: _ctlargcount(m, cp, 2); m->line = ctlrealloc(m->line, (m->nline+1)*sizeof(char*)); m->line[m->nline++] = ctlstrdup(cp->args[1]); menushow(m); break; case EAlign: _ctlargcount(m, cp, 2); m->align = _ctlalignment(cp->args[1]); menushow(m); break; case EBorder: _ctlargcount(m, cp, 2); m->border = cp->iargs[1]; menushow(m); break; case EBordercolor: _ctlargcount(m, cp, 2); _setctlimage(m, &m->bordercolor, cp->args[1]); menushow(m); break; case EFocus: _ctlargcount(m, cp, 2); if(strtol(cp->args[1], 0, 0) == 0) menuhide(m); break; case EFont: _ctlargcount(m, cp, 2); _setctlfont(m, &m->font, cp->args[1]); break; case EFormat: _ctlargcount(m, cp, 2); m->format = ctlstrdup(cp->args[1]); break; case EHide: _ctlargcount(m, cp, 1); m->hidden = 1; break; case EImage: _ctlargcount(m, cp, 2); _setctlimage(m, &m->image, cp->args[1]); menushow(m); break; case ERect: _ctlargcount(m, cp, 5); r.min.x = cp->iargs[1]; r.min.y = cp->iargs[2]; r.max.x = cp->iargs[3]; r.max.y = cp->iargs[4]; if(Dx(r)<0 || Dy(r)<0) ctlerror("%q: bad rectangle: %s", m->name, cp->str); m->rect = r; menushow(m); break; case EReveal: _ctlargcount(m, cp, 1); m->hidden = 0; menushow(m); break; case ESelectcolor: _ctlargcount(m, cp, 2); _setctlimage(m, &m->selectcolor, cp->args[1]); menushow(m); break; case ESelecttextcolor: _ctlargcount(m, cp, 2); _setctlimage(m, &m->selecttextcolor, cp->args[1]); menushow(m); break; case EShow: _ctlargcount(m, cp, 1); menushow(m); break; case ESize: if (cp->nargs == 3) r.max = Pt(0x7fffffff, 0x7fffffff); else{ _ctlargcount(m, cp, 5); r.max.x = cp->iargs[3]; r.max.y = cp->iargs[4]; } r.min.x = cp->iargs[1]; r.min.y = cp->iargs[2]; if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y) ctlerror("%q: bad sizes: %s", m->name, cp->str); m->size.min = r.min; m->size.max = r.max; break; case ETextcolor: _ctlargcount(m, cp, 2); _setctlimage(m, &m->textcolor, cp->args[1]); menushow(m); break; case EWindow: /* no args == toggle; otherwise 0 or 1 for state of window */ if(cp->nargs >= 2) up = cp->iargs[1]; else up = (m->window == nil); if(!up){ /* take window down */ if(m->window) menuhide(m); break; } if(m->window != nil) break; diag = menusize(m); m->rect.max.x = m->rect.min.x + diag.x; m->rect.max.y = m->rect.min.y + diag.y; m->window = allocwindow(_screen, m->rect, Refbackup, DWhite); if(m->window == nil) m->window = m->screen; up = m->prevsel; if(up<0 || up>=m->nline) up = 0; m->selection = up; menushow(m); h = m->font->font->height; moveto(m->controlset->mousectl, Pt(m->rect.min.x+Dx(m->rect)/2, m->rect.min.y+up*h+h/2)); // _ctlfocus(m, 1); break; } } Control* createmenu(Controlset *cs, char *name) { Menu0 *m; m = (Menu0*)_createctl(cs, "menu", sizeof(Menu0), name); m->font = _getctlfont("font"); m->image = _getctlimage("white"); m->textcolor = _getctlimage("black"); m->selectcolor = _getctlimage("yellow"); m->selecttextcolor = _getctlimage("black"); m->bordercolor = _getctlimage("black"); m->format = ctlstrdup("%q: value %d"); m->border = 0; m->align = Aupperleft; m->visible = 0; m->window = nil; m->lastbut = 0; m->selection = -1; m->mouse = menutrack; m->ctl = menuctl; m->exit = menufree; return (Control *)m; }