shithub: view

Download patch

ref: 49f6899470224eab8fc501e50598ca29da22de85
parent: 66c483d5d3e64d53490dc1a8fe51351cdd047e7b
author: phil9 <telephil9@gmail.com>
date: Fri Nov 26 00:44:27 EST 2021

add separators in menu2

	i don't care what you think :]

--- a/a.h
+++ b/a.h
@@ -15,3 +15,6 @@
 int		writefile(char*, char*, int);
 int		fileformat(char*);
 Image*	ipipeto(Image*, char*);
+
+int sepmenuhit(int, Mousectl*, Menu*, Screen*);
+
--- /dev/null
+++ b/sepmenuhit.c
@@ -1,0 +1,295 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+
+enum
+{
+	Margin = 4,		/* outside to text */
+	Border = 2,		/* outside to selection boxes */
+	Blackborder = 2,	/* width of outlining border */
+	Vspacing = 2,		/* extra spacing between lines of text */
+	Maxunscroll = 25,	/* maximum #entries before scrolling turns on */
+	Nscroll = 20,		/* number entries in scrolling part */
+	Scrollwid = 14,		/* width of scroll bar */
+	Gap = 4,			/* between text and scroll bar */
+};
+
+static	Image	*menutxt;
+static	Image	*back;
+static	Image	*high;
+static	Image	*bord;
+static	Image	*text;
+static	Image	*htext;
+
+static
+void
+menucolors(void)
+{
+	/* Main tone is greenish, with negative selection */
+	back = allocimagemix(display, DPalegreen, DWhite);
+	high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen);	/* dark green */
+	bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen);	/* not as dark green */
+	if(back==nil || high==nil || bord==nil)
+		goto Error;
+	text = display->black;
+	htext = back;
+	return;
+
+    Error:
+	freeimage(back);
+	freeimage(high);
+	freeimage(bord);
+	back = display->white;
+	high = display->black;
+	bord = display->black;
+	text = display->black;
+	htext = display->white;
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the rectangle, including its black edge, holding element i.
+ */
+static Rectangle
+menurect(Rectangle r, int i)
+{
+	if(i < 0)
+		return Rect(0, 0, 0, 0);
+	r.min.y += (font->height+Vspacing)*i;
+	r.max.y = r.min.y+font->height+Vspacing;
+	return insetrect(r, Border-Margin);
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the element number containing p.
+ */
+static int
+menusel(Rectangle r, Point p)
+{
+	if(!ptinrect(p, r))
+		return -1;
+	return (p.y-r.min.y)/(font->height+Vspacing);
+}
+
+static int
+issep(Menu *menu, int off, int i)
+{
+	char *item;
+
+	item = menu->item ? menu->item[i+off] : (*menu->gen)(i+off);
+	return (item[0] == '-' && item[1] == 0);
+}
+
+static
+void
+paintitem(Image *m, Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
+{
+	char *item;
+	Rectangle r;
+	Point pt;
+	int y;
+
+	if(i < 0)
+		return;
+	r = menurect(textr, i);
+	if(restore){
+		draw(m, r, restore, nil, restore->r.min);
+		return;
+	}
+	if(save)
+		draw(save, save->r, m, nil, r.min);
+	item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
+	pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
+	pt.y = textr.min.y+i*(font->height+Vspacing);
+	if(issep(menu, off, i)){
+		y = pt.y + (font->height + Vspacing) / 2;
+		draw(screen, r, back, nil, pt);
+		line(screen, Pt(textr.min.x - Margin, y), Pt(textr.max.x + Margin, y), 0, 0, 0, bord, ZP);
+	}else{
+		draw(m, r, highlight? high : back, nil, pt);
+		string(m, pt, highlight? htext : text, pt, font, item);
+	}
+}
+
+/*
+ * menur is a rectangle holding all the highlightable text elements.
+ * track mouse while inside the box, return what's selected when button
+ * is raised, -1 as soon as it leaves box.
+ * invariant: nothing is highlighted on entry or exit.
+ */
+static int
+menuscan(Image *m, Menu *menu, int but, Mousectl *mc, Rectangle textr, int off, int lasti, Image *save)
+{
+	int i;
+
+	paintitem(m, menu, textr, off, lasti, 1, save, nil);
+	for(readmouse(mc); mc->buttons & (1<<(but-1)); readmouse(mc)){
+		i = menusel(textr, mc->xy);
+		if(i != -1 && i == lasti)
+			continue;
+		paintitem(m, menu, textr, off, lasti, 0, nil, save);
+		if(i == -1)
+			return i;
+		lasti = i;
+		paintitem(m, menu, textr, off, lasti, 1, save, nil);
+	}
+	return lasti;
+}
+
+static void
+menupaint(Image *m, Menu *menu, Rectangle textr, int off, int nitemdrawn)
+{
+	int i;
+
+	draw(m, insetrect(textr, Border-Margin), back, nil, ZP);
+	for(i = 0; i<nitemdrawn; i++)
+		paintitem(m, menu, textr, off, i, 0, nil, nil);
+}
+
+static void
+menuscrollpaint(Image *m, Rectangle scrollr, int off, int nitem, int nitemdrawn)
+{
+	Rectangle r;
+
+	draw(m, scrollr, back, nil, ZP);
+	r.min.x = scrollr.min.x;
+	r.max.x = scrollr.max.x;
+	r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
+	r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
+	if(r.max.y < r.min.y+2)
+		r.max.y = r.min.y+2;
+	border(m, r, 1, bord, ZP);
+	if(menutxt == 0)
+		menutxt = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkgreen);	/* border color; BUG? */
+	if(menutxt)
+		draw(m, insetrect(r, 1), menutxt, nil, ZP);
+}
+
+int
+sepmenuhit(int but, Mousectl *mc, Menu *menu, Screen *scr)
+{
+	int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
+	int scrolling;
+	Rectangle r, menur, sc, textr, scrollr;
+	Image *b, *save, *backup;
+	Point pt;
+	char *item;
+
+	if(back == nil)
+		menucolors();
+	sc = screen->clipr;
+	replclipr(screen, 0, screen->r);
+	maxwid = 0;
+	for(nitem = 0;
+	    item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
+	    nitem++){
+		i = stringwidth(font, item);
+		if(i > maxwid)
+			maxwid = i;
+	}
+	if(menu->lasthit<0 || menu->lasthit>=nitem)
+		menu->lasthit = 0;
+	screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
+	if(nitem>Maxunscroll || nitem>screenitem){
+		scrolling = 1;
+		nitemdrawn = Nscroll;
+		if(nitemdrawn > screenitem)
+			nitemdrawn = screenitem;
+		wid = maxwid + Gap + Scrollwid;
+		off = menu->lasthit - nitemdrawn/2;
+		if(off < 0)
+			off = 0;
+		if(off > nitem-nitemdrawn)
+			off = nitem-nitemdrawn;
+		lasti = menu->lasthit-off;
+	}else{
+		scrolling = 0;
+		nitemdrawn = nitem;
+		wid = maxwid;
+		off = 0;
+		lasti = menu->lasthit;
+	}
+	r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
+	r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
+	r = rectaddpt(r, mc->xy);
+	pt = ZP;
+	if(r.max.x>screen->r.max.x)
+		pt.x = screen->r.max.x-r.max.x;
+	if(r.max.y>screen->r.max.y)
+		pt.y = screen->r.max.y-r.max.y;
+	if(r.min.x<screen->r.min.x)
+		pt.x = screen->r.min.x-r.min.x;
+	if(r.min.y<screen->r.min.y)
+		pt.y = screen->r.min.y-r.min.y;
+	menur = rectaddpt(r, pt);
+	textr.max.x = menur.max.x-Margin;
+	textr.min.x = textr.max.x-maxwid;
+	textr.min.y = menur.min.y+Margin;
+	textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
+	if(scrolling){
+		scrollr = insetrect(menur, Border);
+		scrollr.max.x = scrollr.min.x+Scrollwid;
+	}else
+		scrollr = Rect(0, 0, 0, 0);
+
+	if(scr){
+		b = allocwindow(scr, menur, Refbackup, DWhite);
+		if(b == nil)
+			b = screen;
+		backup = nil;
+	}else{
+		b = screen;
+		backup = allocimage(display, menur, screen->chan, 0, -1);
+		if(backup)
+			draw(backup, menur, screen, nil, menur.min);
+	}
+	draw(b, menur, back, nil, ZP);
+	border(b, menur, Blackborder, bord, ZP);
+	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
+	r = menurect(textr, lasti);
+	if(pt.x || pt.y)
+		moveto(mc, divpt(addpt(r.min, r.max), 2));
+	menupaint(b, menu, textr, off, nitemdrawn);
+	if(scrolling)
+		menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
+	while(mc->buttons & (1<<(but-1))){
+		lasti = menuscan(b, menu, but, mc, textr, off, lasti, save);
+		if(lasti >= 0)
+			break;
+		while(!ptinrect(mc->xy, textr) && (mc->buttons & (1<<(but-1)))){
+			if(scrolling && ptinrect(mc->xy, scrollr)){
+				noff = ((mc->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
+				noff -= nitemdrawn/2;
+				if(noff < 0)
+					noff = 0;
+				if(noff > nitem-nitemdrawn)
+					noff = nitem-nitemdrawn;
+				if(noff != off){
+					off = noff;
+					menupaint(b, menu, textr, off, nitemdrawn);
+					menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
+				}
+			}
+			readmouse(mc);
+		}
+	}
+	if(b != screen)
+		freeimage(b);
+	if(backup){
+		draw(screen, menur, backup, nil, menur.min);
+		freeimage(backup);
+	}
+	freeimage(save);
+	replclipr(screen, 0, sc);
+	flushimage(display, 1);
+	if(lasti >= 0){
+		menu->lasthit = lasti+off;
+		if(issep(menu, off, lasti))
+			return -1;
+		return menu->lasthit;
+	}
+	return -1;
+}
--- a/utils.c
+++ b/utils.c
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
+#include <mouse.h>
 #include "a.h"
 
 void*
--- a/view.c
+++ b/view.c
@@ -43,10 +43,13 @@
 	Mzoomin,
 	Mzoomout,
 	Morigsize,
+	Mzsep,
 	Mflip,
 	Mflop,
+	Mfsep,
 	Mrotcw,
 	Mrotccw,
+	Mrsep,
 	Mpipeto,
 };
 char *menu2str[] =
@@ -54,10 +57,13 @@
 	"zoom in",
 	"zoom out",
 	"orig. size",
+	"-",
 	"flip",
 	"flop",
+	"-",
 	"rotate cw",
 	"rotate ccw",
+	"-",
 	"pipe to...",
 	nil,
 };
@@ -335,7 +341,7 @@
 	int n;
 	char buf[255];
 
-	n = menuhit(2, mctl, &menu2, nil);
+	n = sepmenuhit(2, mctl, &menu2, nil);
 	switch(n){
 	case Mzoomin:
 	case Mzoomout: