Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/pierio/sys/src/libdraw/piemenuhit.c

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


#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>

static double
dist(Point p0, Point p1)
{
	int dx, dy;
	dx = p1.x - p0.x;
	dy = p1.y - p0.y;
	return sqrt(dx*dx + dy*dy);
}

static int
pie(int but, Mousectl *mc, Screen *scr, char *item[], int nitem, int nsub)
{
	Rectangle r1, r2, sc, menur;
	Point p, p0, pz;
	int i, hit, r, l, rmin, rmax;
	Image *b, *backup, *color[3];
	char *s;
	double a0, a, e;

	hit = -1;

	e = nitem > 0 ? 2*PI/nitem : 2*PI;

	// the 1st at 12 o'clock
	a0 = 2*PI-PI/2;

	rmin = 16;
	rmax = rmin * 2;

	p = mc->xy;

Resize:
	r2 = Rect(0, 0, 0, 0);
	for(i = 0, a = a0; i < nitem; i++, a += e, r2 = r1){
		/* middle ring radius where the label is placed */
		r = rmin + (rmax - rmin)/2;

		p0 = addpt(p, Pt(cos(a)*r, sin(a)*r));

		pz = stringsize(font, item[i]);
		r1.min = subpt(p0, Pt(pz.x/2, pz.y/2));
		r1.max = addpt(p0, Pt(pz.x/2, pz.y/2));
		r1 = insetrect(r1, -1);

		/*
		 * if the label is overlapping with its previous one,
		 * increase the inner ring radius
		 */
		if(rectXrect(r2, r1)){
			rmin++;
			if(rmax < rmin)
				rmax = rmin;
			goto Resize;
		}

		/*
		 * if any corner of the label crosses the outer radius,
		 * increase the outer ring radius.
		 */
		l = dist(p, Pt(r1.min.x, r1.min.y));
		if(l > rmax){
			rmax++;
			goto Resize;
		}
		l = dist(p, Pt(r1.min.x, r1.max.y));
		if(l > rmax){
			rmax++;
			goto Resize;
		}
		l = dist(p, Pt(r1.max.x, r1.min.y));
		if(l > rmax){
			rmax++;
			goto Resize;
		}
		l = dist(p, Pt(r1.max.x, r1.max.y));
		if(l > rmax){
			rmax++;
			goto Resize;
		}
	}

	rmin -= 2;
	rmax += 2;

	sc = screen->clipr;
	replclipr(screen, 0, screen->r);

	/* as we know the size now, translate to screen */
	if(p.x + rmax > screen->r.max.x)
		p.x = screen->r.max.x - rmax;
	if(p.x - rmax < screen->r.min.x)
		p.x = screen->r.min.x + rmax;
	if(p.y + rmax > screen->r.max.y)
		p.y = screen->r.max.y - rmax;
	if(p.y - rmax < screen->r.min.y)
		p.y = screen->r.min.y + rmax;

	moveto(mc, p);

	menur.min = Pt(p.x - rmax-2, p.y - rmax-2);
	menur.max = Pt(p.x + rmax+2, p.y + rmax+2);

	if(scr){
		b = allocwindow(scr, menur, Refbackup, DNofill);
		if(b == nil)
			b = screen;
		backup = nil;
	}else{
		b = screen;
		backup = allocimage(display, menur, screen->chan, 0, DNofill);
		if(backup)
			draw(backup, menur, screen, nil, menur.min);
	}

	color[0] = allocimagemix(display, DPalegreen, DWhite);
	color[1] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen);
	color[2] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen);

Redraw:
	fillellipse(b, p, rmax, rmax, color[0], ZP);
	ellipse(b, p, rmin, rmin, 1, color[1], ZP);
	ellipse(b, p, rmax, rmax, 1, color[1], ZP);
	for(i = 0, a = a0; i < nitem; i++, a += e){
		s = item[i];
		pz = stringsize(font, s);

		r = rmin + (rmax - rmin)/2;
		p0 = addpt(p, Pt(cos(a)*r, sin(a)*r));

		if(i == hit){
			Point ap[3];

			r = (pz.x < pz.y ? pz.x : pz.y) / 2;

			ap[0] = p;
			ap[1] = addpt(p0, Pt(cos(a - PI/2)*r, sin(a - PI/2)*r));
			ap[2] = addpt(p0, Pt(cos(a + PI/2)*r, sin(a + PI/2)*r));
			fillpoly(b, ap, nelem(ap), 0, color[2], ZP);

			r1.min = subpt(p0, Pt(pz.x/2, pz.y/2));
			r1.max = addpt(p0, Pt(pz.x/2, pz.y/2));
			r1 = insetrect(r1, -1);
			draw(b, r1, color[2], nil, ZP);
			string(b, subpt(p0, Pt(pz.x/2, pz.y/2)), color[0], ZP, font, s);
		} else {
			string(b, subpt(p0, Pt(pz.x/2, pz.y/2)), display->black, ZP, font, s);
		}
	}
	flushimage(display, 1);

	while(mc->buttons & (1<<(but-1))){
		l = dist(p, mc->xy);

		i = -1;
		if(l > 4){
			for(i = 0, a = a0; i < nitem; i++, a += e){
				double b;

				b = atan2((mc->xy.y - p.y),(mc->xy.x - p.x)) - a + e/2;
				while(b < 0)
					b += 2*PI;
				while(b >= 2*PI)
					b -= 2*PI;
				if(b < e)
					break;
			}
			if(i == nitem)
				i = -1;
		}

		if(l >= rmax){
			if(nsub < 0){
				hit = -1;
				break;
			}
			if(nsub>0 && nitem>0 && i == nitem-1){
				int k;

				k = pie(but, mc, scr, item + nitem, nsub, -1);
				if(k != -1){
					hit = k + nitem;
					break;
				}
			}
			i = -1;
		}

		if(hit != i){
			hit = i;
			goto Redraw;
		}

		readmouse(mc);
	}

	if(b != screen)
		freeimage(b);
	if(backup){
		draw(screen, menur, backup, nil, menur.min);
		freeimage(backup);
	}
	replclipr(screen, 0, sc);
	flushimage(display, 1);

	for(i = 0; i < nelem(color); i++)
		freeimage(color[i]);

	return hit;
}

int
piemenuhit(int but, Mousectl *mc, Menu *menu, Screen *scr)
{
	char *s, **item;
	int i, n, nitem, nsub, hit;

	item = nil;
	i = n = 0;
	while(s = menu->item ? menu->item[i] : menu->gen(i)){		
		i++;
		if(n % 16 == 0)
			item = realloc(item, (n + 16) * sizeof(item[0]));
		if(s[0] == '[' && s[strlen(s)-1] == ']'){
			s = strdup(s+1);
			s[strlen(s)-1] = 0;
			item[n++] = s;
			break;
		} else {
			item[n++] = strdup(s);
		}
	}
	nitem = n;
	while(s = menu->item ? menu->item[i] : menu->gen(i)){		
		i++;
		if(n % 16 == 0)
			item = realloc(item, (n + 16) * sizeof(item[0]));
		item[n++] = strdup(s);
	}
	nsub = n - nitem;

	hit = pie(but, mc, scr, item, nitem, nsub);

	for(i = 0; i<n; i++)
		free(item[i]);
	free(item);

	if(hit != -1)
		menu->lasthit = hit;

	return hit;
}


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.