Subject: Re: bug#26599: patch for mwheel.el

From: Tak Kunihiro <[email-masked]>
Date: Fri, 28 Apr 2017 18:12:50 +0900 (JST)

>> >> Keyboard is the primary scroll device.  Thus I want to turn
>> >> auto-hscroll-mode t by default.
>> >> 
>> >> Occasionally I want to use mouse as scroll device.  When I use mouse,
>> >> I want to set turn auto-hscroll-mode nil, especially after
>> >> implementation of <wheel-right> and <wheel-left>.
>> >> 
>> >> When I come back to keyboard, I want to set auto-hscroll-mode t again.
>> > 
>> > This looks like a very specialized use case, so I'm not sure we need a
>> > solution for it in Emacs.
>> 
>> I see how you see.
>> 
>> How I described, is similar to how spreadsheet program reacts.  It
>> lets user scroll both by <wheel-left> and <left>.
>> 
>> I very often edit a buffer with long and short line (for example,
>> LaTeX table), using mouse and keyboard.  However, as you infer, this
>> can be already very special.
> 
> What makes this special is that you want Emacs to work differently
> depending on the input device.  Emacs normally makes a significant
> effort in the other direction: to produce the same behavior no matter
> where input came from.
> 
> I'm not sure we want to have such unusual behavior as part of Emacs.

Can you take a look minor-mode that I want to invoke by hook?  I still
think this is potentially useful to mouse-loving cloud using
<wheel-left> and <wheel-right>.

;;; touchpad.el --- Scroll two dimensionally by touchpad

;; Copyright (C) 2017 Tak Kunihiro
;; Author: Tak Kunihiro <[email-masked]>
;; Maintainer: Tak Kunihiro <[email-masked]>
;; URL: http://dream.misasa.okayama-u.ac.jp
;; Package-Requires: ((emacs "26.1"))
;; Version: 1.0.0
;; Package-Version: 20170427.1515
;; Keywords: mouse, scroll

;;; This file is NOT part of GNU Emacs

;;; License

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.

;;; Commentary:

