#include #include #include #include #include #include #include #include <9p.h> #include "gui.h" /* We follow the conventions in graphics(2). * Rectangles do not include the max point. */ int layoutdebug; /* * Compute size and record if we want more x/y room * by looking into the inner components. * Save also current rect in orect. */ static void size(File* f) { Panel* p; Panel* np; File** l; File** w; File* fp; int some; p = f->aux; p->orect = p->rect; switch(p->type){ case Qcol: // lay out inner parts in rows case Qrow: // lay out inner parts in cols if (hastag(f)) p->size = Pt(Tagwid + Inset, Inset); else p->size = Pt(Inset, Inset); p->wants = Pt(0,0); some = 0; w = newfilewalk(f); for (l = w; fp = *l; l++){ if (!ispanel(fp)) continue; np = fp->aux; if (np->flags&Phide) continue; some++; size(fp); // We ignore wants.[xy] != 1. See comment below. p->wants.x |= (np->wants.x == 1); p->wants.y |= (np->wants.y == 1); if (p->type == Qcol){ if (some) p->size.y += Inset; p->size.y += np->size.y; if (p->size.x < np->size.x) p->size.x = np->size.x; } else { if (some) p->size.x += Inset; p->size.x += np->size.x; if (p->size.y < np->size.y) p->size.y = np->size.y; } } endfilewalk(w); if (!some && (p->flags&Playout)) p->wants = Pt(1,1); addpt(p->size, Pt(Inset, Inset)); break; default: p->size = p->minsz; if (p->size.x < Inset) // ensure a min sz p->size.x = Inset; if (p->size.y < Inset) p->size.y = Inset; /* Heuristic to keep tiny text panels * small. If they don't want too much, * we assing the space and ignore their wants. */ if (p->wants.x) if (p->maxsz.x && p->maxsz.x < 120){ p->wants.x = 2; // ignored later p->size.x = p->maxsz.x; } else p->wants.x = 1; if (p->wants.y) if (p->maxsz.y && p->maxsz.y < 120){ p->wants.y = 2; // ignored later p->size.y = p->maxsz.y; } else p->wants.y = 1; break; } } /* * Recursively layout the hierarchy to its minimum size. * Panel.rect enters with the available rectangle * for showing the file and its hierarchy. It leaves * the routine with the actual rectangle used. */ static void pack(File* f) { Rectangle r; Point max; Panel* p; Panel* np; File** w; File** l; File* fp; int some; p = f->aux; switch(p->type){ case Qcol: // lay out inner parts in rows case Qrow: // lay out inner parts in cols r = insetrect(p->rect, Inset); if (hastag(f)) r.min.x += Tagwid; max = r.min; // r is always the avail rectangle. // max is the max. point used. some = 0; w = newfilewalk(f); for (l = w; fp = *l; l++){ if (!ispanel(fp)) continue; np = fp->aux; if (np->flags&Phide) continue; some++; np->rect = r; pack(fp); rectclip(&np->rect, p->rect); if (max.x < np->rect.max.x) max.x = np->rect.max.x; if (max.y < np->rect.max.y) max.y = np->rect.max.y; if (p->type == Qcol) r.min.y = np->rect.max.y + Inset; else r.min.x = np->rect.max.x + Inset; } endfilewalk(w); if (!some) goto atom; p->rect.max = addpt(max, Pt(Inset, Inset)); break; default: // Atom atom: if (p->size.x > Dx(p->rect)) p->rect.max.x = p->rect.min.x + Dx(p->rect); else p->rect.max.x = p->rect.min.x + p->size.x; if (p->size.y > Dy(p->rect)) p->rect.max.y = p->rect.min.y + Dy(p->rect); else p->rect.max.y = p->rect.min.y + p->size.y; break; } if(1) ldprint("pack %s: %03dx%03d %R\n", f->name, Dx(p->rect), Dy(p->rect), p->rect); } static void move(File* f, Point pt) { Panel* p; File** w; File** l; File* fp; Panel* np; p = f->aux; p->rect.min.x += pt.x; p->rect.max.x += pt.x; p->rect.min.y += pt.y; p->rect.max.y += pt.y; w = newfilewalk(f); for (l = w; fp = *l; l++){ if (!ispanel(fp)) continue; np = fp->aux; if (!(np->flags&Phide)) move(fp, pt); } endfilewalk(w); } /* * Expands inner components to use all the space * available in this one. Only those who want x/y space * are expanded. */ static void expand(File* f) { Panel* p; Panel* np; File** w; File** l; File* fp; int n; int nwx, nwy; Point last; Point spare; int offset; int incr; int maxx, dx; p = f->aux; if (p->type != Qrow && p->type != Qcol) return; /* * Determine space and how many want x,y room */ nwx = nwy = maxx = 0; last = ZP; w = newfilewalk(f); for (l = w; fp = *l; l++){ if (!ispanel(fp)) continue; np = fp->aux; if (!(np->flags&Phide)){ dx = Dx(np->rect); if (dx > maxx) maxx = dx; if (np->wants.x == 1) nwx++; if (np->wants.y == 1) nwy++; last = np->rect.max; } } endfilewalk(w); spare = subpt(p->rect.max, last); spare = subpt(spare, Pt(Inset, Inset)); /* * Resize to consume spare space: */ /* 1. Try to make them equal sized. * By now, this is only done for rows. * Column processing would be equivalent. */ offset = 0; if (p->type == Qrow){ w = newfilewalk(f); for (l = w; fp = *l; l++){ if (!ispanel(fp)) continue; np = fp->aux; if (np->flags&Phide) continue; if (p->type == Qrow){ move(fp, Pt(offset, 0)); dx = maxx - Dx(np->rect); if (np->wants.x == 1 && spare.x > 0 && dx > 0){ incr = (dx > spare.x) ? spare.x : dx; np->rect.max.x += incr; offset += incr; spare.x -= incr; } } ldprint("expand.1: %s wx %d wy %d %dx%d %R\n", fp->name, np->wants.x, np->wants.y, Dx(np->rect), Dy(np->rect), np->rect); expand(fp); } endfilewalk(w); } /* 2. Proportional sharing of what remains * and extend the other coordinate to use whatever * empty space is there due to different sizes in that axys. */ offset = 0; w = newfilewalk(f); for (l = w; fp = *l; l++){ if (!ispanel(fp)) continue; np = fp->aux; if (np->flags&Phide) continue; if (p->type == Qcol){ move(fp, Pt(0, offset)); n = p->rect.max.x - Inset; if (np->rect.max.x < n) if (np->type == Qcol || np->type == Qrow || np->wants.x == 1) np->rect.max.x = n; if (np->wants.y == 1 && spare.y > 0){ incr = spare.y/nwy; np->rect.max.y += incr; offset += incr; } } else { move(fp, Pt(offset, 0)); n = p->rect.max.y - Inset; if (np->rect.max.y < n) if (np->type == Qcol || np->type == Qrow || np->wants.y == 1) np->rect.max.y = n; if (np->wants.x == 1 && spare.x > 0){ incr = spare.x/nwx; np->rect.max.x += incr; offset += incr; } } ldprint("expand.2: %s wx %d wy %d %dx%d %R\n", fp->name, np->wants.x, np->wants.y, Dx(np->rect), Dy(np->rect), np->rect); expand(fp); } endfilewalk(w); } void layout(File* f) { Rectangle r; Panel* p; p = f->aux; if (screen == nil) return; r = screen->r; p->rect = r; size(f); /* BUG: if none of /devs/ui/col:n wants.x/wants.y * we should set wants.x/wants.y for any of them. * Otherwise, the screen gets ugly. */ pack(f); p->rect = r; expand(f); }