unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#18923: Alternative scrolling model
@ 2014-11-02  1:15 E Sabof
       [not found] ` <jwv61ey5qfb.fsf-monnier+emacsbugs@gnu.org>
                   ` (3 more replies)
  0 siblings, 4 replies; 21+ messages in thread
From: E Sabof @ 2014-11-02  1:15 UTC (permalink / raw)
  To: 18923

I've made a prototype for an alternative way to scroll. Essentially scrolling is done pixelwise irrespective of content. Whole lines are scrolled "normally", and the remainder is vscrolled. If the end result is close to a line boundary it gets "snapped" to it.

This prevents unpleasant jumping when encountering an image. It doesn't handle the "image taller than window" case, but it would if `st-height' could measure more accurately.

Evgeni

;; Any vscroll adjustements will be reset by line-move
(require 'cl-lib)
(defun st-message (&rest args)
  ;; (apply 'message args)
  )

(defvar st-ov nil)
(defun st-height (&optional pos)
  "Won't report accurately, if the line is higher than window."
  (cl-flet (( posn-y ()
              (cdr (posn-x-y (or (posn-at-point)
                                 (progn
                                   (vertical-motion 0)
                                   (set-window-start nil (point))
                                   (posn-at-point)))))))
    (save-excursion
      (save-window-excursion
        (let* ((ws (window-vscroll nil t))
               a b)
          (when (cl-plusp ws)
            (set-window-vscroll nil 0 t))
          (setq a (posn-y))
          (vertical-motion 1)
          (setq b (posn-y))
          (when (cl-plusp ws)
            (set-window-vscroll nil ws t))
          (- b a)
          )))))

(cl-defun st-get-lines (ammount)
  "Provide the information required to scroll by AMMOUNT.

AMMOUNT can be positive, if scrolling towards the end of the
buffer, or negative otherwise.

Returns \(list vscroll \(list lines\)\), where \"vscroll\" is the
current \(window-vscroll\) and \"lines\" are the lines are enogh
or more lines required for to scroll."

  (let* (( direction (if (cl-plusp ammount) 1 -1))
         ( vscroll (window-vscroll nil t))
         rows)
    (save-excursion
      (goto-char (window-start))
      (cl-incf ammount vscroll)
      (when (cl-minusp direction)
        (unless (cl-minusp ammount)
          (cl-return-from st-get-lines
            (list vscroll nil)))
        (vertical-motion -1))
      (cl-loop do (push (st-height) rows)
               until (or (zerop (vertical-motion direction))
                         ;; >= ?
                         (>= (cl-reduce '+ rows)
                             (abs ammount))))
      (list vscroll (nreverse rows)))))

(cl-defun st-move (lines vscroll)
  ;; vscroll changes aren't always displayed. Haven't found a work-around for this.
  (let (( ori-point (point))
        ( new-ws
          (save-excursion
            (goto-char (window-start))
            (vertical-motion lines)
            (point))))
    (progn
      (set-window-start nil new-ws)

      ;; If I don't do this, vscroll might get reset to 0

      ;; (point) might change after this

      ;; (window-start) might change after this, if the cursor is positioned on
      ;; that image, and scrolling down. This always happends if image would be
      ;; split at the bottom, but sometimes it happens earlier. What follows is
      ;; a work-around.

      (redisplay t)
      (when (/= (window-start) new-ws)
        ;; (message "HIT")
        (vertical-motion -1)
        (set-window-start nil new-ws)
        (redisplay t)
        )
      )
    (set-window-vscroll nil vscroll t)

    ;; Prevents flashes of incorrectly positioned images

    ;; (window-start) might change after this, if the cursor is on an image and
    ;; it might get divided on the upper edge

    (redisplay t)

    (when (/= (window-start) new-ws)
      ;; (message "HIT2")
      (vertical-motion 1)
      (set-window-start nil new-ws)
      (redisplay t)
      )

    ))

(cl-defun scroll--backtick (&optional (arg 1) pixelwise snap)
  (interactive)

  (let* (( default-height (default-line-height))
         ( pixels-to-move (if pixelwise
                              arg
                            (* arg default-height)))
         ( snap (or snap (/ default-height 2)))
         ( line-info (st-get-lines (- pixels-to-move)))
         ( heights (cadr line-info))
         ( initial-vscroll (car line-info))
         ( excess 0)
         enough-or-too-many-heights
         too-few-heights)

    (if (<= pixels-to-move initial-vscroll)
        (progn
          (setq heights nil
                excess (- initial-vscroll pixels-to-move)))

      (cl-decf pixels-to-move initial-vscroll)

      (setq enough-or-too-many-heights (cl-reduce '+ heights)
            too-few-heights (cl-reduce '+ (butlast heights) :initial-value 0))

      (cond ( (= enough-or-too-many-heights pixels-to-move)
              (st-message "Exact %s" heights)
              )
            ( (> pixels-to-move enough-or-too-many-heights)
              (st-message "Near edge %s > %s"
                          pixels-to-move
                          enough-or-too-many-heights)
              (setq excess 0))

            ( (<= (- enough-or-too-many-heights snap)
                  pixels-to-move)
              (st-message "Snap out")
              (setq excess 0))

            ( (and (cl-plusp too-few-heights)
                   (>= (+ too-few-heights snap)
                       pixels-to-move))
              (st-message "Snap in %s" heights)
              (setq excess 0)
              (setq heights (butlast heights))
              )

            ( t
              (st-message "Default")
              (setq excess (- enough-or-too-many-heights
                              pixels-to-move))
              )))

    (st-move (- (length heights)) excess)

    ))

(cl-defun scroll-tick (&optional (arg 1) pixelwise snap)
  (interactive)
  (cond ( (zerop arg)
          (cl-return-from scroll-tick))
        ( (< arg 0)
          (cl-return-from scroll-tick
            (scroll--backtick (- arg) pixelwise snap))))
  (when st-ov (delete-overlay st-ov))

  (let* (( default-height (default-line-height))
         ( pixels-to-move (if pixelwise
                              arg
                            (* arg default-height)))
         ( snap (or snap (/ default-height 2)))
         ( line-info (st-get-lines pixels-to-move))
         ( heights (cadr line-info))
         ( initial-vscroll (car line-info))
         excess
         enough-or-too-many-heights
         too-few-heights)

    (cl-incf pixels-to-move initial-vscroll)

    (setq enough-or-too-many-heights (cl-reduce '+ heights)
          too-few-heights (cl-reduce '+ (butlast heights) :initial-value 0)
          excess (if (= enough-or-too-many-heights pixels-to-move)
                     0
                   (- pixels-to-move too-few-heights)))
    (cond ( (= enough-or-too-many-heights pixels-to-move)
            (st-message "Exact %s" heights)
            )
          ( (> pixels-to-move enough-or-too-many-heights)
            (st-message "Near edge")
            (setq excess 0))

          ( (<= (- enough-or-too-many-heights snap)
                pixels-to-move)
            (st-message "Snap out")
            (setq excess 0))

          ( (and (cl-plusp too-few-heights)
                 (>= (+ too-few-heights snap)
                     pixels-to-move))
            (st-message "Snap in %s" heights)
            (setq excess 0)
            (setq heights (butlast heights))
            )
          ( t
            (st-message "Default")
            (setq heights (butlast heights))
            ))

    (st-move (length heights) excess)

    ))

;; (global-set-key (kbd "<next>") (lambda () (interactive) (scroll-tick 10)))
;; (global-set-key (kbd "<prior>") (lambda () (interactive) (scroll-tick -10)))

;; TESTS

;; (require 'noflet)

;; (ert-deftest scroll-tick ()
;;   (noflet (( st-move (&rest args) args))
;;     (noflet (( st-get-lines (arg)
;;                '(0 (30))))
;;       ;; Simple V-scroll
;;       (should (equal (scroll-tick 5 t 0)
;;                 '(0 5)))
;;       ;; Simple exact
;;       (should (equal (scroll-tick 30 t 0)
;;                 '(1 0)))

;;       )

;;     (noflet (( st-get-lines (arg)
;;                '(0 (5 30))))
;;       ;; Complete line + vscroll
;;       (should (equal (scroll-tick 15 t 0)
;;                 '(1 10)))
;;       ;; Complete 2 lines
;;       (should (equal (scroll-tick 35 t 0)
;;                 '(2 0)))
;;       )

;;     (noflet (( st-get-lines (arg)
;;                '(5 (10 20))))
;;       ;;
;;       (should (equal (scroll-tick 20 t 0)
;;                 '(1 15)))
;;       ;; Complete 2 lines
;;       (should (equal (scroll-tick 25 t 0)
;;                 '(2 0)))
;;       ))
;;   )

;; (ert-deftest scroll-backtick ()
;;   (noflet (( st-move (&rest args) args))
;;     (noflet (( st-get-lines (arg)
;;                '(0 (30))))
;;       ;; Simple V-scroll
;;       (should (equal (scroll-tick -5 t 0)
;;                 '(-1 25)))
;;       ;; Simple exact
;;       (should (equal (scroll-tick -30 t 0)
;;                 '(-1 0))))

;;     (noflet (( st-get-lines (arg)
;;                '(0 (5 30))))
;;       ;; Complete line + vscroll
;;       (should (equal (scroll-tick -15 t 0)
;;                 '(-2 20)))
;;       ;; Complete 2 lines
;;       (should (equal (scroll-tick -35 t 0)
;;                 '(-2 0)))
;;       )

;;     (noflet (( st-get-lines (arg)
;;                '(5 (10))))

;;       ;; Scroll across existing vscroll + a bit
;;       (should (equal (scroll-tick -10 t 0)
;;                 '(-1 5)))
;;       )

;;     (noflet (( st-get-lines (arg)
;;                '(5 (10 20))))
;;       ;; Scroll up a bit
;;       (should (equal (scroll-tick -1 t 0)
;;                 '(0 4)))
;;       ;; Remove vscroll
;;       (should (equal (scroll-tick -5 t 0)
;;                 '(0 0)))

;;       ;;
;;       (should (equal (scroll-tick -20 t 0)
;;                 '(-2 15)))

;;       )

;;     )
;;   )





^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-04-22 12:16 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-02  1:15 bug#18923: Alternative scrolling model E Sabof
     [not found] ` <jwv61ey5qfb.fsf-monnier+emacsbugs@gnu.org>
     [not found]   ` <87vbmy9wdx.fsf@gmail.com>
2014-11-02  2:31     ` E Sabof
2014-11-02 15:16       ` Eli Zaretskii
2014-11-02 18:25         ` E Sabof
2014-11-02 18:36           ` Eli Zaretskii
     [not found]     ` <jwvioiy416g.fsf-monnier+emacsbugs@gnu.org>
     [not found]       ` <87sii1ahy9.fsf@gmail.com>
     [not found]         ` <jwvppd538tu.fsf-monnier+emacsbugs@gnu.org>
2014-11-02 23:10           ` E Sabof
2014-11-03  2:35             ` Stefan Monnier
2014-11-03 16:03               ` Eli Zaretskii
2014-11-03 19:02                 ` E Sabof
2014-11-02 15:14 ` Eli Zaretskii
2014-11-02 15:56   ` Stefan Monnier
2014-11-02 16:06     ` Eli Zaretskii
2014-11-02 16:21   ` E Sabof
2014-11-02 16:31     ` Eli Zaretskii
2014-11-02 17:43       ` E Sabof
2014-11-02 18:22         ` Eli Zaretskii
2014-11-02 19:09           ` E Sabof
2014-11-02 19:29             ` Eli Zaretskii
2014-11-03  3:45               ` Eli Zaretskii
2019-11-04  9:14 ` Stefan Kangas
2022-04-22 12:16 ` Lars Ingebrigtsen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).