mothra: tune up entry control logic for easier text editing
This commit is contained in:
parent
04bf70d344
commit
79cf39c53a
6 changed files with 200 additions and 70 deletions
|
@ -13,8 +13,8 @@
|
|||
#define CKWID 1 /* width of frame around check mark */
|
||||
#define CKINSET 1 /* space around check mark frame */
|
||||
#define CKBORDER 2 /* space around X inside frame */
|
||||
static Image *pl_white, *pl_light, *pl_dark, *pl_black, *pl_hilit;
|
||||
Image *pl_blue;
|
||||
static Image *pl_light, *pl_dark, *pl_tick, *pl_hilit;
|
||||
Image *pl_blue, *pl_white, *pl_black;
|
||||
int pl_drawinit(void){
|
||||
pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF);
|
||||
pl_light=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF);
|
||||
|
@ -22,7 +22,13 @@ int pl_drawinit(void){
|
|||
pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF);
|
||||
pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
|
||||
pl_blue=allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x0000FFFF);
|
||||
if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0 || pl_blue==0) sysfatal("allocimage: %r");
|
||||
if((pl_tick = allocimage(display, Rect(0, 0, TICKW, font->height), screen->chan, 0, DNofill)) != nil){
|
||||
draw(pl_tick, pl_tick->r, pl_white, nil, ZP);
|
||||
draw(pl_tick, Rect(TICKW/2, 0, TICKW/2+1, font->height), pl_black, nil, ZP);
|
||||
draw(pl_tick, Rect(0, 0, TICKW, TICKW), pl_black, nil, ZP);
|
||||
draw(pl_tick, Rect(0, font->height-TICKW, TICKW, font->height), pl_black, nil, ZP);
|
||||
}
|
||||
if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0 || pl_blue==0 || pl_tick==0) sysfatal("allocimage: %r");
|
||||
return 1;
|
||||
}
|
||||
Rectangle pl_boxoutline(Image *b, Rectangle r, int style, int fill){
|
||||
|
@ -227,6 +233,9 @@ Point pl_iconsize(int flags, Icon *p){
|
|||
void pl_highlight(Image *b, Rectangle r){
|
||||
draw(b, r, pl_dark, pl_hilit, ZP);
|
||||
}
|
||||
void pl_drawtick(Image *b, Rectangle r){
|
||||
draw(b, r, pl_tick, nil, ZP);
|
||||
}
|
||||
void pl_clr(Image *b, Rectangle r){
|
||||
draw(b, r, display->white, 0, ZP);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,25 @@
|
|||
|
||||
typedef struct Entry Entry;
|
||||
struct Entry{
|
||||
char *entry;
|
||||
char *entp;
|
||||
char *eent;
|
||||
Rectangle lastr;
|
||||
Rune *entry;
|
||||
char *sentry;
|
||||
int sz, n;
|
||||
int a, b;
|
||||
Point text;
|
||||
void (*hit)(Panel *, char *);
|
||||
Point minsize;
|
||||
};
|
||||
#define SLACK 7 /* enough for one extra rune and ◀ and a nul */
|
||||
void pl_cutentry(Panel *p){
|
||||
Entry *ep;
|
||||
|
||||
ep=p->data;
|
||||
memmove(ep->entry+ep->a, ep->entry+ep->b, (ep->n-ep->b)*sizeof(Rune));
|
||||
ep->n -= ep->b-ep->a;
|
||||
ep->entry[ep->n]=0;
|
||||
ep->b=ep->a;
|
||||
}
|
||||
char *pl_snarfentry(Panel *p){
|
||||
Entry *ep;
|
||||
int n;
|
||||
|
@ -22,72 +34,144 @@ char *pl_snarfentry(Panel *p){
|
|||
if(p->flags&USERFL) /* no snarfing from password entry */
|
||||
return nil;
|
||||
ep=p->data;
|
||||
n=utfnlen(ep->entry, ep->entp-ep->entry);
|
||||
n=ep->b-ep->a;
|
||||
if(n<1) return nil;
|
||||
return smprint("%.*s", n, ep->entry);
|
||||
return smprint("%.*S", n, ep->entry+ep->a);
|
||||
}
|
||||
void pl_pasteentry(Panel *p, char *s){
|
||||
Entry *ep;
|
||||
char *e;
|
||||
int n, m;
|
||||
int m;
|
||||
|
||||
ep=p->data;
|
||||
n=ep->entp-ep->entry;
|
||||
m=strlen(s);
|
||||
e=pl_erealloc(ep->entry,n+m+SLACK);
|
||||
ep->entry=e;
|
||||
e+=n;
|
||||
strncpy(e, s, m);
|
||||
e+=m;
|
||||
*e='\0';
|
||||
ep->entp=ep->eent=e;
|
||||
m=utflen(s);
|
||||
ep->sz=ep->n+m+100+SLACK;
|
||||
ep->entry=pl_erealloc(ep->entry,ep->sz*sizeof(Rune));
|
||||
memmove(ep->entry+ep->a+m, ep->entry+ep->b, (ep->n-ep->b)*sizeof(Rune));
|
||||
ep->n+=m-(ep->b-ep->a);
|
||||
while(m-- > 0)
|
||||
s += chartorune(&ep->entry[ep->a++], s);
|
||||
ep->b=ep->a;
|
||||
ep->entry[ep->n]=0;
|
||||
pldraw(p, p->b);
|
||||
}
|
||||
static void drawentry(Panel *p, Rectangle r, Rune *s){
|
||||
Rectangle save;
|
||||
Point tick;
|
||||
Entry *ep;
|
||||
Image *b;
|
||||
int d;
|
||||
|
||||
ep = p->data;
|
||||
b = p->b;
|
||||
|
||||
if(Dx(r) != Dx(ep->lastr)){
|
||||
ep->text = r.min;
|
||||
ep->lastr = r;
|
||||
}
|
||||
tick = ep->text;
|
||||
tick.x += runestringnwidth(font, s, ep->a);
|
||||
|
||||
if(plkbfocus == p)
|
||||
r.max.x -= TICKW;
|
||||
ep->text.y = r.min.y;
|
||||
if(!ptinrect(tick, r)){
|
||||
d = 0;
|
||||
if(tick.x < r.min.x)
|
||||
d = r.min.x - tick.x;
|
||||
else if(tick.x > r.max.x)
|
||||
d = r.max.x - tick.x;
|
||||
tick.x += d;
|
||||
ep->text.x += d;
|
||||
}
|
||||
if(plkbfocus == p)
|
||||
r.max.x += TICKW;
|
||||
|
||||
save = b->clipr;
|
||||
if(!rectclip(&r, save))
|
||||
return;
|
||||
replclipr(b, b->repl, r);
|
||||
runestring(b, ep->text, pl_black, ZP, font, s);
|
||||
if(plkbfocus == p){
|
||||
r.min = tick;
|
||||
if(ep->a != ep->b){
|
||||
r.max.x = ep->text.x+runestringnwidth(font, s, ep->b);
|
||||
if(r.max.x < r.min.x){
|
||||
d = r.min.x;
|
||||
r.min.x = r.max.x;
|
||||
r.max.x = d;
|
||||
}
|
||||
pl_highlight(b, r);
|
||||
}else
|
||||
pl_drawtick(b, r);
|
||||
}
|
||||
replclipr(b, b->repl, save);
|
||||
}
|
||||
void pl_drawentry(Panel *p){
|
||||
Rectangle r;
|
||||
Entry *ep;
|
||||
char *s;
|
||||
Rune *s;
|
||||
|
||||
ep=p->data;
|
||||
r=pl_box(p->b, p->r, p->state|BORDER);
|
||||
s=ep->entry;
|
||||
if(p->flags & USERFL){
|
||||
char *p;
|
||||
s=strdup(s);
|
||||
Rune *p;
|
||||
s=runestrdup(s);
|
||||
for(p=s; *p; p++)
|
||||
*p='*';
|
||||
}
|
||||
if(stringwidth(font, s)<=r.max.x-r.min.x)
|
||||
pl_drawicon(p->b, r, PLACEW, 0, s);
|
||||
else
|
||||
pl_drawicon(p->b, r, PLACEE, 0, s);
|
||||
drawentry(p, r, s);
|
||||
if(s != ep->entry)
|
||||
free(s);
|
||||
}
|
||||
int pl_hitentry(Panel *p, Mouse *m){
|
||||
Entry *ep;
|
||||
int i, n, selecting;
|
||||
if((m->buttons&7)==1){
|
||||
if(plkbfocus != p)
|
||||
p->state=DOWN;
|
||||
plgrabkb(p);
|
||||
|
||||
p->state=DOWN;
|
||||
ep = p->data;
|
||||
for(i = 1; i <= ep->n; i++)
|
||||
if(runestringnwidth(font, ep->entry, i) > m->xy.x-ep->text.x)
|
||||
break;
|
||||
n = i-1;
|
||||
ep->a = ep->b = n;
|
||||
pldraw(p, p->b);
|
||||
selecting = 1;
|
||||
while(m->buttons&1){
|
||||
int old;
|
||||
old=m->buttons;
|
||||
if(display->bufp > display->buf)
|
||||
flushimage(display, 1);
|
||||
*m=emouse();
|
||||
p->state=UP;
|
||||
if((old&7)==1){
|
||||
if((m->buttons&7)==3){
|
||||
Entry *ep;
|
||||
|
||||
plsnarf(p);
|
||||
|
||||
/* cut */
|
||||
ep=p->data;
|
||||
ep->entp=ep->entry;
|
||||
*ep->entp='\0';
|
||||
pl_cutentry(p);
|
||||
pldraw(p, p->b);
|
||||
ep->b = n = ep->a;
|
||||
}
|
||||
if(selecting && (m->buttons&7)==1){
|
||||
p->state=UP;
|
||||
for(i = 0; i < ep->n; i++)
|
||||
if(runestringnwidth(font, ep->entry, i)+TICKW > m->xy.x-ep->text.x)
|
||||
break;
|
||||
/*
|
||||
* tick is moved towards the mouse pointer dragging the selection
|
||||
* after drawing it has to be set so that (a <= b), since
|
||||
* the rest of the logic assumes that's always the case
|
||||
*/
|
||||
ep->a = i;
|
||||
ep->b = n;
|
||||
pldraw(p, p->b);
|
||||
if(ep->a > ep->b){
|
||||
ep->a = n;
|
||||
ep->b = i;
|
||||
}
|
||||
}else
|
||||
selecting = 0;
|
||||
if((m->buttons&7)==5)
|
||||
plpaste(p);
|
||||
}
|
||||
|
@ -98,45 +182,67 @@ int pl_hitentry(Panel *p, Mouse *m){
|
|||
return 0;
|
||||
}
|
||||
void pl_typeentry(Panel *p, Rune c){
|
||||
int n;
|
||||
Entry *ep;
|
||||
ep=p->data;
|
||||
switch(c){
|
||||
case '\n':
|
||||
case '\r':
|
||||
*ep->entp='\0';
|
||||
if(ep->hit) ep->hit(p, ep->entry);
|
||||
if(ep->hit) ep->hit(p, plentryval(p));
|
||||
return;
|
||||
case Kleft:
|
||||
if(ep->a > 0)
|
||||
ep->a--;
|
||||
ep->b=ep->a;
|
||||
break;
|
||||
case Kright:
|
||||
if(ep->a<ep->n)
|
||||
ep->a++;
|
||||
ep->b = ep->a;
|
||||
break;
|
||||
case Ksoh:
|
||||
ep->a=ep->b=0;
|
||||
break;
|
||||
case Kenq:
|
||||
ep->a=ep->b=ep->n;
|
||||
break;
|
||||
case Kesc:
|
||||
ep->a=0;
|
||||
ep->b=ep->n;
|
||||
plsnarf(p);
|
||||
/* no break */
|
||||
case Kdel: /* clear */
|
||||
ep->a = ep->b = ep->n = 0;
|
||||
*ep->entry = 0;
|
||||
break;
|
||||
case Knack: /* ^U: erase line */
|
||||
ep->entp=ep->entry;
|
||||
*ep->entp='\0';
|
||||
ep->a = 0;
|
||||
pl_cutentry(p);
|
||||
break;
|
||||
case Kbs: /* ^H: erase character */
|
||||
while(ep->entp!=ep->entry && !pl_rune1st(ep->entp[-1])) *--ep->entp='\0';
|
||||
if(ep->entp!=ep->entry) *--ep->entp='\0';
|
||||
break;
|
||||
if(ep->a > 0 && ep->a == ep->b)
|
||||
ep->a--;
|
||||
/* wet floor */
|
||||
if(0){
|
||||
case Ketb: /* ^W: erase word */
|
||||
while(ep->entp!=ep->entry && !pl_idchar(ep->entp[-1]))
|
||||
--ep->entp;
|
||||
while(ep->entp!=ep->entry && pl_idchar(ep->entp[-1]))
|
||||
--ep->entp;
|
||||
*ep->entp='\0';
|
||||
while(ep->a>0 && !pl_idchar(ep->entry[ep->a-1]))
|
||||
--ep->a;
|
||||
while(ep->a>0 && pl_idchar(ep->entry[ep->a-1]))
|
||||
--ep->a;
|
||||
}
|
||||
pl_cutentry(p);
|
||||
break;
|
||||
default:
|
||||
if(c < 0x20 || (c & 0xFF00) == KF || (c & 0xFF00) == Spec)
|
||||
break;
|
||||
ep->entp+=runetochar(ep->entp, &c);
|
||||
if(ep->entp>ep->eent){
|
||||
n=ep->entp-ep->entry;
|
||||
ep->entry=pl_erealloc(ep->entry, n+100+SLACK);
|
||||
ep->entp=ep->entry+n;
|
||||
ep->eent=ep->entp+100;
|
||||
memmove(ep->entry+ep->a+1, ep->entry+ep->b, (ep->n-ep->b)*sizeof(Rune));
|
||||
ep->n -= ep->b - ep->a - 1;
|
||||
ep->entry[ep->a++] = c;
|
||||
ep->b = ep->a;
|
||||
if(ep->n>ep->sz){
|
||||
ep->sz = ep->n+100;
|
||||
ep->entry=pl_erealloc(ep->entry, (ep->sz+SLACK)*sizeof(Rune));
|
||||
}
|
||||
*ep->entp='\0';
|
||||
ep->entry[ep->n]=0;
|
||||
break;
|
||||
}
|
||||
pldraw(p, p->b);
|
||||
|
@ -152,10 +258,11 @@ void pl_freeentry(Panel *p){
|
|||
Entry *ep;
|
||||
ep = p->data;
|
||||
free(ep->entry);
|
||||
ep->entry = ep->eent = ep->entp = 0;
|
||||
free(ep->sentry);
|
||||
ep->entry = nil;
|
||||
ep->sentry = nil;
|
||||
}
|
||||
void plinitentry(Panel *v, int flags, int wid, char *str, void (*hit)(Panel *, char *)){
|
||||
int elen;
|
||||
Entry *ep;
|
||||
ep=v->data;
|
||||
v->flags=flags|LEAF;
|
||||
|
@ -169,12 +276,11 @@ void plinitentry(Panel *v, int flags, int wid, char *str, void (*hit)(Panel *, c
|
|||
v->free=pl_freeentry;
|
||||
v->snarf=pl_snarfentry;
|
||||
v->paste=pl_pasteentry;
|
||||
elen=100;
|
||||
if(str) elen+=strlen(str);
|
||||
ep->entry=pl_erealloc(ep->entry, elen+SLACK);
|
||||
ep->eent=ep->entry+elen;
|
||||
strecpy(ep->entry, ep->eent, str ? str : "");
|
||||
ep->entp=ep->entry+strlen(ep->entry);
|
||||
ep->a = ep->b = 0;
|
||||
ep->n = str ? utflen(str) : 0;
|
||||
ep->sz = ep->n + 100;
|
||||
ep->entry=pl_erealloc(ep->entry, (ep->sz+SLACK)*sizeof(Rune));
|
||||
runesnprint(ep->entry, ep->sz, "%s", str ? str : "");
|
||||
ep->hit=hit;
|
||||
v->kind="entry";
|
||||
}
|
||||
|
@ -187,6 +293,7 @@ Panel *plentry(Panel *parent, int flags, int wid, char *str, void (*hit)(Panel *
|
|||
char *plentryval(Panel *p){
|
||||
Entry *ep;
|
||||
ep=p->data;
|
||||
*ep->entp='\0';
|
||||
return ep->entry;
|
||||
free(ep->sentry);
|
||||
ep->sentry = smprint("%S", ep->entry);
|
||||
return ep->sentry;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
#include "pldefs.h"
|
||||
|
||||
void plgrabkb(Panel *g){
|
||||
Panel *o;
|
||||
o=plkbfocus;
|
||||
plkbfocus=nil;
|
||||
if(o && o!=g) /* redraw if lost focus */
|
||||
pldraw(o, o->b);
|
||||
plkbfocus=g;
|
||||
}
|
||||
void plkeyboard(Rune c){
|
||||
|
|
|
@ -13,6 +13,7 @@ Rtext *pl_rthit(Rtext *, Point, Point, Point);
|
|||
#define LEAF 0x10000 /* newpanel will refuse to attach children */
|
||||
#define INVIS 0x20000 /* don't draw this */
|
||||
#define REMOUSE 0x40000 /* send next mouse event here, even if not inside */
|
||||
#define TICKW 3 /* tick width */
|
||||
/*
|
||||
* States, also styles
|
||||
*/
|
||||
|
@ -40,7 +41,7 @@ enum{
|
|||
SCROLLABSX,
|
||||
};
|
||||
|
||||
extern Image *pl_blue;
|
||||
extern Image *pl_blue, *pl_white, *pl_black;
|
||||
|
||||
/*
|
||||
* Scrollbar, slider orientations
|
||||
|
@ -70,6 +71,7 @@ void pl_sliderupd(Image *, Rectangle, int, int, int);
|
|||
void pl_invis(Panel *, int);
|
||||
Point pl_iconsize(int, Icon *);
|
||||
void pl_highlight(Image *, Rectangle);
|
||||
void pl_drawtick(Image *, Rectangle);
|
||||
void pl_clr(Image *, Rectangle);
|
||||
void pl_fill(Image *, Rectangle);
|
||||
void pl_cpy(Image *, Point, Rectangle);
|
||||
|
|
|
@ -399,6 +399,7 @@ void main(int argc, char *argv[]){
|
|||
case Ekeyboard:
|
||||
switch(e.kbdc){
|
||||
default:
|
||||
Plkey:
|
||||
adjkb();
|
||||
plkeyboard(e.kbdc);
|
||||
break;
|
||||
|
@ -424,9 +425,13 @@ void main(int argc, char *argv[]){
|
|||
search();
|
||||
break;
|
||||
case Kright:
|
||||
if(plkbfocus)
|
||||
goto Plkey;
|
||||
sidescroll(text->size.x/4, 1);
|
||||
break;
|
||||
case Kleft:
|
||||
if(plkbfocus)
|
||||
goto Plkey;
|
||||
sidescroll(-text->size.x/4, 1);
|
||||
break;
|
||||
}
|
||||
|
@ -441,6 +446,8 @@ void main(int argc, char *argv[]){
|
|||
break;
|
||||
}
|
||||
plmouse(root, &mouse);
|
||||
if(mouse.buttons == 1 && root->lastmouse == root)
|
||||
plgrabkb(nil);
|
||||
break;
|
||||
case Eplumb:
|
||||
pm=e.v;
|
||||
|
|
|
@ -374,7 +374,7 @@ void pl_rmentities(Hglob *, char *s){
|
|||
/*
|
||||
* Skip over white space
|
||||
*/
|
||||
char *pl_white(char *s){
|
||||
char *pl_whitespace(char *s){
|
||||
while(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') s++;
|
||||
return s;
|
||||
}
|
||||
|
@ -431,19 +431,19 @@ void pl_tagparse(Hglob *g, char *str){
|
|||
g->tag=tagp-tag;
|
||||
if(g->tag==Tag_end) htmlerror(g->name, g->lineno, "no tag %s", name);
|
||||
for(;;){
|
||||
s=pl_white(s);
|
||||
s=pl_whitespace(s);
|
||||
if(*s=='\0'){
|
||||
ap->name=0;
|
||||
return;
|
||||
}
|
||||
ap->name=s;
|
||||
s=pl_word(s);
|
||||
t=pl_white(s);
|
||||
t=pl_whitespace(s);
|
||||
c=*t;
|
||||
*s='\0';
|
||||
for(s=ap->name;*s;s++) if('A'<=*s && *s<='Z') *s+='a'-'A';
|
||||
if(c=='='){
|
||||
s=pl_white(t+1);
|
||||
s=pl_whitespace(t+1);
|
||||
if(*s=='\'' || *s=='"'){
|
||||
ap->value=s+1;
|
||||
s=pl_quote(s);
|
||||
|
|
Loading…
Reference in a new issue