emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Context menus and mouse-3


From: Juri Linkov
Subject: Re: Context menus and mouse-3
Date: Mon, 12 Jul 2021 02:38:19 +0300
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (x86_64-pc-linux-gnu)

>>   > We don't need to choose, since we can get a context menu on
>>   > `down-mouse-3` and 'mouse-save-then-kill' on `mouse-3`.
>
>     (defun mouse-maybe-context-menu (event)
>       "Bring up a context menu for a long click.
>     See `mouse-long-click-time' and `mouse-context-menu-function'."
>       (interactive "@e")
>       (if (let ((track-mouse t)) (sit-for (/ mouse-long-click-time 1000.0)))
>           (push (cons 'context-menu (cdr event)) unread-command-events)))
>
> It's not good enough for `master` (IIRC the sit-for tends to return nil
> immediately in some circumstances), but I didn't pursue this any further
> given the lack of interest at the time.  I can't think of any reason why
> it should be hard to fix.

This is a much needed feature and it would nice to finish it one way or another.
I tried to use timers, and the result works well in all test cases:

1. Modes that already bind down-mouse-3 continue working without problems,
   so e.g. smerge-popup-context-menu works fine.  BTW, while testing it
   by clicking down-mouse-3 in non-selected windows, I found a problem,
   so a fix for this very rare problem is also included in the patch.

   For the same reason 'popup-menu' needs to be called explicitly
   to be able to select the right window, as the patch does,
   for the case when the mouse is dragged and down-mouse-3
   is released in another window.

2. flyspell.el doesn't need to be changed.  ibuffer.el works too.

3. cua-rect.el helped to fix one problem and now is supported too.

The new user option mouse-3-down-context-menu is based on 
mouse-1-click-follows-link.
Currently mouse-context-menu-map shows just the Edit menu - filling the content
of the context menu with e.g. mouse-context-menu-function is a separate task.

diff --git a/lisp/mouse.el b/lisp/mouse.el
index 89e5d7c48a..febda78212 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -156,6 +156,82 @@ key-translation-map
 (define-key key-translation-map [double-mouse-1]
   #'mouse--click-1-maybe-follows-link)
 
+
+(defcustom mouse-3-down-context-menu 450
+  "Non-nil means that holding down Mouse-3 shows context menu.
+
+With the default setting, holding down the Mouse-3 button
+for more than 450 milliseconds performs the same action as S-Down-Mouse-3
+\(which shows the menu), while an ordinary Mouse-3 click performs the
+original Mouse-3 binding (which typically sets the region where you
+click the mouse).
+
+If value is an integer, the time elapsed between pressing and
+releasing the mouse button determines whether to show the menu
+or perform the normal Mouse-3 action (typically set the region).
+The absolute numeric value specifies the maximum duration of a
+\"short click\" in milliseconds.  A positive value means that a
+short click shows the menu, and a longer click performs the
+normal action.
+
+Otherwise, a single Mouse-3 click unconditionally shows the menu."
+  :version "28.1"
+  :type '(choice (const :tag "Disabled" nil)
+                 (number :tag "Single click time limit" :value 450)
+                 (other :tag "Single click" t)))
+
+(defvar mouse--down-3-timer nil)
+
+(defun mouse-maybe-context-menu (event)
+  (interactive "@e")
+  (cond
+   ;; Delay context menu display.
+   ((numberp mouse-3-down-context-menu)
+    (when (timerp mouse--down-3-timer)
+      (cancel-timer mouse--down-3-timer))
+    (setq mouse--down-3-timer
+          (run-with-timer
+           (/ (abs mouse-3-down-context-menu) 1000.0) nil
+           (lambda ()
+             (setq mouse--down-3-timer t)
+             (unless (eq (posn-window (event-end event)) (selected-window))
+               (select-window (posn-window (event-end event))))
+             (mouse-context-menu event))))
+    nil)
+   ;; Immediately pop up context menu.
+   (mouse-3-down-context-menu
+    (unless (eq (posn-window (event-end event)) (selected-window))
+      (select-window (posn-window (event-end event))))
+    (mouse-context-menu event))))
+
+(defun mouse--click-3-maybe-context-menu (&optional _prompt)
+  (cond
+   ((numberp mouse-3-down-context-menu)
+    (cond
+     ((timerp mouse--down-3-timer)
+      ;; Don't wait for context menu and fall back to mouse-save-then-kill.
+      (cancel-timer mouse--down-3-timer)
+      (setq mouse--down-3-timer nil)
+      nil)
+     (mouse--down-3-timer
+      ;; Context menu was displayed after delay.
+      (setq mouse--down-3-timer nil)
+      [])))
+   ;; Context menu was displayed immediately.
+   (mouse-3-down-context-menu
+    [])))
+
+(define-key key-translation-map [mouse-3]
+  #'mouse--click-3-maybe-context-menu)
+
+(defun mouse-context-menu-map ()
+  (cddr (assq 'edit (lookup-key global-map [menu-bar]))))
+
+(defun mouse-context-menu (event)
+  "Show a context menu for the current buffer."
+  (interactive "@e")
+  (popup-menu (mouse-context-menu-map) event))
+
 
 ;; Provide a mode-specific menu on a mouse button.
 
@@ -1672,7 +1748,7 @@ mouse-save-then-kill
      ((not (numberp click-pt)) nil)
      ;; If the user clicked without moving point, kill the region.
      ;; This also resets `mouse-selection-click-count'.
-     ((and (eq last-command 'mouse-save-then-kill)
+     ((and (memq last-command '(mouse-save-then-kill mouse-maybe-context-menu))
           (eq click-pt mouse-save-then-kill-posn)
           (eq window (selected-window)))
       (if mouse-drag-copy-region
@@ -2899,6 +2975,8 @@ function-key-map
 ;; Allow yanking also when the corresponding cursor is "in the fringe".
 (define-key function-key-map [right-fringe mouse-2] 'mouse--strip-first-event)
 (define-key function-key-map [left-fringe mouse-2] 'mouse--strip-first-event)
+
+(global-set-key [down-mouse-3] 'mouse-maybe-context-menu)
 (global-set-key [mouse-3]      'mouse-save-then-kill)
 (define-key function-key-map [right-fringe mouse-3] 'mouse--strip-first-event)
 (define-key function-key-map [left-fringe mouse-3] 'mouse--strip-first-event)
diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el
index 694d4529b9..3aa7399dd5 100644
--- a/lisp/vc/smerge-mode.el
+++ b/lisp/vc/smerge-mode.el
@@ -385,6 +385,8 @@ smerge-remove-props
 (defun smerge-popup-context-menu (event)
   "Pop up the Smerge mode context menu under mouse."
   (interactive "e")
+  (unless (eq (posn-window (event-end event)) (selected-window))
+    (select-window (posn-window (event-end event))))
   (if (and smerge-mode
           (save-excursion (posn-set-point (event-end event)) (smerge-check 1)))
       (progn

reply via email to

[Prev in Thread] Current Thread [Next in Thread]