Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/cmd/omero/layout.c

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


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <draw.h>
#include <mouse.h>
#include <frame.h>
#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);
}

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.