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