[hackers] [swk] initial implementation of multiline input widget || pancake

From: <hg_AT_suckless.org>
Date: Wed, 25 Aug 2010 03:03:45 +0000 (UTC)

changeset: 66:a59af1f44831
tag: tip
user: pancake <pancake_AT_nopcode.org>
date: Wed Aug 25 05:00:20 2010 +0200
files: Makefile config.def.h gi_sdl.c gi_x11.c swk.c swk.h t/test.c text.c
description:
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

diff -r 1c88f618843c -r a59af1f44831 Makefile
--- a/Makefile Tue Aug 24 23:14:10 2010 +0200
+++ b/Makefile Wed Aug 25 05:00:20 2010 +0200
@@ -38,7 +38,7 @@
 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 @@
 
 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 -r 1c88f618843c -r a59af1f44831 config.def.h
--- a/config.def.h Tue Aug 24 23:14:10 2010 +0200
+++ b/config.def.h Wed Aug 25 05:00:20 2010 +0200
@@ -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 -r 1c88f618843c -r a59af1f44831 gi_sdl.c
--- a/gi_sdl.c Tue Aug 24 23:14:10 2010 +0200
+++ b/gi_sdl.c Wed Aug 25 05:00:20 2010 +0200
@@ -171,7 +171,6 @@
                 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 @@
                         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 -r 1c88f618843c -r a59af1f44831 gi_x11.c
--- a/gi_x11.c Tue Aug 24 23:14:10 2010 +0200
+++ b/gi_x11.c Wed Aug 25 05:00:20 2010 +0200
@@ -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 @@
                 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 @@
                 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 @@
         } 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 @@
         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 -r 1c88f618843c -r a59af1f44831 swk.c
--- a/swk.c Tue Aug 24 23:14:10 2010 +0200
+++ b/swk.c Wed Aug 25 05:00:20 2010 +0200
@@ -71,7 +71,8 @@
                 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 @@
 
 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 @@
                 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 @@
         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 -r 1c88f618843c -r a59af1f44831 swk.h
--- a/swk.h Tue Aug 24 23:14:10 2010 +0200
+++ b/swk.h Wed Aug 25 05:00:20 2010 +0200
@@ -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_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_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 -r 1c88f618843c -r a59af1f44831 t/test.c
--- a/t/test.c Tue Aug 24 23:14:10 2010 +0200
+++ b/t/test.c Wed Aug 25 05:00:20 2010 +0200
@@ -63,6 +63,28 @@
         { .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(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 @@
         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 @@
         { .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 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 -r 1c88f618843c -r a59af1f44831 text.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/text.c Wed Aug 25 05:00:20 2010 +0200
@@ -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;
+ }
+}
Received on Wed Aug 25 2010 - 05:03:45 CEST

This archive was generated by hypermail 2.2.0 : Wed Aug 25 2010 - 05:12:04 CEST