Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/live/layout.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.