ref: 3ca0009a7450e86fed00995d8e296c41619160ba
parent: 95fa0cb9868e932653e84533fe41691ad4e1579e
author: aap <aap@papnet.eu>
date: Tue Aug 20 06:59:59 EDT 2024
first implementation of tabbed windows (WIP)
--- a/README
+++ b/README
@@ -30,6 +30,15 @@
- screenoffset wctl message
- (no)sticky wctl message
- select window with mouse by reading from `pick' file
+- tabbed windows (WIP)
+ - new tab: click window instead of dragging out rectangle
+ - close tab: button 2 on tab
+ - move tab: 1-2 and 1-3 chords
+ - move tab across window: button 3 on tab or title bar,
+ click new window or background
+ - TODO:
+ - no fs interface yet
+ - only implemented for simple theme so far
- misc
- 1-3 chord on desktop opens secondary menu
@@ -38,4 +47,4 @@
The window decoration style is set by linking in the relevant file,
so edit the `mkfile' to try different styles.
The options are: flat.c, simple.c, win3.c, win95.c
-
+ BUG: only simple.c currently working because of tabs
--- a/TODO
+++ /dev/null
@@ -1,20 +1,0 @@
-rethink resizing and repainting
-rethink hiding/unhiding
-check for bad rects (what's left here?)
-top/bottom/current seems to work a bit different in rio
-make sure there are no deadlocks
-...
-
-ideas:
- case-insensitive 'look'
- virtual screen (like fvwm)
- cursor movement
- decorations (at least make them possible)
- tabbed window
-
-problems:
- Borderwidth hardcoded in gengetwindow
- originwindow doesn't work with gengetwindow
- non-origin screen breaks samterm scrollbars
- raw mode where?
- initkeyboard with /dev/kbd support (also fix leaks in old code)
--- a/data.c
+++ b/data.c
@@ -168,7 +168,8 @@
Image*
mkcolor(ulong col)
{
- return allocimage(display, Rect(0,0,1,1), screen->chan, 1, col);
+// return allocimage(display, Rect(0,0,1,1), screen->chan, 1, col);
+ return allocimage(display, Rect(0,0,1,1), RGBA32, 1, col);
}
Image*
--- a/flat.c
+++ b/flat.c
@@ -19,7 +19,7 @@
Image *wincolors[NumWinColors];
void
-wdecor(Window *w)
+wdecor(WinTab *w)
{
if(w->frame == nil)
return;
@@ -44,7 +44,7 @@
}
void
-wtitlectl(Window *w)
+wtitlectl(WinTab *w)
{
if(mctl->buttons & 7){
wraise(w);
--- a/fs.c
+++ b/fs.c
@@ -95,7 +95,7 @@
typedef struct Xfid Xfid;
struct Xfid
{
- Window *w;
+ WinTab *w;
RuneConvBuf cnv;
};
#define XF(fid) ((Xfid*)(fid)->aux)
@@ -156,7 +156,7 @@
}
static Xfid*
-getxfid(Window *w)
+getxfid(WinTab *w)
{
Xfid *xf;
xf = emalloc(sizeof(Xfid));
@@ -175,7 +175,7 @@
static void
fsattach(Req *r)
{
- Window *w;
+ WinTab *w;
char *end;
int id;
Wctlcmd cmd;
@@ -240,7 +240,7 @@
int i;
Dirent *d;
Xfid *xf;
- Window *w;
+ WinTab *w;
int dir;
xf = fid->aux;
@@ -288,7 +288,7 @@
static int
genrootdir(int n, Dir *d, void *a)
{
- Window *w = a;
+ WinTab *w = a;
int i;
n++; /* -1 is root dir */
@@ -320,7 +320,7 @@
static int
genwsysdir(int n, Dir *d, void*)
{
- Window *w;
+ WinTab *w;
if(n == -1){
genrootdir(0, d, nil);
@@ -328,8 +328,8 @@
d->name = estrdup9p("wsys");
return 0;
}
- if(n < nwindows){
- w = windows[n];
+ if(n < nwintabs){
+ w = wintabs[n];
genrootdir(-1, d, w);
free(d->name);
d->name = smprint("%d", w->id);
@@ -345,7 +345,7 @@
fsopen(Req *r)
{
Xfid *xf;
- Window *w;
+ WinTab *w;
int rd, wr;
xf = XF(r->fid);
@@ -421,7 +421,7 @@
/* pick window from main thread.
* TODO: this may not be optimal because
* it might block this thread. */
- Channel *wc = chancreate(sizeof(Window*), 0);
+ Channel *wc = chancreate(sizeof(WinTab*), 0);
sendp(pickchan, wc);
xf->w = w = recvp(wc);
if(w)
@@ -452,7 +452,7 @@
fsclose(Fid *fid)
{
Xfid *xf;
- Window *w;
+ WinTab *w;
Text *x;
int rd, wr;
@@ -578,7 +578,7 @@
static char*
readblocking(Req *r, Channel *readchan)
{
- Window *w;
+ WinTab *w;
Channel *chan;
Stringpair pair;
enum { Adata, Agone, Aflush, NALT };
@@ -612,7 +612,7 @@
static void
xread(Req *r)
{
- Window *w;
+ WinTab *w;
char *data;
w = XF(r->fid)->w;
@@ -660,7 +660,7 @@
respond(r, readimg(r, screen));
return;
case Qwindow:
- respond(r, readimg(r, w->frame));
+ respond(r, readimg(r, w->w->frame));
return;
case Qwctl:
/* TODO: what's with the Etooshort conditions?? */
@@ -704,7 +704,7 @@
xwrite(Req *r)
{
Xfid *xf;
- Window *w;
+ WinTab *w;
Text *x;
vlong offset;
u32int count;
@@ -793,7 +793,7 @@
return;
}
pt.y = strtoul(p, nil, 0);
- wmovemouse(w, pt);
+ wmovemouse(w->w, pt);
break;
case Qcursor:
--- a/inc.h
+++ b/inc.h
@@ -187,33 +187,48 @@
extern int titlesz;
typedef struct Window Window;
+typedef struct WinTab WinTab;
+
struct Window
{
Ref;
- bool deleted;
- bool hidden;
Window *lower;
Window *higher;
+ bool hidden;
Image *frame;
+ Screen *screen;
+ int noborder;
+ bool notitle;
+ bool maximized;
+ bool sticky;
+ Rectangle rect;
+ Rectangle titlerect;
+ Rectangle tabrect;
+ Rectangle contrect;
+ Rectangle scrollr;
+ Rectangle textr;
+ Rectangle origrect;
+
+ // tmp
+ WinTab *tab;
+ WinTab *cur;
+};
+
+struct WinTab
+{
+ Ref;
+ bool deleted;
+ Window *w;
+ WinTab *next;
Image *content;
int id;
char name[32];
int namecount;
char *label;
- int noborder;
int notefd;
char *dir;
- bool notitle;
- bool maximized;
- bool sticky;
- Rectangle origrect;
- Rectangle titlerect;
- Rectangle contrect;
-
Text text;
- Rectangle scrollr;
- Rectangle textr;
int holdmode;
bool scrolling;
bool wctlready;
@@ -248,6 +263,8 @@
extern Window *bottomwin, *topwin;
extern Window *windows[MAXWINDOWS];
extern int nwindows;
+extern WinTab *wintabs[MAXWINDOWS];
+extern int nwintabs;
extern Window *focused, *cursorwin;
extern Point screenoff;
@@ -256,28 +273,34 @@
void wmaximize(Window *w);
void wrestore(Window *w);
void wresize(Window *w, Rectangle r);
-Window *wcreate(Rectangle r, bool hidden, bool scrolling);
-void wrelease(Window *w);
-void wsendmsg(Window *w, int type);
-Window *wfind(int id);
+void wrecreate(Window *w);
Window *wpointto(Point pt);
-void wsetcursor(Window *w);
-void wsetlabel(Window *w, char *label);
-void wsetname(Window *w);
-void wsetpid(Window *w, int pid, int dolabel);
void wdelete(Window *w);
void wmove(Window *w, Point pos);
void wraise(Window *w);
void wlower(Window *w);
void wfocus(Window *w);
-void wremove(Window *w);
+void wunfocus(Window *w);
int whide(Window *w);
int wunhide(Window *w);
-void wsethold(Window *w, int hold);
void wmovemouse(Window *w, Point pt);
-void wtype(Window *w, Rune r);
-int wincmd(Window *w, int pid, char *dir, char **argv);
+WinTab *wcreate(Rectangle r, bool hidden, bool scrolling);
+void wrelease(WinTab *w);
+void wsendmsg(WinTab *w, int type);
+WinTab *wfind(int id);
+void wsetcursor(WinTab *w);
+void wsetlabel(WinTab *w, char *label);
+void wsetname(WinTab *w);
+void wsetpid(WinTab *w, int pid, int dolabel);
+void wsethold(WinTab *w, int hold);
+void wtype(WinTab *w, Rune r);
+int wincmd(WinTab *w, int pid, char *dir, char **argv);
+
+WinTab *tcreate(Window *w, bool scrolling);
+void tfocus(WinTab *t);
+void tdelete(WinTab *t);
+
void screenoffset(int offx, int offy);
typedef struct Wctlcmd Wctlcmd;
@@ -295,7 +318,7 @@
};
Wctlcmd parsewctl(char *s, Rectangle r);
-char *writewctl(Window *w, char *data);
+char *writewctl(WinTab *w, char *data);
extern Cursor *cursor;
@@ -311,7 +334,7 @@
void refresh(void);
Point dmenuhit(int but, Mousectl *mc, int nx, int ny, Point last);
-void drainmouse(Mousectl *mc, Window *w);
+void drainmouse(Mousectl *mc, WinTab *w);
void grab(Window *w, int btn);
void btn3menu(void);
--- a/main.c
+++ b/main.c
@@ -22,9 +22,9 @@
{
int i;
- for(i = 0; i < nwindows; i++)
- if(windows[i]->notefd >= 0)
- write(windows[i]->notefd, "hangup", 6);
+ for(i = 0; i < nwintabs; i++)
+ if(wintabs[i]->notefd >= 0)
+ write(wintabs[i]->notefd, "hangup", 6);
}
static char *oknotes[] ={
@@ -143,10 +143,10 @@
char *rcargv[] = { "rc", "-i", nil };
-Window*
+WinTab*
new(Rectangle r)
{
- Window *w;
+ WinTab *w;
w = wcreate(r, FALSE, scrolling);
assert(w);
@@ -155,8 +155,21 @@
return w;
}
+WinTab*
+newtab(Window *ww)
+{
+ WinTab *w;
+
+ w = tcreate(ww, scrolling);
+ assert(w);
+ if(wincmd(w, 0, nil, rcargv) == 0)
+ return nil;
+ return w;
+}
+
+
void
-drainmouse(Mousectl *mc, Window *w)
+drainmouse(Mousectl *mc, WinTab *w)
{
if(w) send(w->mc.c, &mc->Mouse);
while(mc->buttons){
@@ -163,7 +176,7 @@
readmouse(mc);
/* stop sending once focus changes.
* buttons released in wfocus() */
- if(w != focused) w = nil;
+ if(w && w->w != focused) w = nil;
if(w) send(w->mc.c, &mc->Mouse);
}
}
@@ -365,6 +378,13 @@
}
flushimage(display, 1);
}
+
+//TODO(tab): temp hack
+ else{
+ Window *ww = wpointto(r.min);
+ if(w == nil && ww)
+ newtab(ww);
+ }
}
void
@@ -452,7 +472,7 @@
void
-btn2menu(Window *w)
+btn2menu(WinTab *w)
{
enum {
Cut,
@@ -549,7 +569,7 @@
continue;
if(t->hidden || obscured(t, t->frame->r, t->higher)){
hidden[nhidden] = windows[i];
- str[nhidden+Hidden] = windows[i]->label;
+ str[nhidden+Hidden] = windows[i]->cur->label;
nhidden++;
}
}
@@ -685,11 +705,13 @@
setcursornormal(nil);
wtitlectl(w);
}
+ }else if(w->cur == nil){
+ /* no tab in window */
}else if(w != focused){
/* inactive window */
- wsetcursor(w);
+ wsetcursor(w->cur);
if(mctl->buttons & 7 ||
- mctl->buttons & (8|16) && focused->mouseopen){
+ mctl->buttons & (8|16) && focused && focused->cur->mouseopen){
wraise(w);
wfocus(w);
if(mctl->buttons & 1)
@@ -697,24 +719,24 @@
else
goto again;
}
- }else if(!w->mouseopen){
+ }else if(!w->cur->mouseopen){
/* active text window */
- wsetcursor(w);
+ wsetcursor(w->cur);
if(mctl->buttons && topwin != w)
wraise(w);
- if(mctl->buttons & (1|8|16) || ptinrect(mctl->xy, w->text.scrollr))
- drainmouse(mctl, w);
+ if(mctl->buttons & (1|8|16) || ptinrect(mctl->xy, w->cur->text.scrollr))
+ drainmouse(mctl, w->cur);
if(mctl->buttons & 2){
- incref(w);
- btn2menu(w);
- wrelease(w);
+ incref(w->cur);
+ btn2menu(w->cur);
+ wrelease(w->cur);
}
if(mctl->buttons & 4)
btn3menu();
}else{
/* active graphics window */
- wsetcursor(w);
- drainmouse(mctl, w);
+ wsetcursor(w->cur);
+ drainmouse(mctl, w->cur);
}
}
}
@@ -790,7 +812,7 @@
Channel *fschan, *chan;
int n;
Stringpair pair;
- Window *cur, *prev;
+ WinTab *cur, *prev;
Queue tapq;
threadsetname("keyboardtap");
@@ -813,6 +835,7 @@
alts[Atotap].op = CHANSND;
else
alts[Atotap].op = CHANNOP;
+WinTab *ftab = focused ? focused->cur : nil;
switch(alt(alts)){
case Akbd:
/* from keyboard to tap or to window */
@@ -821,7 +844,7 @@
ctldown = utfrune(s+1, Kctl) != nil;
}
prev = cur;
- cur = focused;
+ cur = ftab;
if(totap){
if(cur != prev && cur){
/* notify tap of focus change */
@@ -842,7 +865,7 @@
case Afromtap:
/* from tap to window */
- if(cur && cur == focused)
+ if(cur && cur == ftab)
sendp(cur->kbd, s);
else
free(s);
--- a/simple.c
+++ b/simple.c
@@ -21,6 +21,8 @@
Image *wincolors[NumWinColors];
Image *icons[4];
+Image *testcol;
+
void
btn(Image *img, Rectangle r, Image *col, Image *icon, int down)
{
@@ -50,6 +52,13 @@
return ptinrect(mctl->xy, r);
}
+int
+colsel(Window *w)
+{
+ return w->cur && w->cur->holdmode ?
+ w == focused ? 2 : 3 :
+ w == focused ? 0 : 1;
+}
void
wdecor(Window *w)
@@ -56,9 +65,7 @@
{
if(w->frame == nil)
return;
- int c = w->holdmode ?
- w == focused ? TITLEHOLD : LTITLEHOLD :
- w == focused ? TITLE : LTITLE;
+ int c = TITLE + colsel(w);
int c2 = w == focused ? FRAME : LFRAME;
Rectangle r, b1, b2, b3;
@@ -88,20 +95,101 @@
r.min.x += bordersz/2;
r.min.y -= 2;
Point pt = Pt(r.min.x, r.min.y);
- string(w->frame, pt, wincolors[c+4], pt, font, w->label);
+//if(w->cur && w->ref == 1)
+if(w->cur)
+ string(w->frame, pt, wincolors[c+4], pt, font, w->cur->label);
}
+
+ r = rectsubpt(w->tabrect, Pt(0,1));
+// r.min.x--;
+// r.max.x++;
+ draw(w->frame, r, wincolors[c], nil, ZP);
+
+ int n = w->ref;
+ if(n > 1){
+ int wd = Dx(r)/n;
+ int xxx = r.max.x;
+ r.max.x = r.min.x + wd;
+ for(WinTab *t = w->tab; t; t = t->next){
+ if(t->next == nil)
+ r.max.x = xxx;
+ if(t != w->cur)
+ draw(w->frame, r, testcol, nil, ZP);
+ Point pt = Pt(r.min.x+bordersz/2, r.min.y);
+ string(w->frame, pt, wincolors[c+4], pt, font, t->label);
+ r = rectaddpt(r, Pt(wd,0));
+ }
+ }
}
-void
-wtitlectl(Window *w)
+Window *pick(void);
+Window *wcreate_(Rectangle r, bool hidden);
+void tmigrate(WinTab *t, Window *w);
+void tmoveleft(WinTab *t);
+void tmoveright(WinTab *t);
+
+static void
+tabctl(Window *w)
{
+ Rectangle r;
+
if(mctl->buttons & 7){
wraise(w);
wfocus(w);
+ r = rectsubpt(w->tabrect, Pt(0,1));
+ int n = w->ref;
+ if(n > 1){
+ int wd = Dx(r)/n;
+ r.max.x = r.min.x + wd;
+ for(WinTab *t = w->tab; t; t = t->next){
+ if(ptinrect(mctl->xy, r)){
+ if(mctl->buttons & 1){
+ tfocus(t);
+ /* chording */
+ while(mctl->buttons){
+ int b = mctl->buttons;
+ if(b & 6){
+ if(b & 2)
+ tmoveleft(t);
+ else
+ tmoveright(t);
+ }
+ while(mctl->buttons == b)
+ readmouse(mctl);
+ }
+ }else if(mctl->buttons & 2)
+ tdelete(t);
+ else if(mctl->buttons & 4){
+Point pt = mctl->xy;
+ Window *ww = pick();
+ if(ww){
+ tmigrate(t, ww);
+ wraise(ww);
+ wfocus(ww);
+ return;
+ }else{
+ ww = wpointto(mctl->xy);
+ if(ww == nil){
+ tmigrate(t, wcreate_(rectaddpt(w->rect, subpt(mctl->xy, pt)), 0));
+ }
+ }
+ }
+ break;
+ }
+ r = rectaddpt(r, Pt(wd,0));
+ }
+ }
+ }
+}
+
+static void
+titlectl(Window *w)
+{
+ if(mctl->buttons & 7){
+ wraise(w);
+ wfocus(w);
if(mctl->buttons & 1) {
- int c = w->holdmode ?
- w == focused ? TITLEHOLD : LTITLEHOLD :
- w == focused ? TITLE : LTITLE;
+ int c = TITLE + colsel(w);
Rectangle r = w->titlerect;
Rectangle br = Rect(0,0,titlesz-bordersz,titlesz-bordersz);
@@ -129,12 +217,29 @@
}else if(!w->maximized)
grab(w, 1);
}
- if(mctl->buttons & 4)
- btn3menu();
+ if(mctl->buttons & 4){
+// btn3menu();
+ Window *ww = pick();
+ if(ww){
+ tmigrate(w->cur, ww);
+ wraise(ww);
+ wfocus(ww);
+ return;
+ }
+ }
}
}
+void
+wtitlectl(Window *w)
+{
+ if(ptinrect(mctl->xy, w->titlerect))
+ titlectl(w);
+ else
+ tabctl(w);
+}
+
static char minbtn[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -230,4 +335,6 @@
icons[1] = mkicon(maxbtn, 16, 13);
icons[2] = mkicon(rstbtn, 16, 13);
icons[3] = mkicon(closebtn, 16, 13);
+
+ testcol = getcolor("asdfasdfasdf", 0x00000020);
}
--- a/wctl.c
+++ b/wctl.c
@@ -139,6 +139,8 @@
Rectangle
rectonscreen(Rectangle r)
{
+//TODO(vdesk) this changes
+return r;
shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x);
shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y);
return r;
@@ -288,49 +290,50 @@
}
char*
-wctlcmd(Window *w, Rectangle r, int cmd)
+wctlcmd(WinTab *w, Rectangle r, int cmd)
{
+ Window *ww = w->w;
switch(cmd){
case Move:
- r = rectaddpt(Rect(0,0,Dx(w->frame->r),Dy(w->frame->r)), r.min);
+ r = rectaddpt(Rect(0,0,Dx(ww->frame->r),Dy(ww->frame->r)), r.min);
if(!goodrect(r))
return Ebadwr;
- if(!eqpt(r.min, w->frame->r.min))
- wmove(w, r.min);
+ if(!eqpt(r.min, ww->frame->r.min))
+ wmove(ww, r.min);
break;
case Resize:
if(!goodrect(r))
return Ebadwr;
- if(!eqrect(r, w->frame->r))
- wresize(w, r);
+ if(!eqrect(r, ww->frame->r))
+ wresize(ww, r);
break;
// TODO: these three work somewhat differently in rio
case Top:
- wraise(w);
+ wraise(ww);
break;
case Bottom:
- wlower(w);
+ wlower(ww);
break;
case Current:
- if(w->hidden)
+ if(ww->hidden)
return "window is hidden";
- wfocus(w);
- wraise(w);
+ wfocus(ww);
+ wraise(ww);
break;
case Hide:
- switch(whide(w)){
+ switch(whide(ww)){
case -1: return "window already hidden";
case 0: return "hide failed";
}
break;
case Unhide:
- switch(wunhide(w)){
+ switch(wunhide(ww)){
case -1: return "window not hidden";
case 0: return "hide failed";
}
break;
case Delete:
- wdelete(w);
+ wdelete(ww);
break;
case Scroll:
w->scrolling = TRUE;
@@ -342,26 +345,26 @@
wsendmsg(w, Wakeup);
break;
case Border:
- w->noborder &= ~1;
- wresize(w, w->frame->r);
+ ww->noborder &= ~1;
+ wrecreate(ww);
break;
case Noborder:
- w->noborder |= 1;
- wresize(w, w->frame->r);
+ ww->noborder |= 1;
+ wrecreate(ww);
break;
case Title:
- w->notitle = FALSE;
- wresize(w, w->frame->r);
+ ww->notitle = FALSE;
+ wrecreate(ww);
break;
case Notitle:
- w->notitle = TRUE;
- wresize(w, w->frame->r);
+ ww->notitle = TRUE;
+ wrecreate(ww);
break;
case Sticky:
- w->sticky = TRUE;
+ ww->sticky = TRUE;
break;
case Nosticky:
- w->sticky = FALSE;
+ ww->sticky = FALSE;
break;
default:
return "invalid wctl message";
@@ -372,7 +375,7 @@
char*
wctlnew(Wctlcmd cmd)
{
- Window *w;
+ WinTab *w;
char *argv[4], **args;
w = wcreate(cmd.r, cmd.hidden, cmd.scrolling);
@@ -399,7 +402,7 @@
}
char*
-writewctl(Window *w, char *data)
+writewctl(WinTab *w, char *data)
{
Rectangle r;
Wctlcmd cmd;
@@ -407,7 +410,7 @@
if(w == nil)
r = ZR;
else
- r = rectsubpt(w->frame->r, screen->r.min);
+ r = rectsubpt(w->w->frame->r, screen->r.min);
cmd = parsewctl(data, r);
if(cmd.error)
return cmd.error;
--- a/win3.c
+++ b/win3.c
@@ -97,7 +97,7 @@
void
-wdecor(Window *w)
+wdecor(WinTab *w)
{
if(w->frame == nil)
return;
@@ -155,7 +155,7 @@
}
void
-wtitlectl(Window *w)
+wtitlectl(WinTab *w)
{
if(mctl->buttons & 7){
wraise(w);
--- a/win95.c
+++ b/win95.c
@@ -69,7 +69,7 @@
}
void
-wdecor(Window *w)
+wdecor(WinTab *w)
{
if(w->frame == nil)
return;
@@ -105,7 +105,7 @@
}
void
-wtitlectl(Window *w)
+wtitlectl(WinTab *w)
{
if(mctl->buttons & 7){
wraise(w);
--- a/wind.c
+++ b/wind.c
@@ -3,6 +3,8 @@
Window *bottomwin, *topwin;
Window *windows[MAXWINDOWS];
int nwindows;
+WinTab *wintabs[MAXWINDOWS];
+int nwintabs;
Window *focused, *cursorwin;
Point screenoff;
@@ -62,9 +64,10 @@
wresize(w, w->origrect);
}
-void
+static void
wcalcrects(Window *w, Rectangle r)
{
+ w->rect = r;
w->contrect = r;
if(!w->noborder)
w->contrect = insetrect(w->contrect, bordersz);
@@ -76,6 +79,13 @@
w->contrect.min.y += titlesz;
}
+ w->tabrect = ZR;
+ if(w->ref > 1){
+ w->tabrect = w->contrect;
+ w->tabrect.max.y = w->tabrect.min.y + 18;
+ w->contrect.min.y += 18;
+ }
+
r = insetrect(w->contrect, 1);
w->scrollr = r;
w->scrollr.max.x = w->scrollr.min.x + 12;
@@ -83,100 +93,191 @@
w->textr.min.x = w->scrollr.max.x + 4;
}
-void
-wsetcolors(Window *w)
+static void
+wsetcolors(WinTab *w)
{
int c = w->holdmode ?
- w == focused ? HOLDTEXT : PALEHOLDTEXT :
- w == focused ? TEXT : PALETEXT;
+ w->w == focused ? HOLDTEXT : PALEHOLDTEXT :
+ w->w == focused ? TEXT : PALETEXT;
w->text.cols[TEXT] = colors[c];
}
static void
-wfreeimages(Window *w)
+tinit(WinTab *t)
{
- if(w->frame == nil)
+ Window *w;
+
+ if(t->deleted)
return;
+ w = t->w;
+ t->mc.image = w->frame;
+ t->content = allocwindow(w->screen, w->contrect, Refbackup, DNofill);
+ assert(t->content);
+ draw(t->content, t->content->r, colors[BACK], nil, ZP);
+ xinit(&t->text, w->textr, w->scrollr, font, t->content, colors);
+}
- Screen *s = w->content->screen;
- freeimage(w->content);
- w->content = nil;
+static void
+tdeinit(WinTab *t)
+{
+ /* have to move image out of the way
+ * because client program can hold a ref
+ * and mess up our drawing. */
+ if(t->content)
+ originwindow(t->content, t->content->r.min, screen->r.max);
+ freeimage(t->content);
+ t->content = nil;
+ t->mc.image = nil;
+}
- freescreen(s);
+/* get rid of window visually */
+static void
+wremove(Window *w)
+{
+ if(w->frame)
+ originwindow(w->frame, w->frame->r.min, screen->r.max);
+}
+static void
+wfreeimages(Window *w)
+{
+ freescreen(w->screen);
+ w->screen = nil;
+
freeimage(w->frame);
w->frame = nil;
- w->mc.image = nil;
}
+/* create images, destroy first if they exist.
+ * either only tab images e.g. when the tab bar appears/disappears
+ * or full window when window is resized/moved. */
static void
-wsetsize(Window *w, Rectangle r)
+wreinit(Window *w, bool all)
{
- Rectangle hr;
- Screen *s;
+ Rectangle r, hr;
- wfreeimages(w);
- if(w->hidden){
- hr = rectaddpt(r, subpt(screen->r.max, r.min));
- w->frame = allocwindow(wscreen, hr, Refbackup, DNofill);
- originwindow(w->frame, r.min, hr.min);
- }else
- w->frame = allocwindow(wscreen, r, Refbackup, DNofill);
- w->mc.image = w->frame;
- s = allocscreen(w->frame, colors[BACK], 0);
- assert(s);
- wcalcrects(w, r);
- w->content = allocwindow(s, w->contrect, Refnone, DNofill);
- assert(w->content);
- draw(w->frame, w->frame->r, colors[BACK], nil, ZP);
- xinit(&w->text, w->textr, w->scrollr, font, w->content, colors);
+ for(WinTab *t = w->tab; t; t = t->next)
+ tdeinit(t);
- wrepaint(w);
+ if(all){
+ r = w->rect;
+ /* reference can be held by client program
+ * indefinitely which would keep this on screen. */
+ wremove(w);
+ wfreeimages(w);
+ if(w->hidden){
+ hr = rectaddpt(r, subpt(screen->r.max, r.min));
+ w->frame = allocwindow(wscreen, hr, Refbackup, DNofill);
+ originwindow(w->frame, r.min, hr.min);
+ }else
+ w->frame = allocwindow(wscreen, r, Refbackup, DNofill);
+ w->screen = allocscreen(w->frame, colors[BACK], 0);
+ assert(w->screen);
+ }
+
+ for(WinTab *t = w->tab; t; t = t->next)
+ tinit(t);
+
+ tfocus(w->cur);
}
+// TODO: find better name
+void
+wrecreate(Window *w)
+{
+ wcalcrects(w, w->rect);
+ wreinit(w, 0);
+ for(WinTab *t = w->tab; t; t = t->next)
+ wsendmsg(t, Resized);
+}
+
+
static int id = 1;
Window*
-wcreate(Rectangle r, bool hidden, bool scrolling)
+wcreate_(Rectangle r, bool hidden)
{
Window *w;
w = emalloc(sizeof(Window));
- incref(w);
- w->id = id++;
- w->notefd = -1;
- wsetlabel(w, "<unnamed>");
- w->dir = estrdup(startdir);
w->hidden = hidden;
- w->scrolling = scrolling;
w->notitle = notitle; // TODO: argument?
- wsetsize(w, r);
+ wcalcrects(w, r);
+ wreinit(w, 1);
wlistpushfront(w);
// TODO: could be more graceful here
assert(nwindows < MAXWINDOWS);
windows[nwindows++] = w;
- w->mc.c = chancreate(sizeof(Mouse), 16);
- w->gone = chancreate(sizeof(int), 0);
- w->kbd = chancreate(sizeof(char*), 16);
- w->ctl = chancreate(sizeof(int), 0);
- w->conswrite = chancreate(sizeof(Channel**), 0);
- w->consread = chancreate(sizeof(Channel**), 0);
- w->kbdread = chancreate(sizeof(Channel**), 0);
- w->mouseread = chancreate(sizeof(Channel**), 0);
- w->wctlread = chancreate(sizeof(Channel**), 0);
- w->complete = chancreate(sizeof(Completion*), 0);
- threadcreate(winthread, w, mainstacksize);
-
- wsetname(w);
wfocus(w);
return w;
}
+void
+tfocus(WinTab *t)
+{
+ if(t == nil || t->deleted)
+ return;
+ t->w->cur = t;
+ topwindow(t->content);
+ wrepaint(t->w);
+}
+
+WinTab*
+tcreate(Window *w, bool scrolling)
+{
+ WinTab *t, **tp;
+
+ /* recreate window when tab bar appears
+ * before we attach the new tab. */
+ incref(w);
+ if(w->ref == 2)
+ wrecreate(w);
+
+ t = emalloc(sizeof(WinTab));
+ incref(t);
+ t->w = w;
+ for(tp = &w->tab; *tp; tp = &(*tp)->next);
+ *tp = t;
+ tinit(t);
+ t->id = id++;
+ t->notefd = -1;
+ wsetlabel(t, "<unnamed>");
+ t->dir = estrdup(startdir);
+ t->scrolling = scrolling;
+
+ t->mc.c = chancreate(sizeof(Mouse), 16);
+ t->gone = chancreate(sizeof(int), 0);
+ t->kbd = chancreate(sizeof(char*), 16);
+ t->ctl = chancreate(sizeof(int), 0);
+ t->conswrite = chancreate(sizeof(Channel**), 0);
+ t->consread = chancreate(sizeof(Channel**), 0);
+ t->kbdread = chancreate(sizeof(Channel**), 0);
+ t->mouseread = chancreate(sizeof(Channel**), 0);
+ t->wctlread = chancreate(sizeof(Channel**), 0);
+ t->complete = chancreate(sizeof(Completion*), 0);
+ threadcreate(winthread, t, mainstacksize);
+
+ wsetname(t);
+ // TODO: could be more graceful here
+ assert(nwintabs < MAXWINDOWS);
+ wintabs[nwintabs++] = t;
+
+ tfocus(t);
+
+ return t;
+}
+
+WinTab*
+wcreate(Rectangle r, bool hidden, bool scrolling)
+{
+ return tcreate(wcreate_(r, hidden), scrolling);
+}
+
/* called from winthread when it exits */
static void
-wfree(Window *w)
+wfree(WinTab *w)
{
if(w->notefd >= 0)
close(w->notefd);
@@ -195,18 +296,13 @@
free(w);
}
-/* logically and visually close the window.
- * struct and thread will stick around until all references are gone.
- * safe to call multiple times. */
static void
-wclose(Window *w)
+wdestroy(Window *w)
{
int i;
- if(w->deleted)
- return;
- w->deleted = TRUE;
assert(w != focused); /* this must be done elsewhere */
+ assert(w->tab == nil);
wlistremove(w);
for(i = 0; i < nwindows; i++)
if(windows[i] == w){
@@ -215,47 +311,212 @@
break;
}
wfreeimages(w);
+ free(w);
flushimage(display, 1);
}
int
-inwinthread(Window *w)
+inwinthread(WinTab *w)
{
return w->threadname == threadgetname();
}
-/* decrement reference, close window once all references gone. */
-void
-wrelease(Window *w)
+/* decrement reference, close window once all tabs gone. */
+static int
+wrelease_(Window *w)
{
if(decref(w) == 0){
+ assert(w->tab == nil);
+ assert(w->cur == nil);
+ wremove(w);
+ wunfocus(w);
+ assert(w != focused);
+ wdestroy(w);
+ return 0;
+ }else{
+ assert(w->ref > 0);
+ return w->ref;
+ }
+}
+
+/* logically and visually close the tab.
+ * struct, thread and link into window will stick
+ * around until all references are gone.
+ * safe to call multiple times. */
+static void
+tclose(WinTab *w)
+{
+ int i;
+
+ if(w->deleted)
+ return;
+ w->deleted = TRUE;
+ for(i = 0; i < nwintabs; i++)
+ if(wintabs[i] == w){
+ nwintabs--;
+ memmove(&wintabs[i], &wintabs[i+1], (nwintabs-i)*sizeof(WinTab*));
+ break;
+ }
+ tdeinit(w);
+}
+
+/* detach tab from window */
+void
+tdetach(WinTab *t)
+{
+ WinTab **tp;
+ Window *w = t->w;
+
+ if(w == nil)
+ return;
+
+ /* remove tab from window */
+ for(tp = &w->tab; *tp; tp = &(*tp)->next){
+ if(*tp == t){
+ (*tp) = t->next;
+ t->next = nil;
+ t->w = nil;
+ break;
+ }
+ }
+ assert(t->w == nil);
+ tdeinit(t);
+
+ /* find new focused tab */
+ if(w->cur == t){
+ w->cur = *tp;
+ if(w->cur == nil)
+ for(w->cur = w->tab;
+ w->cur && w->cur->next;
+ w->cur = w->cur->next);
+ }
+ if(wrelease_(w) > 0){
+ /* complete redraw if tab bar disappears */
+ if(w->ref == 1)
+ wrecreate(w);
+ else
+ tfocus(w->cur);
+ }
+}
+
+void
+tmigrate(WinTab *t, Window *w)
+{
+ WinTab **tp;
+
+ if(t->w == w)
+ return;
+ tdetach(t);
+
+ /* recreate window when tab bar appears
+ * before we attach the new tab. */
+ incref(w);
+ if(w->ref == 2)
+ wrecreate(w);
+
+ t->w = w;
+ for(tp = &w->tab; *tp; tp = &(*tp)->next);
+ *tp = t;
+ tinit(t);
+
+ tfocus(t);
+ wsendmsg(t, Resized);
+}
+
+/* this SUCKS, want doubly linked lists */
+static WinTab**
+getprevptr(WinTab *t)
+{
+ WinTab **tp;
+ for(tp = &t->w->tab; *tp; tp = &(*tp)->next)
+ if(*tp == t)
+ return tp;
+ return nil;
+}
+static WinTab*
+getprev(WinTab *t)
+{
+ WinTab *tt;
+ for(tt = t->w->tab; tt; tt = tt->next)
+ if(tt->next == t)
+ return tt;
+ return nil;
+}
+
+static void
+tswapadjacent(WinTab *l, WinTab *r)
+{
+ WinTab **tp;
+
+ tp = getprevptr(l);
+ assert(tp);
+ l->next = r->next;
+ r->next = l;
+ *tp = r;
+ wdecor(l->w);
+}
+
+void
+tmoveleft(WinTab *r)
+{
+ WinTab *l;
+ l = getprev(r);
+ if(l == nil) return;
+ tswapadjacent(l, r);
+}
+
+void
+tmoveright(WinTab *l)
+{
+ WinTab *r;
+ r = l->next;
+ if(r == nil) return;
+ tswapadjacent(l, r);
+}
+
+/* decrement reference, close tab once all references gone. */
+void
+wrelease(WinTab *t)
+{
+ if(decref(t) == 0){
/* increment ref count temporarily
* so win thread doesn't exit too early */
- incref(w);
- wremove(w);
- wclose(w);
- decref(w);
- if(!inwinthread(w))
- wsendmsg(w, Wakeup);
+ incref(t);
+ tdetach(t);
+ tclose(t);
+ decref(t);
+ if(!inwinthread(t))
+ wsendmsg(t, Wakeup);
}else
- assert(w->ref > 0);
+ assert(t->ref > 0);
}
void
-wsendmsg(Window *w, int type)
+tdelete(WinTab *t)
{
+ assert(!t->deleted);
+ tdetach(t);
+ tclose(t);
+
+ wsendmsg(t, Deleted);
+}
+
+
+void
+wsendmsg(WinTab *w, int type)
+{
assert(!inwinthread(w));
sendul(w->ctl, type);
}
-Window*
+WinTab*
wfind(int id)
{
int i;
- for(i = 0; i < nwindows; i++)
- if(windows[i]->id == id)
- return windows[i];
+ for(i = 0; i < nwintabs; i++)
+ if(wintabs[i]->id == id)
+ return wintabs[i];
return nil;
}
@@ -271,22 +532,22 @@
}
void
-wsetcursor(Window *w)
+wsetcursor(WinTab *w)
{
- if(w == cursorwin)
+ if(w->w == cursorwin)
setcursornormal(w->holdmode ? &whitearrow : w->cursorp);
}
void
-wsetlabel(Window *w, char *label)
+wsetlabel(WinTab *w, char *label)
{
free(w->label);
w->label = estrdup(label);
- wdecor(w);
+ wdecor(w->w);
}
void
-wsetname(Window *w)
+wsetname(WinTab *w)
{
int i, n;
char err[ERRMAX];
@@ -306,7 +567,7 @@
}
void
-wsetpid(Window *w, int pid, int dolabel)
+wsetpid(WinTab *w, int pid, int dolabel)
{
char buf[32];
int ofd;
@@ -330,16 +591,18 @@
wdelete(Window *w)
{
wremove(w);
- wsendmsg(w, Deleted);
+ wunfocus(w);
+ for(WinTab *t = w->tab; t; t = t->next)
+ wsendmsg(t, Deleted);
}
static void
wrepaint(Window *w)
{
- wsetcolors(w);
+ wsetcolors(w->cur);
wdecor(w);
- if(!w->mouseopen)
- xredraw(&w->text);
+ if(!w->cur->mouseopen)
+ xredraw(&w->cur->text);
}
/* restore window order after reshaping has disturbed it */
@@ -355,10 +618,12 @@
void
wresize(Window *w, Rectangle r)
{
- wsetsize(w, r);
+ wcalcrects(w, r);
+ wreinit(w, 1);
if(w != topwin && !w->hidden)
worder();
- wsendmsg(w, Resized);
+ for(WinTab *t = w->tab; t; t = t->next)
+ wsendmsg(t, Resized);
}
void
@@ -386,15 +651,16 @@
flushimage(display, 1);
}
-void
+static void
wfocuschanged(Window *w)
{
- if(w == nil)
+// TODO(tab):
+ if(w == nil || w->cur == nil)
return;
- w->wctlready = TRUE;
+ w->cur->wctlready = TRUE;
wrepaint(w);
- if(!inwinthread(w))
- wsendmsg(w, Wakeup);
+ if(!inwinthread(w->cur))
+ wsendmsg(w->cur, Wakeup);
}
void
@@ -406,15 +672,17 @@
return;
prev = focused;
focused = w;
- if(prev){
+ if(prev && prev->cur){
+// TODO(tab): check this
+ WinTab *t = prev->cur;
/* release keys (if possible) */
char *s = estrdup("K");
- if(nbsendp(prev->kbd, s) != 1)
+ if(nbsendp(t->kbd, s) != 1)
free(s);
/* release mouse buttons */
- if(prev->mc.buttons){
- prev->mc.buttons = 0;
- prev->mq.counter++;
+ if(t->mc.buttons){
+ t->mc.buttons = 0;
+ t->mq.counter++;
}
}
wfocuschanged(prev);
@@ -421,48 +689,47 @@
wfocuschanged(focused);
}
-/* Take away focus but also get rid of the window visually.
- * For hiding/deleting */
void
-wremove(Window *w)
+wunfocus(Window *w)
{
- if(!w->deleted)
- originwindow(w->frame, w->frame->r.min, screen->r.max);
if(w == focused)
wfocus(nil);
}
+// TODO(tab): wctl ready everyone?
int
whide(Window *w)
{
if(w->hidden)
return -1;
- incref(w);
+ incref(w->tab);
wremove(w);
+ wunfocus(w);
w->hidden = TRUE;
- w->wctlready = TRUE;
- wsendmsg(w, Wakeup);
- wrelease(w);
+ w->tab->wctlready = TRUE;
+ wsendmsg(w->tab, Wakeup);
+ wrelease(w->tab);
return 1;
}
+// TODO(tab): wctl ready everyone?
int
wunhide(Window *w)
{
if(!w->hidden)
return -1;
- incref(w);
+ incref(w->tab);
w->hidden = FALSE;
- w->wctlready = TRUE;
+ w->tab->wctlready = TRUE;
originwindow(w->frame, w->frame->r.min, w->frame->r.min);
wraise(w);
wfocus(w);
- wrelease(w);
+ wrelease(w->tab);
return 1;
}
void
-wsethold(Window *w, int hold)
+wsethold(WinTab *w, int hold)
{
int switched;
@@ -472,7 +739,7 @@
switched = --w->holdmode == 0;
if(switched){
wsetcursor(w);
- wrepaint(w);
+ wrepaint(w->w);
}
}
@@ -508,7 +775,7 @@
{
char *dir;
char *str;
- Window *win;
+ WinTab *win;
};
static void
@@ -532,7 +799,7 @@
}
static int
-windfilewidth(Window *w, uint q0, int oneelement)
+windfilewidth(WinTab *w, uint q0, int oneelement)
{
uint q;
Rune r;
@@ -550,7 +817,7 @@
}
static void
-namecomplete(Window *w)
+namecomplete(WinTab *w)
{
Text *x;
int nstr, npath;
@@ -590,7 +857,7 @@
}
static void
-showcandidates(Window *w, Completion *c)
+showcandidates(WinTab *w, Completion *c)
{
Text *x;
int i;
@@ -634,7 +901,7 @@
}
void
-wkeyctl(Window *w, Rune r)
+wkeyctl(WinTab *w, Rune r)
{
Text *x;
int nlines, n;
@@ -726,7 +993,7 @@
}
void
-wmousectl(Window *w)
+wmousectl(WinTab *w)
{
int but;
@@ -747,19 +1014,22 @@
}
void
-winctl(Window *w, int type)
+winctl(WinTab *w, int type)
{
Text *x;
int i;
- x = &w->text;
- switch(type){
- case Deleted:
+ if(type == Deleted){
if(w->notefd >= 0)
write(w->notefd, "hangup", 6);
- wclose(w);
- break;
+ tclose(w);
+ return;
+ }
+ if(w->deleted)
+ return;
+ x = &w->text;
+ switch(type){
case Resized:
wsetname(w);
w->resized = TRUE;
@@ -770,8 +1040,6 @@
case Refresh:
/* take over window again */
- if(w->deleted)
- break;
draw(w->content, w->content->r, x->cols[BACK], nil, ZP);
xfullredraw(&w->text);
break;
@@ -797,7 +1065,7 @@
static void
winthread(void *arg)
{
- Window *w;
+ WinTab *w;
Text *x;
Rune r, *rp;
char *s;
@@ -965,11 +1233,13 @@
case AWctlRead:
w->wctlready = FALSE;
recv(fsc, &pair);
+Window *ww = w->w;
pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %11s %11s ",
- w->frame->r.min.x, w->frame->r.min.y,
- w->frame->r.max.x, w->frame->r.max.y,
- w == focused ? "current" : "notcurrent",
- w->hidden ? "hidden" : "visible");
+ ww->frame->r.min.x, ww->frame->r.min.y,
+ ww->frame->r.max.x, ww->frame->r.max.y,
+ ww->cur == w ? ww == focused ? "current" : "notcurrent"
+ : "tab",
+ ww->hidden ? "hidden" : "visible");
send(fsc, &pair);
break;
@@ -978,7 +1248,7 @@
break;
case AComplete:
- if(w->frame){
+ if(w->content){
if(!comp->advance)
showcandidates(w, comp);
if(comp->advance){
@@ -1010,7 +1280,7 @@
static void
shellproc(void *args)
{
- Window *w;
+ WinTab *w;
Channel *pidc;
void **arg;
char *cmd, *dir;
@@ -1054,7 +1324,7 @@
}
int
-wincmd(Window *w, int pid, char *dir, char **argv)
+wincmd(WinTab *w, int pid, char *dir, char **argv)
{
Channel *cpid;
void *args[5];
@@ -1071,7 +1341,7 @@
pid = recvul(cpid);
chanfree(cpid);
if(pid == 0){
- wdelete(w);
+ wdelete(w->w);
return 0;
}
}