;; To interactively toggle the mode:
;;
;;   M-x touchpad-mode
;;
;; To make the mode permanent, add the following lines to your init
;; file.
;;
;;   (require 'touchpad)
;;   (touchpad-mode 1)
;;
;; This package offers a global minor mode which makes swiping
;; touchpad scroll smoothly.  This package disables
;; `auto-hscroll-mode' during scroll because of following two aspects.

;; (1) It should be off during vertical scroll.  Let’s consider a
;;     buffer is with short and long alternative lines and when point
;;     is at the end of long line, at the top of current window.
;;     After `scroll-up 1', point jumps to the end of the next short
;;     line and you see scope shifts suddenly leftward.  This behavior
;;     is sometimes unexpected one.

;; (2) It should be off during horizontal scroll.  During horizontal
;;     scroll, you may scroll a little in vertical direction without
;;     intention.  The horizontal scroll should be tolerance against
;;     such perturbation.  The source of concern is same as (1).

;; After scroll, you want to set `auto-hscroll-mode' back again
;; otherwise too inconvenient for further edition.  Approach of this
;; package is to turn on another minor-mode `touchpad--2d-mode' with
;; `auto-hscroll-mode' nil at the beginning of `mwheel-scroll'.  The
;; minor mode is turned off upon any key inputs that move point.

;;; Change Log:

;; 20170409.1204
;;  - (setq scroll-conservatively 100) on minor mode may work as backup

;;; Todo:
;;  - Release as a package

(require 'mwheel)

;;; Code:
(defvar touchpad--cursor-type cursor-type
  "Cursor used by user.  This variable is used internally to
  restore original `cursor-type'.")

(define-minor-mode touchpad-mode
  "A minor mode to scroll text two dimensionally.  With a prefix argument ARG,
enable Touchpad Mode if ARG is positive, and disable it
otherwise.  If called from Lisp, enable Touchpad Mode if ARG is
omitted or nil."
  :init-value nil
  :group 'scrolling
  :global t

  (if touchpad-mode
      (progn
        (advice-add 'mwheel-scroll :before 'touchpad-enable--2d-mode)
        ;; (add-hook 'mwheel-pre-scroll-hook 'touchpad-enable--2d-mode)
        (setq mwheel-tilt-scroll-p t))
    (advice-remove 'mwheel-scroll #'touchpad-enable--2d-mode)
    ;; (remove-hook 'mwheel-pre-scroll-hook 'touchpad-enable--2d-mode)
    (dolist (var '(mwheel-tilt-scroll-p))
      (custom-reevaluate-setting var))))

;; (defun touchpad-enable--2d-mode ()
;;   "Enable minor mode `touchpad--2d-mode' to disable
;; `auto-hscroll-mode'.  This is supposed to be called before actual
;; scrolling."
;;   (let ((buffer (window-buffer (mwheel-event-window last-input-event))))
;;     (with-current-buffer buffer
;;       (touchpad--2d-mode 1)))) ; turn on minor-mode

(defun touchpad-enable--2d-mode (func &rest args)
  "Enable minor mode `touchpad--2d-mode' to disable
`auto-hscroll-mode'.  This is supposed to be adviced before
`mwheel-scroll'."
  (let ((buffer (window-buffer (mwheel-event-window last-input-event))))
    (with-current-buffer buffer
      (touchpad--2d-mode 1)))) ; turn on minor-mode

(defun touchpad-disable--2d-mode ()
  "Disable minor mode `touchpad--2d-mode' to enable
`auto-hscroll-mode' back.  Then invoke command that is bound to
the original key."
  (interactive)
  (touchpad--2d-mode 0) ; turn off minor-mode
  (call-interactively (key-binding (this-command-keys))))

(define-minor-mode touchpad--2d-mode
  "A minor-mode with `auto-hscroll-mode' off.  This minor mode is used
internally."
  :init-value nil
  :keymap (let ((map (make-sparse-keymap)))
            (define-key map [remap keyboard-quit] 'touchpad-disable--2d-mode)
            (define-key map [remap mouse-set-point] 'touchpad-disable--2d-mode)
            (define-key map [remap right-char] 'touchpad-disable--2d-mode)
            (define-key map [remap forward-char] 'touchpad-disable--2d-mode)
            (define-key map [remap forward-word] 'touchpad-disable--2d-mode)
            (define-key map [remap forward-sentence] 'touchpad-disable--2d-mode)
            (define-key map [remap forward-paragraph] 'touchpad-disable--2d-mode)
            (define-key map [remap forward-page] 'touchpad-disable--2d-mode)
            (define-key map [remap left-char] 'touchpad-disable--2d-mode)
            (define-key map [remap backward-char] 'touchpad-disable--2d-mode)
            (define-key map [remap backward-word] 'touchpad-disable--2d-mode)
            (define-key map [remap backward-sentence] 'touchpad-disable--2d-mode)
            (define-key map [remap backward-paragraph] 'touchpad-disable--2d-mode)
            (define-key map [remap backward-page] 'touchpad-disable--2d-mode)
            (define-key map [remap move-beginning-of-line] 'touchpad-disable--2d-mode)
            (define-key map [remap move-end-of-line] 'touchpad-disable--2d-mode)
            (define-key map [remap next-line] 'touchpad-disable--2d-mode)
            (define-key map [remap next-error] 'touchpad-disable--2d-mode)
            (define-key map [remap scroll-up-command] 'touchpad-disable--2d-mode)
            (define-key map [remap previous-line] 'touchpad-disable--2d-mode)
            (define-key map [remap previous-error] 'touchpad-disable--2d-mode)
            (define-key map [remap scroll-down-command] 'touchpad-disable--2d-mode)
            (define-key map [remap beginning-of-defun] 'touchpad-disable--2d-mode)
            (define-key map [remap beginning-of-buffer] 'touchpad-disable--2d-mode)
            (define-key map [remap end-of-defun] 'touchpad-disable--2d-mode)
            (define-key map [remap end-of-buffer] 'touchpad-disable--2d-mode)
            (define-key map [remap goto-char] 'touchpad-disable--2d-mode)
            (define-key map [remap goto-line] 'touchpad-disable--2d-mode)
            (define-key map [remap move-to-column] 'touchpad-disable--2d-mode)
            ;; list as much as think of ... or map all but
            ;; (where-is-internal 'mwheel-scroll)?
            map)
  :group 'scrolling

  (if touchpad--2d-mode
      (progn
        (setq-local auto-hscroll-mode nil)
        (setq-local cursor-type 'hollow))
    (setq-local auto-hscroll-mode t)
    (setq-local cursor-type touchpad--cursor-type)))

(provide 'touchpad)
;;; touchpad.el ends here