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