implement Layout; include "mods.m"; mods, debug, win, tree: import dat; Cpointer, maxpt, Tagwid, Taght, Inset, setcursor, cookclick, Arrow: import gui; Qcol, Qrow, Qatom, Ptag, Phide, Predraw, Playout, Panel: import Wpanel; # This follows conventions from draw. Rectangles do not include their max points. # Also, BUG: a way to let the user adjust the size of panels would be nice. Perhaps by using # % of avail space instead of just equal dividing # The argument is that we have so many screens that they should be handled by the machine, # and not by us. But who knows... init(d: Livedat) { dat = d; initmods(); } # # Compute size and record if we want more x/y room # by looking into the inner components. # Save also current rect in orect. # size(p: ref Panel) { p.orect = p.rect; case p.rowcol { Qcol or Qrow => if (p.flags&Ptag) p.size = Point(Tagwid + Inset, Inset); else p.size = Point(Inset, Inset); p.wants = Point(0,0); some := 0; for (i := 0; i < len p.child; i++){ np := p.child[i]; if (np.flags&Phide) continue; some++; size(np); # We ignore wants.[xy] != 1. See comment below. if (np.wants.x == 1) p.wants.x = 1; if (np.wants.y == 1) p.wants.y = 1; if (p.rowcol == 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; } } if (!some && (p.flags&Playout)) p.wants = Point(1,1); p.size = p.size.add(Point(Inset, Inset)); if (p.size.x < 20) p.size.x = 20; if (p.size.y < 10) p.size.y = 10; Qatom => p.size = p.minsz; p.size = maxpt(p.size, Point(Inset, Inset)); # Heuristic to keep tiny text panels # small. If they don't want too much, # we assing the space and ignore what they 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; # round to 8n to avoid extra resizes n := p.size.x%10; if (n > 0) p.size.x += 10-n; n = p.size.y%10; if (n > 0) p.size.y += 10-n; } if (!p.orect.eq(p.rect)) p.flags |= Predraw; } # # 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. # pack(p: ref Panel) { case p.rowcol { Qcol or Qrow => r := p.rect.inset(Inset); if (p.flags&Ptag) r.min.x += Tagwid; max := r.min; # r is always the avail rectangle. # max is the max. point used. some := 0; for (i := 0; i < len p.child; i++){ np := p.child[i]; if (np.flags&Phide) continue; some++; np.rect = r; pack(np); (np.rect, nil) = np.rect.clip(p.rect); max = maxpt(max, np.rect.max); if (p.rowcol == Qcol) r.min.y = np.rect.max.y + Inset; else r.min.x = np.rect.max.x + Inset; } if (!some) packatom(p); p.rect.max = max.add(Point(Inset, Inset)); Qatom => packatom(p); } if (debug['L'] > 1) fprint(stderr, "pack %s: %03dx%03d [%d %d %d %d]\n", p.name, p.rect.dx(), p.rect.dy(), p.rect.min.x, p.rect.min.y, p.rect.max.x, p.rect.max.y); } packatom(p: ref Panel) { if (p.size.x > p.rect.dx()) p.rect.max.x = p.rect.min.x + p.rect.dx(); else p.rect.max.x = p.rect.min.x + p.size.x; if (p.size.y > p.rect.dy()) p.rect.max.y = p.rect.min.y + p.rect.dy(); else p.rect.max.y = p.rect.min.y + p.size.y; } move(p: ref Panel, pt: Point) { p.rect = p.rect.addpt(pt); for (i := 0; i < len p.child; i++){ np := p.child[i]; if (!(np.flags&Phide)) move(np, pt); } } # # Expands inner components to use all the space # available in this one. Only those who want x/y space # are expanded. # expand(p: ref Panel) { if (p.rowcol == Qatom) # atoms do not expand return; # # Determine space and how many ones want x,y room # nwx := nwy := maxx := 0; last := Point(0,0); for (i := 0; i < len p.child; i++){ np := p.child[i]; if (!(np.flags&Phide)){ dx := np.rect.dx(); if (dx > maxx) maxx = dx; if (np.wants.x == 1) nwx++; if (np.wants.y == 1) nwy++; last = np.rect.max; } } spare := p.rect.max.sub(last); spare = spare.sub(Point(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. Not done. # offset := 0; if (p.rowcol == Qrow) for (i = 0; i < len p.child; i++){ np := p.child[i]; if (np.flags&Phide) continue; if (p.rowcol == Qrow){ move(np, Point(offset, 0)); dx := maxx - np.rect.dx(); if (np.wants.x == 1 && spare.x > 0 && dx > 0){ incr := dx; if (dx > spare.x) incr = spare.x; np.rect.max.x += incr; offset += incr; spare.x -= incr; } } if (debug['L'] > 1) fprint(stderr, "expand.1: %s wx %d wy %d %dx%d [%d %d %d %d]\n", np.name, np.wants.x, np.wants.y, np.rect.dx(), np.rect.dy(), np.rect.min.x, np.rect.min.y, np.rect.max.x, np.rect.max.y); expand(np); } # 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. # incr := offset = 0; for (i = 0; i < len p.child; i++){ np := p.child[i]; if (np.flags&Phide) continue; if (p.rowcol == Qcol){ move(np, Point(0, offset)); n := p.rect.max.x - Inset; if (np.rect.max.x < n) if (np.rowcol == Qcol || np.rowcol == 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(np, Point(offset, 0)); n := p.rect.max.y - Inset; if (np.rect.max.y < n) if (np.rowcol == Qcol || np.rowcol == 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; } } if (debug['L'] > 1) fprint(stderr, "expand.2: %s wx %d wy %d %dx%d [%d %d %d %d]\n", p.name, np.wants.x, np.wants.y, np.rect.dx(), np.rect.dy(), np.rect.min.x, np.rect.min.y, np.rect.max.x, np.rect.max.y); expand(np); } } layout(p: ref Panel) { # if (p.parent == nil) p.rect = win.image.r; r := p.rect; size(p); pack(p); p.rect = r; expand(p); }