Commit Diff


commit - 92803b004812d95b2147c6c8a54618bc26d7d98a
commit + a81dbe5ddfb13ed5d74cafe65fdce0abaf30d953
blob - 0acc85f1ffb1bd74b78377df58003831bf306669
blob + 87c3a615620c6002cee935e34c93f387b85e9cb5
--- mymenu.1
+++ mymenu.1
@@ -265,6 +265,12 @@ you've typed ``fire'' and the first completion is ``fi
 really want to choose ``fire''. While you can type some spaces, this
 keybinding is a more elegant way to change, at runtime, the behaviour
 of the first completion.
+.It Button1
+Confirm the clicked item
+.It Button3
+Confirm but keep looping (if enabled) the clicked item, like C-m
+.It Button4,Button5 / scroll
+Scroll through the completions (without changing the selection)
 .El
 .Sh EXIT STATUS
 0 when the user select an entry, 1 when the user press Esc, EX_USAGE
@@ -321,4 +327,6 @@ centered.
 As a general rule of thumb, if you're overriding the width and/or the
 height of the window, remember to override the x and y coordinates as
 well.
+.It
+Clicking past the last item will be equivalent to clicking the last item.
 .El
blob - b90e5bd093cc36c2ecc50d67aa0c9f8bf6aa7626
blob + 2614cfe00b435f178360885cffec255d8eb18f95
--- mymenu.1.md
+++ mymenu.1.md
@@ -400,7 +400,19 @@ C-i
 > really want to choose \`\`fire''. While you can type some spaces, this
 > keybinding is a more elegant way to change, at runtime, the behaviour
 > of the first completion.
+
+Button1
+
+> Confirm the clicked item
+
+Button3
+
+> Confirm but keep looping (if enabled) the clicked item, like C-m
 
+Button4,Button5 / scroll
+
+> Scroll through the completions (without changing the selection)
+
 # EXIT STATUS
 
 0 when the user select an entry, 1 when the user press Esc, EX\_USAGE
@@ -456,4 +468,4 @@ Omar Polo <omar.polo@europecom.net>
 	height of the window, remember to override the x and y coordinates as
 	well.
 
-Void Linux - October 18, 2019
+Void Linux - October 19, 2019
blob - a55338f82b1ebd2768cd614edb414bbaad506cbe
blob + 5536c9ee22520d3992c12e00937bec30034ef8f6
--- mymenu.c
+++ mymenu.c
@@ -74,6 +74,7 @@ enum obj_type { PROMPT, COMPL, COMPL_HIGH };
 
 /* These are the possible action to be performed after user input. */
 enum action {
+	NO_OP,
 	EXIT,
 	CONFIRM,
 	CONFIRM_CONTINUE,
@@ -83,11 +84,13 @@ enum action {
 	DEL_WORD,
 	DEL_LINE,
 	ADD_CHAR,
-	TOGGLE_FIRST_SELECTED
+	TOGGLE_FIRST_SELECTED,
+	SCROLL_DOWN,
+	SCROLL_UP,
 };
 
 /* A big set of values that needs to be carried around for drawing. A
-big struct to rule them all */
+ * big struct to rule them all */
 struct rendering {
 	Display *d; /* Connection to xorg */
 	Window w;
@@ -101,7 +104,7 @@ struct rendering {
 		       the borders) */
 	int y_zero; /* like x_zero but for the y axis */
 
-	size_t offset; /* scroll offset */
+	int offset; /* scroll offset */
 
 	short free_text;
 	short first_selected;
@@ -144,6 +147,8 @@ struct rendering {
 struct completion {
 	char *completion;
 	char *rcompletion;
+	int offset; /* the x (or y, depending on the layout) coordinate at
+		       which the item is rendered */
 };
 
 /* Wrap the linked list of completions */
@@ -743,12 +748,17 @@ draw_horizontally(struct rendering *r, char *text, str
 		enum obj_type t
 			= cs->selected == (ssize_t)i ? COMPL_HIGH : COMPL;
 
+		cs->completions[i].offset = x;
+
 		x += draw_h_box(
 			r, x, NULL, 0, t, cs->completions[i].completion);
 
 		if (x > inner_width(r))
 			break;
 	}
+
+	for (i += 1; i < cs->length; ++i)
+		cs->completions[i].offset = -1;
 }
 
 /* ,-----------------------------------------------------------------, */
@@ -770,12 +780,17 @@ draw_vertically(struct rendering *r, char *text, struc
 		enum obj_type t
 			= cs->selected == (ssize_t)i ? COMPL_HIGH : COMPL;
 
+		cs->completions[i].offset = y;
+
 		y += draw_v_box(
 			r, y, NULL, 0, t, cs->completions[i].completion);
 
 		if (y > inner_height(r))
 			break;
 	}
+
+	for (i += 1; i < cs->length; ++i)
+		cs->completions[i].offset = -1;
 }
 
 void
@@ -1184,6 +1199,71 @@ confirm(enum state *status, struct rendering *r, struc
 
 	if (!r->free_text) /* cannot accept arbitrary text */
 		*status = LOOPING;
+}
+
+/* cs: completion list
+ * offset: the offset of the click
+ * first: the first (rendered) item
+ * def: the default action
+ */
+enum action
+select_clicked(
+	struct completions *cs, size_t offset, size_t first, enum action def)
+{
+	ssize_t selected = first;
+	int set = 0;
+
+	if (cs->length == 0)
+		return NO_OP;
+
+	if (offset < cs->completions[selected].offset)
+		return NO_OP;
+
+	/* skip the first entry */
+	for (selected += 1; selected < cs->length; ++selected) {
+		if (cs->completions[selected].offset == -1) {
+			printf("caught -1\n");
+			break;
+		}
+		if (offset < cs->completions[selected].offset) {
+			cs->selected = selected - 1;
+			set = 1;
+			break;
+		}
+	}
+
+	if (!set)
+		cs->selected = selected - 1;
+
+	return def;
+}
+
+enum action
+handle_mouse(
+	struct rendering *r, struct completions *cs, XButtonPressedEvent *e)
+{
+	size_t off;
+
+	if (r->horizontal_layout)
+		off = e->x;
+	else
+		off = e->y;
+
+	switch (e->button) {
+	case Button1:
+		return select_clicked(cs, off, r->offset, CONFIRM);
+
+	case Button3:
+		return select_clicked(cs, off, r->offset, CONFIRM_CONTINUE);
+
+	case Button4:
+		return SCROLL_UP;
+
+	case Button5:
+		return SCROLL_DOWN;
+	}
+
+	return NO_OP;
 }
 
 /* event loop */
@@ -1221,11 +1301,22 @@ loop(struct rendering *r, char **text, int *textlen, s
 			draw(r, *text, cs);
 			break;
 
-		case KeyPress: {
-			XKeyPressedEvent *ev = (XKeyPressedEvent *)&e;
-			char *input;
+		case KeyPress:
+		case ButtonPress: {
+			enum action a;
+			char *input = NULL;
 
-			switch (parse_event(r->d, ev, r->xic, &input)) {
+			if (e.type == KeyPress)
+				a = parse_event(r->d, (XKeyPressedEvent *)&e,
+					r->xic, &input);
+			else
+				a = handle_mouse(
+					r, cs, (XButtonPressedEvent *)&e);
+
+			switch (a) {
+			case NO_OP:
+				break;
+
 			case EXIT:
 				status = ERR;
 				break;
@@ -1323,25 +1414,16 @@ loop(struct rendering *r, char **text, int *textlen, s
 				if (!r->first_selected && cs->selected == 0)
 					cs->selected = -1;
 				break;
-			}
-		}
 
-		case ButtonPress: {
-			XButtonPressedEvent *ev = (XButtonPressedEvent *)&e;
-			/* if (ev->button == Button1) { /\* click *\/ */
-			/*   int x = ev->x - r.border_w; */
-			/*   int y = ev->y - r.border_n; */
-			/*   fprintf(stderr, "Click @ (%d, %d)\n", x, y); */
-			/* } */
-
-			if (ev->button == Button4) /* scroll up */
-				r->offset = MAX((ssize_t)r->offset - 1, 0);
-
-			if (ev->button == Button5) /* scroll down */
+			case SCROLL_DOWN:
 				r->offset
 					= MIN(r->offset + 1, cs->length - 1);
+				break;
 
-			break;
+			case SCROLL_UP:
+				r->offset = MAX((ssize_t)r->offset - 1, 0);
+				break;
+			}
 		}
 		}