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

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


implement Menus;

include "mods.m";
mods, debug, win, tree: import dat;

Cpointer, SET, getfont, cols, TEXT, BACK, BACK0, BORD, MSET, MCLEAR, MBACK,
maxpt, Tagwid, Taght, Inset, setcursor, cookclick, SHAD, Arrow: import gui;
Qcol, Qrow, Qatom, Ptag, Phide, Playout, Panel: import Wpanel;

Rx: con 60;
Ry: con 35;
Borwid: con 3;	# border width, for shadow

init(d: Livedat)
{
	dat = d;
	initmods();
}

nullmenu: Menu;

Menu.new(opts: array of string): ref Menu
{
	mn:= ref nullmenu;
	mn.opts = array[len opts] of Opt;
	nopts := len opts;
	if (nopts%2)
		nopts++;
	mn.∆φ = (2.0 * Math->Pi) / real nopts;
	φ := 0.0;
	for (i := 0; i < len opts; i++){
		mn.opts[i] = Opt(opts[i], φ, nil, nil, (0,0), ((0,0), (0,0)) );
		φ += mn.∆φ;
	}
	return mn;
}

mkoptimage(r: Rect, s: string, font: ref Font, c: int): ref Image
{
	dpy := win.display;
	i := dpy.newimage(r, win.image.chans, 0, Draw->White);
	i.draw(i.r, cols[MBACK], nil, (0,0));
	i.border(r, 1, cols[BORD], (0,0));
	pt := Point(Inset/2, Inset/2);
	pt.x += (r.dx() - font.width(s)) / 2;
	i.text(pt, cols[c], (0,0), font, s);
	return i;
}

Menu.mk(mn: self ref Menu)
{
	dpy := win.display;
	font := getfont("B");
	maxwid := 0;
	for (i := 0; i < len mn.opts; i++){
		wid := font.width(mn.opts[i].name);
		if (wid +4 > maxwid)
			maxwid = wid + 4;
	}
	rx := Rx + maxwid/2 + Borwid;
	ry := Ry + font.height/2 + Borwid;
	mn.r = Rect((0,0), (2*rx, 2*ry));
	mn.saved = dpy.newimage(mn.r, win.image.chans, 0, Draw->Black);
	r := Rect((0,0), (maxwid, font.height));
	for(i = 0; i < len mn.opts; i++){
		# center of the option
		mn.opts[i].pt.x = rx + int (real Rx * math->cos(mn.opts[i].φ));
		mn.opts[i].pt.y = ry + int (real Ry * math->sin(mn.opts[i].φ));
		# top-left corner
		mn.opts[i].pt.x -= maxwid/2;
		mn.opts[i].pt.y -= (font.height/2);
		mn.opts[i].si = mkoptimage(r, mn.opts[i].name, font, MSET);
		mn.opts[i].ci = mkoptimage(r, mn.opts[i].name, font, MCLEAR);
	}
}

Menu.draw(mn: self ref Menu, min: Point, set, first: int)
{
	c := min.add((mn.r.dx()/2, mn.r.dy()/2));
	a := Image.arrow(0,0,0);
	win.image.line(c.add((-15,15)),c.add((15, -10)), a, a, 0, cols[TEXT], (0,0));
	win.image.line(c.add((15,15)),c.add((-15, -10)), a, a, 0, cols[TEXT], (0,0));
	for (i := 0; i < len mn.opts; i++){
		dpt := min.add(mn.opts[i].pt);
		mn.opts[i].sr = mn.opts[i].si.r.addpt(dpt);
		img := mn.opts[i].ci;
		if (set == i)
			img = mn.opts[i].si;
		if (first){
			# draw the shadow just once.
			shadr := mn.opts[i].sr.addpt((Borwid-1, Borwid-1));
			win.image.draw(shadr, cols[TEXT], cols[SHAD], (0,0));
		}
		win.image.draw(mn.opts[i].sr, img, nil, (0,0));
	}
}

Menu.optat(mn: self ref Menu, pt: Point): int
{
	# pt in sector [opt.φ - ∆φ/2, opt.φ + ∆φ/2 ]
	if (pt.y == 0)
		pt.y = 1;
	if (pt.x == 0)
		pt.x = 1;
	φ := math->atan2(real - pt.y, real - pt.x) + Math->Pi; # φ in [0, 2π]

	if (φ > 2.0*Math->Pi)
		φ -= 2.0*Math->Pi;
	x := int (φ / mn.∆φ);

	# fprint(stderr, "φ %g n %d\n", φ, int (φ / mn.∆φ));
	# might consider doing nothing when the angle is not clear enough
	# to avoid mistakes.
	if (x < 0 || x > len mn.opts -1)
		return -1;
	return x;
}

Menu.mouse(mn: self ref Menu, nil: Point, m: ref Cpointer, mc: chan of ref Cpointer): string
{
	xy := m.xy;
	m = <-mc;
	case m.buttons {
	4 =>
		do {
			m = <-mc;
		} while (m.buttons);
		return mn.opts[mn.last].name;
	0 =>
		d := oxy := Point(0,0);
		do {
			m = <-mc;
			oxy = d = m.xy.sub(xy);
			if (d.x < 0)
				d.x = -d.x;
			if (d.y < 0)
				d.y = -d.y;
		} while (d.x < 20 && d.y < 20 && !m.buttons);
		case m.buttons {
		0 =>
			sys->sleep(100);
			moved: int;
			do {
				moved = 0;
				alt { m = <-mc => moved = 1; * => moved = 0; }
			} while (moved);
			id := mn.optat(oxy);
			if (id < 0)
				return nil;
			mn.last = id;
			return mn.opts[mn.last].name;
		* =>
			do {
				m = <-mc;
			} while (m.buttons);
			return nil;
		}
	1 =>
		return "scroll";
	* =>
		do {
			m = <-mc;
		} while (m.buttons);
		return nil;
	}
}

Menu.run(mn: self ref Menu, m: ref Cpointer, mc: chan of ref Cpointer): string
{
	at := m.xy;
	if (mn.opts[0].si == nil)
		mn.mk();
	min := at.add((-mn.r.dx()/2, -mn.r.dy()/2));
	smin := min;
	if (smin.x < win.image.r.min.x)
		smin.x = win.image.r.min.x;
	if (smin.y < win.image.r.min.y)
		smin.y = win.image.r.min.y;
	if (smin.y + mn.r.dy() > win.image.r.max.y)
		smin.y = win.image.r.max.y - mn.r.dy();
	if (smin.x + mn.r.dx() > win.image.r.max.x)
		smin.x = win.image.r.max.x - mn.r.dx();
	if (!smin.eq(min)){
		min = smin;
		nm := min.add((mn.r.dx()/2, mn.r.dy()/2));
		win.wmctl("ptr " + string nm.x + " " + string nm.y);
		m.xy.x = nm.x; m.xy.y = nm.y;	# adjust or φ will be wrong
	}
	mn.sr = mn.saved.r.addpt(min);
	mn.saved.draw(mn.r, win.image, nil, min);
	mn.draw(min, mn.last, 1);
	opt := mn.mouse(min, m, mc);
	win.wmctl("ptr " + string at.x + " " + string at.y);
	win.image.draw(mn.r.addpt(min), mn.saved, nil, (0,0));
	return opt;
}

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.