swk

static widget kit
git clone git://git.suckless.org/swk
Log | Files | Refs | README | LICENSE

commit c3e152642868ef546b1edfcc2e1c5c4ac70dab40
parent cac5b29726e7921c332a8428fad7d141d30fdfa8
Author: pancake <pancake@nopcode.org>
Date:   Wed, 25 Aug 2010 05:00:20 +0200

initial implementation of multiline input widget
swk_text is the widget to enter multiple lines of text
supports scrolling, cursor and basic text manipulation
filter keycodes at backend level on x11 and sdl
add support for 5 button mouse to gi_x11
add animation between screens in t/test.c
enter key no longers activates the widget, press control+enter
Diffstat:
Makefile | 8++++----
config.def.h | 2+-
gi_sdl.c | 12++++++++++--
gi_x11.c | 37+++++++++++++++++++++++++++++++++----
swk.c | 44+++++++++++++++++++++++++++++---------------
swk.h | 37+++++++++++++++++++++++++++++++++++--
t/test.c | 40+++++++++++++++++++++++++++++++++++++++-
text.c | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 443 insertions(+), 29 deletions(-)

diff --git a/Makefile b/Makefile @@ -38,7 +38,7 @@ config.mk: config.h clean: echo >swk.mk cd t && ${MAKE} clean - rm -f swk.pc swk.mk libswk.a libswk.so swk.o ${GI_OBJS} + rm -f swk.pc swk.mk libswk.a libswk.so swk.o text.o ${GI_OBJS} install: mkdir -p ${DESTDIR}/${INCDIR} @@ -62,14 +62,14 @@ static: libswk.a shared: libswk.so -libswk.so: config.mk swk.o ${GI_OBJS} +libswk.so: config.mk swk.o text.o ${GI_OBJS} ${CC} ${CFLAGS} -fPIC -shared swk.c ${GI_SRCS} -o libswk.so swk.o: config.mk -libswk.a: config.mk swk.o ${GI_OBJS} +libswk.a: config.mk swk.o text.o ${GI_OBJS} rm -f libswk.a - ar qcvf libswk.a swk.o ${GI_OBJS} + ar qcvf libswk.a text.o swk.o ${GI_OBJS} echo SWKINCS+=-I${PREFIX}/include > swk.mk echo SWKLIB+=${PREFIX}/lib/libswk.a >> swk.mk echo SWKLIBS+=${GI_LIBS} >> swk.mk diff --git a/config.def.h b/config.def.h @@ -22,7 +22,7 @@ /* key bindings */ static SwkKeyBind keys[] = { - { 0, '\n', swk_focus_activate}, + { Ctrl, '\n', swk_focus_activate}, { Ctrl, 'j', swk_focus_next }, { Ctrl, 'k', swk_focus_prev }, //{ Ctrl, 8 , swk_focus_first }, diff --git a/gi_sdl.c b/gi_sdl.c @@ -171,7 +171,6 @@ swk_gi_event(SwkWindow *w, int dowait) { break; case SDL_MOUSEBUTTONUP: //fprintf(stderr, "event: up %d (%d,%d)\n", event.button.button,event.button.x,event.button.y); - mousedown = 0; if(!mousemoved) { ret->type = EClick; @@ -199,8 +198,17 @@ swk_gi_event(SwkWindow *w, int dowait) { ret->data.key.modmask |= Meta; if(ret->data.key.keycode != 0 && event.key.keysym.unicode != 0) { ret->data.key.keycode = event.key.keysym.unicode; - } else // TODO key aliases defined in config.h + } switch((int)event.key.keysym.sym) { + case 13: + ret->data.key.keycode = '\n'; + break; + case 275: + ret->data.key.keycode = KRight; + break; + case 276: + ret->data.key.keycode = KLeft; + break; case 1073741906: // n900 up key case 273: ret->data.key.keycode = KUp; diff --git a/gi_x11.c b/gi_x11.c @@ -13,8 +13,8 @@ #define SWK #include "config.h" -#define FONTNAME "-*-*-medium-*-*-*-14-*-*-*-*-*-*-*" -//#define FONTNAME "10x20" +//#define FONTNAME "-*-*-medium-*-*-*-14-*-*-*-*-*-*-*" +#define FONTNAME "10x20" static int fs = FONTSIZE; // TODO: we need fsW and fsH static Window window; @@ -127,7 +127,24 @@ swk_gi_event(SwkWindow *w, int dowait) { mousedown = 0; if(!mousemoved) { ret->type = EClick; - ret->data.click.button = event.xbutton.state; + switch(event.xbutton.state) { + case 4096: + ret->data.click.button = 4; + break; + case 2048: + ret->data.click.button = 5; + break; + case 1024: + ret->data.click.button = 2; + break; + case 512: + ret->data.click.button = 3; + break; + case 256: + ret->data.click.button = 1; + break; + } +printf ("STATE=%d\n", event.xbutton.state); ret->data.click.point.x = event.xbutton.x / fs; ret->data.click.point.y = event.xbutton.y / fs; } @@ -144,6 +161,12 @@ swk_gi_event(SwkWindow *w, int dowait) { printf("ksym=%d\n", (int)ksym); switch(ksym) { + case 65535: // supr + ret->data.key.keycode = 127; + break; + case 65511: + ret->data.key.keycode = KUp; + break; case 65362: ret->data.key.keycode = KUp; break; @@ -220,7 +243,8 @@ swk_gi_fill(Rect r, int color, int lil) { } else if(lil==2) { area.x/=3; - area.width/=4; + area.x-=2; + area.width=2;///=4; area.y+=4; area.height-=4; } else if (lil==3) { @@ -247,6 +271,11 @@ swk_gi_text(Rect r, const char *text) { if(!text||!*text) return; XSetForeground(dc->dpy, dc->gc, col[ColorFG]); +// XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, strlen(text)); + if(dc->font.xfont) + XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); + //printf("## %d\n", dc->font.xfont); + //XmbDrawString(dc->dpy, dc->canvas, dc->font.set, 5+r.x*fs, ((1+r.y)*fs)-3, text, strlen (text)); XDrawString(dc->dpy, dc->canvas, dc->gc, 5+r.x*fs, ((1+r.y)*fs)-3, text, strlen (text)); } diff --git a/swk.c b/swk.c @@ -71,7 +71,8 @@ swk_update() { SwkBox *b = w->boxes[0]; swk_fit(w); swk_gi_clear(); - if(!w->colpos) { + //if(!w->colpos) { + if(w->colpos<2) { b = w->boxes[1]; count--; col = w->r.w; @@ -347,13 +348,20 @@ swk_focus_prev() { void swk_label(SwkEvent *e) { + char text[128]; // XXX + int cut, len; Rect r; switch(e->type) { case EExpose: r = e->box->r; - r.w += 6; - swk_gi_text(r, e->box->text); - r.w -= 6; + r.w += 4; + cut = r.w*2; + strncpy(text, e->box->text, sizeof(text)-1); + len = strlen(text); + if (len>cut) + text[cut]=0; + swk_gi_text(r, text); + r.w -= 4; if(e->win->box == e->box) swk_gi_line(r.x, r.y+1, r.w, 0, ColorHI); break; @@ -422,18 +430,24 @@ swk_entry(SwkEvent *e) { break; case EExpose: // XXX: add support for cursor (handle arrow keys) - #ifdef USE_SDL - len = 4*e->box->r.x; - len += 2*strlen(e->box->text)+1; - #else - len = 3*e->box->r.x; - len += strlen(e->box->text)+1; - #endif swk_gi_fill(e->box->r, ColorBG, 1); swk_label(e); - { - Rect r = {len, e->box->r.y, 1, 1 }; - swk_gi_fill(r, ColorFG, 2); + /* cursor */ { + int cut = e->box->r.w*2; + #ifdef USE_SDL + len = 4*e->box->r.x; + #else + len = 3*e->box->r.x; + #endif + if (strlen(e->box->text)>cut) + len += cut; + #ifdef USE_SDL + else len += 2*strlen(e->box->text)+1; + #else + else len += strlen(e->box->text)+1; + #endif + Rect r = { len, e->box->r.y, 1, 1 }; + swk_gi_fill(r, ColorFG, 2); } break; } @@ -565,7 +579,7 @@ swk_image(SwkEvent *e) { if(e->box->data == NULL) { e->box->data = swk_gi_img_load(e->box->text); if(!e->box->data) - fprintf(stderr, "Cannot find image %s\n", e->box->text); + fprintf(stderr, "Cannot open image %s\n", e->box->text); } switch(e->type) { case EExpose: diff --git a/swk.h b/swk.h @@ -8,7 +8,7 @@ typedef enum { EVoid, EClick, EMotion, EKey, EExpose, EQuit, ELast } SwkEventType; typedef enum { Shift=1, Ctrl=2, Alt=4, Meta=8 } SwkKeyMod; typedef enum { ColorFG, ColorBG, ColorHI, ColorTF, ColorCC, ColorLast } Palete; -typedef enum { KUp=0xe0, KDown=0xe1, KLeft=0xe2, KRight=0xe3 } SwkKeyCode; +typedef enum { KDel=8, KSupr=127, KUp=0xe0, KDown=0xe1, KLeft=0xe2, KRight=0xe3 } SwkKeyCode; typedef struct SwkBox SwkBox; typedef struct SwkEvent SwkEvent; @@ -124,7 +124,7 @@ void swk_gi_flip(); void swk_gi_line(int x1, int y1, int x2, int y2, int color); void swk_gi_fill(Rect r, int color, int lil); void swk_gi_rect(Rect r, int color); -void swk_gi_text(Rect r, const char *text); +void swk_gi_text(Rect r, const char *t); /* images */ void swk_gi_img(Rect r, void *img); @@ -133,3 +133,36 @@ void* swk_gi_img_load(const char *str); void swk_gi_img_free(void *s); void swk_gi_img_set(void *img, int x, int y, int color); int swk_gi_img_get(void *img, int x, int y); + +/* text.c */ +typedef struct { + const char *otext; + int cur; + int xcur; + int ycur; + char *text; + int len; + int size; + int yscroll; + int sel[2]; + int selmode; +} Text; + +int text_rowcount(Text *t); +int text_rowoff(Text *t, int row); +int text_rowcol(Text *t, int off, int *col); +int text_off(Text *t, int col, int row); +char * text_sub(Text *t, int col, int row, int rows); +void text_init(Text *t, const char *text); +void text_set(Text *t, const char *text); +char * text_get(Text *t, int from, int to); +void text_sync(Text *t); +void text_cur(Text *t, int num, int dir); +void text_ins(Text *t, const char *str, int app); +void text_insc(Text *t, char ch, int app); +void text_del(Text *t, int num, int dir); +void text_sel(Text *t, int begin, int end); +void text_sel_mode(Text *t, int enable); + +/* text.c widgets */ +void swk_text(SwkEvent *e); diff --git a/t/test.c b/t/test.c @@ -63,6 +63,28 @@ static SwkBox about[] = { { .cb=NULL } }; +static void animate(SwkWindow *w, int s) { + int i; + if(w->colpos<1) + w->colpos = 1; + if(s<0 && w->colpos>w->r.w) + w->colpos=w->r.w; + s*=(w->r.w/20); + for(i=0;i<100;i++) { + if (w->colpos<1) { + w->colpos=0; + break; + } + if (w->colpos>w->r.w) { + w->colpos=w->r.w; + break; + } + w->colpos+=s; + swk_update(w); + usleep(10000); + } + w->col=(w->colpos>0)?1:0; +} static void mybutton_about_ok(SwkEvent *e) { if(e->type == EClick) { e->win->boxes[e->win->col] = helloworld; @@ -73,12 +95,21 @@ static void mybutton_about_ok(SwkEvent *e) { static void mybutton_about(SwkEvent *e) { if(e->type == EClick) { + animate(e->win, -1); e->win->boxes[e->win->col] = about; swk_update(e->win); } swk_button(e); } +static void mybutton_shrink(SwkEvent *e) { + if(e->type == EClick) { + animate(e->win, 1); + swk_update(e->win); + } + swk_button(e); +} + static void mybutton_close(SwkEvent *e) { if(e->type == EClick) { e->win->boxes[e->win->col] = helloworld; @@ -111,6 +142,8 @@ static void mybutton_numscroll(SwkEvent *e) { swk_button(e); } +Text txt = {"Hello\nworld\n"}; + static SwkBox helloworld[] = { { .cb=swk_label, .text="Press a button", }, SWK_BOX_NEWLINE(1), @@ -125,6 +158,10 @@ static SwkBox helloworld[] = { { .cb=swk_label, .text="Click here ->" }, { .cb=swk_sketch }, SWK_BOX_NEWLINE(2), + { .cb=swk_label, .text="multiline:" }, + { .cb=swk_text, .text="Hello\nWorld\n", .data=&txt, .r.h=4, .r.w=10 }, + { .cb=swk_filler, }, + SWK_BOX_NEWLINE(1), { .cb=swk_image, .text="image.png" }, { .cb=swk_image, .text="image.png" }, { .cb=swk_image, .text="image.png" }, @@ -151,7 +188,8 @@ static SwkBox helloworld[] = { static SwkBox column[] = { { .cb=swk_label, .text="this is a column", }, SWK_BOX_NEWLINE(-1), - { .cb=swk_label, .text="text in column", }, + { .cb=mybutton_shrink, .text="shrink", }, + { .cb=swk_label, .text="hide this column", }, { .cb=NULL } }; diff --git a/text.c b/text.c @@ -0,0 +1,292 @@ +/* See LICENSE file for copyright and license details. */ +#define _BSD_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <swk.h> + +int +text_rowcount(Text *t) { + int rows = 1; + char *p; + for(p=t->text;*p;p++) + if(*p=='\n') + rows++; + return rows; +} + +int +text_rowoff(Text *t, int row) { + char *p; + int off = 0; + if(row<1) + return 0; + for(p=t->text;row&&*p;p++) { + if(*p=='\n') + row--; + off++; + } + return off; +} + +int +text_rowcol(Text *t, int off, int *col) { + char *p,*nl = NULL; + char *e = t->text+off; + int r = 0; + int c = 0; + for(p=t->text;*p&&p<=e;p++) { + if(*p=='\n') { + r++; + c = 0; + nl = p+1; + } else c++; + } + if(col) *col = c; + return r; +} +int +text_off(Text *t, int col, int row) { + int off = text_rowoff(t, row); + int len = strlen (t->text); + if (col>len) + col = len; + return off+(t->text[off])?col:0; +} + +char * +text_sub(Text *t, int col, int row, int rows) { + // find row number N in text + // +=col if < \n + // count N rows and \0 + return NULL; +} + +void +text_init(Text *t, const char *str) { + if(!t->text) { + t->len = strlen (t->otext); + t->size = (t->len+1)*2; + t->text = malloc (t->size); + if (str) strcpy(t->text, t->otext); + else strcpy(t->text, t->otext); + } +} + +void +text_set(Text *t, const char *text) { + int len = strlen (text); + if(t->text) { + if(len>t->size) { + t->size = (len+10)*2; + t->text = realloc(t->text, t->size); + } + strcpy(t->text, text); + } else text_init(t, text); + text_sync(t); +} + +char * +text_get(Text *t, int from, int to) { + /* get buffer between from and to offsets */ + char *p; + if(to!=-1&&(to<from || from>t->len)) + return strdup (""); + if(to>t->len||to==-1) + to = t->len; + if (!t->text) + if (t->otext) + t->text = t->otext; + else t->text = ""; + p = strdup (t->text+from); + //if(to!=-1) p[to-from] = '\0'; + return p; +} + +void +text_sync(Text *t) { + // count cols, rows, fix xcur, ycur from cur, etc.. + t->len = strlen(t->text); + t->ycur = text_rowcol(t, t->cur, &t->xcur); +} + +void +text_cur(Text *t, int num, int dir) { + if(dir) t->cur += (num*dir); + else t->cur = num; + if(t->cur<0) + t->cur = 0; + if(t->cur>t->len) + t->cur = t->len; + if(t->text[t->cur]=='\n') + t->cur+=dir; +} + +void +text_resize(Text *t, int newsize) { + if (newsize>t->size) { + t->text = realloc(t->text, newsize); + t->size = newsize; + } +} + +void +text_insc(Text *t, char ch, int app) { + char str[2] = {ch}; + text_ins(t, str, app); +} + +void +text_ins(Text *t, const char *str, int app) { + int len = strlen(str); + text_resize(t, (2*len)+t->size); + if(app) { + char *tmp = strdup(t->text+t->cur); + strcpy(t->text+t->cur, str); + strcat(t->text+t->cur+len, tmp); + free(tmp); + } else memcpy(t->text+t->cur, str, len); + t->cur += len; +} + +void +text_del(Text *t, int num, int dir) { + switch(dir) { + case -1: + if(t->cur-num<0) + num = 0; + strcpy(t->text+t->cur-num, t->text+t->cur); + t->cur -= num; + break; + case 0: + t->text[num] = 0; + t->len = strlen (t->text); + if (t->cur>t->len) + t->cur = t->len; + break; + case 1: + if (t->cur+num>=t->len) + num = t->len-t->cur; + strcpy(t->text+t->cur, t->text+t->cur+num); + break; + } + text_sync(t); +} + +void +text_sel(Text *t, int begin, int end) { // if off != off2 text is selected + t->sel[0] = begin; + t->sel[1] = end; +} + +void +text_sel_mode(Text *t, int enable) { + t->selmode = enable; +} + +/* swk widget */ +void +swk_text(SwkEvent *e) { + Text *t = (Text*)e->box->data; + int row, len, key; + char *ptr; + + text_init(e->box->data, e->box->text); + text_sync(e->box->data); + switch(e->type) { + case EClick: + // TODO: text_sel// + switch(e->data.click.button) { + case 1: + //text_sel_mode(1) + break; + case 2: + // activate link (TODO: implement hyperlinks) + break; + case 3: + //text_sel_mode(0) + break; + } + break; + case EKey: + key = e->data.key.keycode; + if(e->data.key.modmask&Ctrl) + return; + if(key == KDel) + text_del(e->box->data, 1, -1); + else if(key == KSupr) + text_del(e->box->data, 1, 1); + else if(key=='\n'||(key>=' '&&key<='~')) + text_insc(e->box->data, key, 1); + else if(key==KUp) + text_cur(e->box->data, 10, -1); //XXX + else if(key==KDown) + text_cur(e->box->data, 10, 1); //XXX + else if(key==KLeft) + text_cur(e->box->data, 1, -1); + else if(key==KRight) + text_cur(e->box->data, 1, 1); + break; + case EExpose: + swk_gi_fill(e->box->r, ColorBG, 0); + row = t->ycur-2; + /* text */{ + int len, rows = 0; + int y = e->box->r.y--; + char *p, *p0, *str; + str = text_get (e->box->data, text_rowoff(e->box->data,row), -1); + if(row<0) row = 0; + //printf("str(%s)\n", str); + for(p=p0=str;*p;p++) { + if(*p=='\n') { + if(++rows>e->box->r.h) + break; + *p = 0; + e->box->text = p0; + e->box->r.y++; + len = strlen(p0); + if(len>=e->box->r.w*3) + p0[(e->box->r.w*3)-1]=0; + swk_gi_text(e->box->r, p0); + p0 = p+1; + } + } + if((rows<=e->box->r.h-1)&&p0&&*p0) { + e->box->r.y++; + swk_gi_text(e->box->r, p0); + } + e->box->r.y = y; + free(str); + } + /* cursor */ { + int len, cut = e->box->r.w*2; + int row = ((Text*)e->box->data)->ycur; + int col = ((Text*)e->box->data)->xcur; + if(row>=e->box->r.h-1) row = e->box->r.h-2; +#if 1 + #ifdef USE_SDL + len = 4*e->box->r.x; + #else + len = 3*e->box->r.x; + #endif + if(col>cut) + len += cut; + #ifdef USE_SDL + else len += 2*col+1; + #else + else len += col+1; + #endif +#endif + Text* t = e->box->data; + text_sync(e->box->data); + + Rect r = { (e->box->r.x*3)+col, e->box->r.y+row, 1, 1}; + swk_gi_fill(r, ColorHI, 2); + } + swk_gi_rect(e->box->r, ColorFG); + break; + default: + swk_label(e); + break; + } +}