diff --git a/sys/include/event.h b/sys/include/event.h index 03679f8f2..86e6ca6a4 100644 --- a/sys/include/event.h +++ b/sys/include/event.h @@ -64,3 +64,4 @@ extern Rectangle egetrect(int, Mouse*); extern void edrawgetrect(Rectangle, int); extern int ereadmouse(Mouse*); extern int eatomouse(Mouse*, char*, int); +extern int eenter(char*, char*, int, Mouse*); diff --git a/sys/include/mouse.h b/sys/include/mouse.h index 14d26de6d..5f91f11a3 100644 --- a/sys/include/mouse.h +++ b/sys/include/mouse.h @@ -4,6 +4,7 @@ typedef struct Channel Channel; typedef struct Cursor Cursor; typedef struct Menu Menu; typedef struct Mousectl Mousectl; +typedef struct Keyboardctl Keyboardctl; struct Mouse { @@ -44,3 +45,4 @@ extern void setcursor(Mousectl*, Cursor*); extern void drawgetrect(Rectangle, int); extern Rectangle getrect(int, Mousectl*); extern int menuhit(int, Mousectl*, Menu*, Screen*); +extern int enter(char *, char *, int, Mousectl*, Keyboardctl*, Screen*); diff --git a/sys/man/2/event b/sys/man/2/event index 11324a9af..8027f2b91 100644 --- a/sys/man/2/event +++ b/sys/man/2/event @@ -1,6 +1,6 @@ .TH EVENT 2 .SH NAME -event, einit, estart, estartfn, etimer, eread, emouse, ekbd, ecanread, ecanmouse, ecankbd, ereadmouse, eatomouse, eresized, egetrect, edrawgetrect, emenuhit, emoveto, esetcursor, Event, Mouse, Menu \- graphics events +event, einit, estart, estartfn, etimer, eread, emouse, ekbd, ecanread, ecanmouse, ecankbd, ereadmouse, eatomouse, eresized, egetrect, edrawgetrect, emenuhit, eenter, emoveto, esetcursor, Event, Mouse, Menu \- graphics events .SH SYNOPSIS .nf .PP @@ -69,15 +69,16 @@ void edrawgetrect(Rectangle r, int up) .B int emenuhit(int but, Mouse *m, Menu *menu) .PP -.PP .B int emoveto(Point p) .PP -.PP .B int esetcursor(Cursor *c) .PP .B +int eenter(char *ask, char *buf, int len, Mouse *m) +.PP +.B extern Mouse *mouse .PP .B @@ -374,6 +375,35 @@ changes the cursor image to that described by the If .B c is nil, it restores the image to the default arrow. +.PP +.I Eenter +provides a simple way of text input in graphical programs. It displays +a box at the current position of the mouse cursor (passed in the +.B Mouse *m +argument) in wich text can be +typed and edited. +If the string argument +.B ask +is not +.B nil, +it is displayed as a static label before the input string. +The +.B buf +parameter contains the null-terminated input string to be edited. The +.B len +argument specified the length of +.B buf +in bytes including the terminating null byte. +If +.B buf +or +.B len +is zero, no text can be entered. +On success, +.I eenter +returns the number of bytes in the edited string +.B buf +or -1 on error. .SH SOURCE .B /sys/src/libdraw .SH "SEE ALSO" diff --git a/sys/man/2/mouse b/sys/man/2/mouse index 76625b683..72981dd50 100644 --- a/sys/man/2/mouse +++ b/sys/man/2/mouse @@ -1,6 +1,6 @@ .TH MOUSE 2 .SH NAME -initmouse, readmouse, closemouse, moveto, getrect, drawgetrect, menuhit, setcursor \- mouse control +initmouse, readmouse, closemouse, moveto, getrect, drawgetrect, menuhit, setcursor, enter \- mouse control .SH SYNOPSIS .nf .B @@ -42,6 +42,11 @@ void drawgetrect(Rectangle r, int up) .PP .B int menuhit(int but, Mousectl *mc, Menu *menu, Screen *scr) +.PP +.B +int enter(char *ask, char *buf, int len, +.B + Mousectl *mc, Keyboardctl *kc, Screen *scr) .fi .SH DESCRIPTION These functions access and control a mouse in a multi-threaded environment. @@ -239,6 +244,20 @@ behaves like creating backing store for the menu, writing the menu directly on the display, and restoring the display when the menu is removed. .PP +.I Enter +is a multithreded version of the +.I eenter +function described in +.IR event(2). +Like +.I menuhit, +it has a optional +.B scr +argument to create a window. Keyboard input is read from the channel in the +.B Keyboardctl *kc +argument (see +.IR keyboard (2)). +.PP .SH SOURCE .B /sys/src/libdraw .SH SEE ALSO diff --git a/sys/src/libdraw/eenter.c b/sys/src/libdraw/eenter.c new file mode 100644 index 000000000..d1434eacc --- /dev/null +++ b/sys/src/libdraw/eenter.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +int +eenter(char *ask, char *buf, int len, Mouse *m) +{ + int done, down, tick, n, h, w, l, i; + Image *b, *save, *backcol, *bordcol; + Point p, o, t; + Rectangle r; + Event ev; + Rune k; + + o = screen->r.min; + backcol = allocimagemix(display, DPurpleblue, DWhite); + bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); + if(backcol == nil || bordcol == nil) + return -1; + + while(ecankbd()) + ekbd(); + + if(m) o = m->xy; + + if(buf && len > 0) + n = strlen(buf); + else { + buf = nil; + len = 0; + n = 0; + } + + k = -1; + b = screen; + tick = n; + save = nil; + done = down = 0; + + p = stringsize(font, " "); + h = p.y; + w = p.x; + + while(!done){ + p = stringsize(font, buf ? buf : ""); + if(ask && ask[0]){ + if(buf) p.x += w; + p.x += stringwidth(font, ask); + } + r = rectaddpt(insetrect(Rpt(ZP, p), -4), o); + p.x = 0; + r = rectsubpt(r, p); + + p = ZP; + if(r.min.x < screen->r.min.x) + p.x = screen->r.min.x - r.min.x; + if(r.min.y < screen->r.min.y) + p.y = screen->r.min.y - r.min.y; + r = rectaddpt(r, p); + p = ZP; + if(r.max.x > screen->r.max.x) + p.x = r.max.x - screen->r.max.x; + if(r.max.y > screen->r.max.y) + p.y = r.max.y - screen->r.max.y; + r = rectsubpt(r, p); + + r = insetrect(r, -2); + if(save == nil){ + save = allocimage(display, r, b->chan, 0, DNofill); + if(save == nil){ + n = -1; + break; + } + draw(save, r, b, nil, r.min); + } + draw(b, r, backcol, nil, ZP); + border(b, r, 2, bordcol, ZP); + p = addpt(r.min, Pt(6, 6)); + if(ask && ask[0]){ + p = string(b, p, bordcol, ZP, font, ask); + if(buf) p.x += w; + } + if(buf){ + t = p; + p = stringn(b, p, display->black, ZP, font, buf, utfnlen(buf, tick)); + draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), display->black, nil, ZP); + draw(b, Rect(p.x, p.y, p.x+1, p.y+h), display->black, nil, ZP); + draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), display->black, nil, ZP); + p = string(b, p, display->black, ZP, font, buf+tick); + } + flushimage(display, 1); + + i = Ekeyboard; + if(m != nil) + i |= Emouse; + switch(eread(i, &ev)){ + default: + done = 1; + n = -1; + break; + case Ekeyboard: + k = ev.kbdc; + if(buf == nil || k == Keof || k == '\n'){ + done = 1; + break; + } + if(k == Knack || k == Kesc){ + done = !n; + buf[n = tick = 0] = 0; + break; + } + if(k == Ksoh || k == Khome){ + tick = 0; + continue; + } + if(k == Kenq || k == Kend){ + tick = n; + continue; + } + if(k == Kright){ + if(tick < n) + tick += chartorune(&k, buf+tick); + continue; + } + if(k == Kleft){ + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+tick); + if(i+l >= tick){ + tick = i; + break; + } + } + continue; + } + if(k == Kbs){ + if(tick <= 0) + continue; + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+i); + if(i+l >= tick){ + memmove(buf+i, buf+i+l, n - (i+l)); + buf[n -= l] = 0; + tick -= l; + break; + } + } + break; + } + if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec) + continue; + if((len-n) <= (l = runelen(k))) + continue; + memmove(buf+tick+l, buf+tick, n - tick); + runetochar(buf+tick, &k); + buf[n += l] = 0; + tick += l; + break; + case Emouse: + *m = ev.mouse; + if(!ptinrect(m->xy, r)){ + down = 0; + continue; + } + if(m->buttons & 7){ + down = 1; + if(buf && m->xy.x >= (t.x - w)){ + down = 0; + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+i); + t.x += stringnwidth(font, buf+i, 1); + if(t.x > m->xy.x) + break; + } + tick = i; + } + continue; + } + done = down; + break; + } + + draw(b, save->r, save, nil, save->r.min); + freeimage(save); + save = nil; + } + + freeimage(backcol); + freeimage(bordcol); + flushimage(display, 1); + + return n; +} + diff --git a/sys/src/libdraw/enter.c b/sys/src/libdraw/enter.c new file mode 100644 index 000000000..54c7ba702 --- /dev/null +++ b/sys/src/libdraw/enter.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include + +int +enter(char *ask, char *buf, int len, Mousectl *mc, Keyboardctl *kc, Screen *scr) +{ + int done, down, tick, n, h, w, l, i; + Image *b, *save, *backcol, *bordcol; + Point p, o, t; + Rectangle r; + Alt a[3]; + Mouse m; + Rune k; + + o = screen->r.min; + backcol = allocimagemix(display, DPurpleblue, DWhite); + bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); + if(backcol == nil || bordcol == nil) + return -1; + + n = 0; + if(kc){ + while(nbrecv(kc->c, nil) == 1) + ; + a[n].op = CHANRCV; + a[n].c = kc->c; + a[n].v = &k; + n++; + } + if(mc){ + o = mc->xy; + a[n].op = CHANRCV; + a[n].c = mc->c; + a[n].v = &m; + n++; + } + a[n].op = CHANEND; + a[n].c = nil; + a[n].v = nil; + + if(buf && len > 0) + n = strlen(buf); + else { + buf = nil; + len = 0; + n = 0; + } + + k = -1; + b = nil; + tick = n; + save = nil; + done = down = 0; + + p = stringsize(font, " "); + h = p.y; + w = p.x; + + while(!done){ + p = stringsize(font, buf ? buf : ""); + if(ask && ask[0]){ + if(buf) p.x += w; + p.x += stringwidth(font, ask); + } + r = rectaddpt(insetrect(Rpt(ZP, p), -4), o); + p.x = 0; + r = rectsubpt(r, p); + + p = ZP; + if(r.min.x < screen->r.min.x) + p.x = screen->r.min.x - r.min.x; + if(r.min.y < screen->r.min.y) + p.y = screen->r.min.y - r.min.y; + r = rectaddpt(r, p); + p = ZP; + if(r.max.x > screen->r.max.x) + p.x = r.max.x - screen->r.max.x; + if(r.max.y > screen->r.max.y) + p.y = r.max.y - screen->r.max.y; + r = rectsubpt(r, p); + + r = insetrect(r, -2); + if(scr){ + if(b == nil) + b = allocwindow(scr, r, Refbackup, DWhite); + if(b == nil) + scr = nil; + } + if(scr == nil && save == nil){ + if(b == nil) + b = screen; + save = allocimage(display, r, b->chan, 0, DNofill); + if(save == nil){ + n = -1; + break; + } + draw(save, r, b, nil, r.min); + } + draw(b, r, backcol, nil, ZP); + border(b, r, 2, bordcol, ZP); + p = addpt(r.min, Pt(6, 6)); + if(ask && ask[0]){ + p = string(b, p, bordcol, ZP, font, ask); + if(buf) p.x += w; + } + if(buf){ + t = p; + p = stringn(b, p, display->black, ZP, font, buf, utfnlen(buf, tick)); + draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), display->black, nil, ZP); + draw(b, Rect(p.x, p.y, p.x+1, p.y+h), display->black, nil, ZP); + draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), display->black, nil, ZP); + p = string(b, p, display->black, ZP, font, buf+tick); + } + flushimage(display, 1); + + switch(alt(a)){ + case -1: + done = 1; + n = -1; + break; + case 0: + if(buf == nil || k == Keof || k == '\n'){ + done = 1; + break; + } + if(k == Knack || k == Kesc){ + done = !n; + buf[n = tick = 0] = 0; + break; + } + if(k == Ksoh || k == Khome){ + tick = 0; + continue; + } + if(k == Kenq || k == Kend){ + tick = n; + continue; + } + if(k == Kright){ + if(tick < n) + tick += chartorune(&k, buf+tick); + continue; + } + if(k == Kleft){ + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+tick); + if(i+l >= tick){ + tick = i; + break; + } + } + continue; + } + if(k == Kbs){ + if(tick <= 0) + continue; + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+i); + if(i+l >= tick){ + memmove(buf+i, buf+i+l, n - (i+l)); + buf[n -= l] = 0; + tick -= l; + break; + } + } + break; + } + if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec) + continue; + if((len-n) <= (l = runelen(k))) + continue; + memmove(buf+tick+l, buf+tick, n - tick); + runetochar(buf+tick, &k); + buf[n += l] = 0; + tick += l; + break; + case 1: + if(!ptinrect(m.xy, r)){ + down = 0; + continue; + } + if(m.buttons & 7){ + down = 1; + if(buf && m.xy.x >= (t.x - w)){ + down = 0; + for(i = 0; i < n; i += l){ + l = chartorune(&k, buf+i); + t.x += stringnwidth(font, buf+i, 1); + if(t.x > m.xy.x) + break; + } + tick = i; + } + continue; + } + done = down; + break; + } + + if(b != screen) { + freeimage(b); + b = nil; + } else { + draw(b, save->r, save, nil, save->r.min); + freeimage(save); + save = nil; + } + } + + freeimage(backcol); + freeimage(bordcol); + flushimage(display, 1); + + return n; +} diff --git a/sys/src/libdraw/mkfile b/sys/src/libdraw/mkfile index 86f275bd0..0a70c3a48 100644 --- a/sys/src/libdraw/mkfile +++ b/sys/src/libdraw/mkfile @@ -18,9 +18,11 @@ OFILES=\ defont.$O\ draw.$O\ drawrepl.$O\ + eenter.$O\ egetrect.$O\ ellipse.$O\ emenuhit.$O\ + enter.$O\ event.$O\ fmt.$O\ font.$O\