diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index cd1745614eb..274c456d8c3 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -1969,6 +1969,8 @@ events do not appear. @xref{Mouse Tracking}. When non-@code{nil}, mouse motion events are generated even for very small movements. Otherwise, motion events are not generated as long as the mouse cursor remains pointing to the same glyph in the text. +Note that non-@code{nil} @code{mouse-prefer-closest-glyph} changes +that to the left/right half of the glyph under the mouse cursor instead. @end defvar @node Touchscreen Events @@ -2751,6 +2753,14 @@ If @var{whole} is non-@code{nil}, the @var{x} coordinate is relative to the entire window area including scroll bars, margins and fringes. @end defun +@defvar mouse-prefer-closest-glyph +If you set this variable to non-@code{nil}, whenever you click or drag the mouse, +instead of the point being always set in front of the clicked glyph, the point +horizontally closest to the mouse position will be used. +So if you click in the left half of a glyph, point is set in front of it, +but if you click in the right half, point is set after it. +@end defvar + @node Accessing Scroll @subsection Accessing Scroll Bar Events @cindex scroll bar events, data in diff --git a/etc/NEWS b/etc/NEWS index 9e6f0c16bcd..978ba0dfa69 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -952,6 +952,14 @@ wheel reports. Unlike 'pixel-scroll-mode', this mode scrolls the display pixel-by-pixel, as opposed to only animating line-by-line scrolls. ++++ +** New user option 'mouse-prefer-closest-glyph'. +When enabled, clicking or dragging with the mouse will put the point +in front of the glyph with the closest x coordinate to the mouse pointer. +In other words, if the mouse pointer is in the right half of a glyph, +point will be put after that glyph, while if the mouse pointer is in the +left half of a glyph, point will be put in front of that glyph. + ** Terminal Emacs --- diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 054683d7cf6..2ed904f178f 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -231,6 +231,7 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of (inverse-video display boolean) (visible-bell display boolean) (no-redraw-on-reenter display boolean) + (mouse-prefer-closest-glyph display boolean) ;; doc.c (text-quoting-style display diff --git a/src/dispnew.c b/src/dispnew.c index 65d9cf9b4e1..669cd5f4d33 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -5611,6 +5611,15 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p argument is ZV to prevent move_it_in_display_line from matching based on buffer positions. */ move_it_in_display_line (&it, ZV, to_x, MOVE_TO_X); + if (mouse_prefer_closest_glyph) + { + int next_x = it.current_x + it.pixel_width; + int before_dx = to_x - it.current_x; + int after_dx = next_x - to_x; + if (before_dx > after_dx) + move_it_in_display_line (&it, ZV, next_x, MOVE_TO_X); + } + bidi_unshelve_cache (itdata, 0); Fset_buffer (old_current_buffer); @@ -6788,6 +6797,15 @@ predicates which report frame's specific UI-related capabilities. */); DEFVAR_BOOL ("cursor-in-echo-area", cursor_in_echo_area, doc: /* Non-nil means put cursor in minibuffer, at end of any message there. */); + DEFVAR_BOOL ("mouse-prefer-closest-glyph", mouse_prefer_closest_glyph, + doc: /* Non-nil means mouse position lists are reported relative +to the glyph closest to their coordinates. + + When non-nil, mouse position lists will be reported with their +`posn-point' set to the position of the glyph closest to the mouse +pointer, instead of the glyph immediately under. */); + mouse_prefer_closest_glyph = false; + DEFVAR_LISP ("glyph-table", Vglyph_table, doc: /* Table defining how to output a glyph code to the frame. If not nil, this is a vector indexed by glyph code to define the glyph. diff --git a/src/xdisp.c b/src/xdisp.c index 763af7d3bc8..4ab53d47f0c 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -2723,6 +2723,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) enum window_part part; enum glyph_row_area area; int x, y, width, height; + int original_gx; if (mouse_fine_grained_tracking) { @@ -2733,6 +2734,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) /* Try to determine frame pixel position and size of the glyph under frame pixel coordinates X/Y on frame F. */ + original_gx = gx; + if (window_resize_pixelwise) { width = height = 1; @@ -2950,6 +2953,16 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) store_rect: STORE_NATIVE_RECT (*rect, gx, gy, width, height); + if (mouse_prefer_closest_glyph) + { + int half_width = rect->width / 2; + rect->width = half_width; + + int bisection = rect->x + half_width; + if (original_gx > bisection) + rect->x = bisection; + } + /* Visible feedback for debugging. */ #if false && defined HAVE_X_WINDOWS XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), @@ -37345,7 +37358,10 @@ may be more familiar to users. */); DEFVAR_BOOL ("mouse-fine-grained-tracking", mouse_fine_grained_tracking, doc: /* Non-nil for pixel-wise mouse-movement. When nil, mouse-movement events will not be generated as long as the -mouse stays within the extent of a single glyph (except for images). */); +mouse stays within the extent of a single glyph (except for images). +When nil and mouse-prefer-closest-glyph is non-nil, mouse-movement +events will instead not be generated as long as the mouse stays within +the extent of a single left/right half glyph (except for images). */); mouse_fine_grained_tracking = false; DEFVAR_BOOL ("tab-bar--dragging-in-progress", tab_bar__dragging_in_progress,