Added support for double/triple click+dragging.
Now double-click+dragging automatically snaps both ends to word boundaries (unless on series of spaces), and triple-click selects whole lines. As a side effect, snapping now occurs on button press, not button release like it previously was, but I hope that won't be inconvenient for anyone. Signed-off-by: Christoph Lohmann <20h@r-36.net>
This commit is contained in:
		
				
					committed by
					
						 Christoph Lohmann
						Christoph Lohmann
					
				
			
			
				
	
			
			
			
						parent
						
							3c546ae739
						
					
				
				
					commit
					872a7f18ea
				
			
							
								
								
									
										90
									
								
								st.c
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								st.c
									
									
									
									
									
								
							| @@ -135,6 +135,11 @@ enum selection_type { | |||||||
| 	SEL_RECTANGULAR = 2 | 	SEL_RECTANGULAR = 2 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | enum selection_snap { | ||||||
|  | 	SNAP_WORD = 1, | ||||||
|  | 	SNAP_LINE = 2 | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* bit macro */ | /* bit macro */ | ||||||
| #undef B0 | #undef B0 | ||||||
| enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 }; | enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 }; | ||||||
| @@ -232,6 +237,7 @@ typedef struct { | |||||||
| typedef struct { | typedef struct { | ||||||
| 	int mode; | 	int mode; | ||||||
| 	int type; | 	int type; | ||||||
|  | 	int snap; | ||||||
| 	int bx, by; | 	int bx, by; | ||||||
| 	int ex, ey; | 	int ex, ey; | ||||||
| 	struct { | 	struct { | ||||||
| @@ -372,6 +378,7 @@ static void selinit(void); | |||||||
| static inline bool selected(int, int); | static inline bool selected(int, int); | ||||||
| static void selcopy(void); | static void selcopy(void); | ||||||
| static void selscroll(int, int); | static void selscroll(int, int); | ||||||
|  | static void selsnap(int, int *, int *, int); | ||||||
|  |  | ||||||
| static int utf8decode(char *, long *); | static int utf8decode(char *, long *); | ||||||
| static int utf8encode(long *, char *); | static int utf8encode(long *, char *); | ||||||
| @@ -657,6 +664,25 @@ selected(int x, int y) { | |||||||
| 			&& (x <= sel.e.x || sel.b.y != sel.e.y)); | 			&& (x <= sel.e.x || sel.b.y != sel.e.y)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | selsnap(int mode, int *x, int *y, int direction) { | ||||||
|  | 	switch(mode) { | ||||||
|  | 	case SNAP_WORD: | ||||||
|  | 		while(*x > 0 && *x < term.col-1 && term.line[*y][*x + direction].c[0] != ' ') { | ||||||
|  | 			*x += direction; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	case SNAP_LINE: | ||||||
|  | 		*x = (direction < 0) ? 0 : term.col - 1; | ||||||
|  | 		break; | ||||||
|  | 	 | ||||||
|  | 	default: | ||||||
|  | 		/* do nothing */ | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| void | void | ||||||
| getbuttoninfo(XEvent *e) { | getbuttoninfo(XEvent *e) { | ||||||
| 	int type; | 	int type; | ||||||
| @@ -667,6 +693,15 @@ getbuttoninfo(XEvent *e) { | |||||||
| 	sel.ex = x2col(e->xbutton.x); | 	sel.ex = x2col(e->xbutton.x); | ||||||
| 	sel.ey = y2row(e->xbutton.y); | 	sel.ey = y2row(e->xbutton.y); | ||||||
|  |  | ||||||
|  | 	if (sel.by < sel.ey | ||||||
|  | 			|| (sel.by == sel.ey && sel.bx < sel.ex)) { | ||||||
|  | 		selsnap(sel.snap, &sel.bx, &sel.by, -1); | ||||||
|  | 		selsnap(sel.snap, &sel.ex, &sel.ey, +1); | ||||||
|  | 	} else { | ||||||
|  | 		selsnap(sel.snap, &sel.ex, &sel.ey, -1); | ||||||
|  | 		selsnap(sel.snap, &sel.bx, &sel.by, +1); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex; | 	sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex; | ||||||
| 	sel.b.y = MIN(sel.by, sel.ey); | 	sel.b.y = MIN(sel.by, sel.ey); | ||||||
| 	sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx; | 	sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx; | ||||||
| @@ -730,9 +765,13 @@ mousereport(XEvent *e) { | |||||||
|  |  | ||||||
| void | void | ||||||
| bpress(XEvent *e) { | bpress(XEvent *e) { | ||||||
|  | 	struct timeval now; | ||||||
|  |  | ||||||
| 	if(IS_SET(MODE_MOUSE)) { | 	if(IS_SET(MODE_MOUSE)) { | ||||||
| 		mousereport(e); | 		mousereport(e); | ||||||
| 	} else if(e->xbutton.button == Button1) { | 	} else if(e->xbutton.button == Button1) { | ||||||
|  | 		gettimeofday(&now, NULL); | ||||||
|  | 		/* Clear previous selection, logically and visually. */ | ||||||
| 		if(sel.bx != -1) { | 		if(sel.bx != -1) { | ||||||
| 			sel.bx = -1; | 			sel.bx = -1; | ||||||
| 			tsetdirt(sel.b.y, sel.e.y); | 			tsetdirt(sel.b.y, sel.e.y); | ||||||
| @@ -742,6 +781,30 @@ bpress(XEvent *e) { | |||||||
| 		sel.type = SEL_REGULAR; | 		sel.type = SEL_REGULAR; | ||||||
| 		sel.ex = sel.bx = x2col(e->xbutton.x); | 		sel.ex = sel.bx = x2col(e->xbutton.x); | ||||||
| 		sel.ey = sel.by = y2row(e->xbutton.y); | 		sel.ey = sel.by = y2row(e->xbutton.y); | ||||||
|  | 		/* | ||||||
|  | 		 * Snap handling. | ||||||
|  | 		 * If user clicks are fasst enough (e.g. below timeouts), | ||||||
|  | 		 * we ignore if his hand slipped left or down and accidentally selected more; | ||||||
|  | 		 * we are just snapping to whatever we're snapping. | ||||||
|  | 		 */ | ||||||
|  | 		if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { | ||||||
|  | 			/* Snap to line */ | ||||||
|  | 			sel.snap = SNAP_LINE; | ||||||
|  | 		} else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { | ||||||
|  | 			sel.snap = SNAP_WORD; | ||||||
|  | 		} else { | ||||||
|  | 			sel.snap = 0; | ||||||
|  | 		} | ||||||
|  | 		selsnap(sel.snap, &sel.bx, &sel.by, -1); | ||||||
|  | 		selsnap(sel.snap, &sel.ex, &sel.ey, 1); | ||||||
|  | 		sel.b.x = sel.bx, sel.b.y = sel.by, sel.e.x = sel.ex, sel.e.y = sel.ey; | ||||||
|  | 		/* Draw selection, unless it's regular and we don't want to make clicks visible */ | ||||||
|  | 		if (sel.snap != 0) { | ||||||
|  | 			tsetdirt(sel.b.y, sel.e.y); | ||||||
|  | 			draw(); | ||||||
|  | 		} | ||||||
|  | 		sel.tclick2 = sel.tclick1; | ||||||
|  | 		sel.tclick1 = now; | ||||||
| 	} else if(e->xbutton.button == Button4) { | 	} else if(e->xbutton.button == Button4) { | ||||||
| 		ttywrite("\031", 1); | 		ttywrite("\031", 1); | ||||||
| 	} else if(e->xbutton.button == Button5) { | 	} else if(e->xbutton.button == Button5) { | ||||||
| @@ -907,8 +970,6 @@ xsetsel(char *str) { | |||||||
|  |  | ||||||
| void | void | ||||||
| brelease(XEvent *e) { | brelease(XEvent *e) { | ||||||
| 	struct timeval now; |  | ||||||
|  |  | ||||||
| 	if(IS_SET(MODE_MOUSE)) { | 	if(IS_SET(MODE_MOUSE)) { | ||||||
| 		mousereport(e); | 		mousereport(e); | ||||||
| 		return; | 		return; | ||||||
| @@ -922,35 +983,10 @@ brelease(XEvent *e) { | |||||||
| 		term.dirty[sel.ey] = 1; | 		term.dirty[sel.ey] = 1; | ||||||
| 		if(sel.bx == sel.ex && sel.by == sel.ey) { | 		if(sel.bx == sel.ex && sel.by == sel.ey) { | ||||||
| 			sel.bx = -1; | 			sel.bx = -1; | ||||||
| 			gettimeofday(&now, NULL); |  | ||||||
|  |  | ||||||
| 			if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { |  | ||||||
| 				/* triple click on the line */ |  | ||||||
| 				sel.b.x = sel.bx = 0; |  | ||||||
| 				sel.e.x = sel.ex = term.col; |  | ||||||
| 				sel.b.y = sel.e.y = sel.ey; |  | ||||||
| 				selcopy(); |  | ||||||
| 			} else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { |  | ||||||
| 				/* double click to select word */ |  | ||||||
| 				sel.bx = sel.ex; |  | ||||||
| 				while(sel.bx > 0 && term.line[sel.ey][sel.bx-1].c[0] != ' ') { |  | ||||||
| 					sel.bx--; |  | ||||||
| 				} |  | ||||||
| 				sel.b.x = sel.bx; |  | ||||||
| 				while(sel.ex < term.col-1 && term.line[sel.ey][sel.ex+1].c[0] != ' ') { |  | ||||||
| 					sel.ex++; |  | ||||||
| 				} |  | ||||||
| 				sel.e.x = sel.ex; |  | ||||||
| 				sel.b.y = sel.e.y = sel.ey; |  | ||||||
| 				selcopy(); |  | ||||||
| 			} |  | ||||||
| 		} else { | 		} else { | ||||||
| 			selcopy(); | 			selcopy(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval)); |  | ||||||
| 	gettimeofday(&sel.tclick1, NULL); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user