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;
+ }
+}