+(defun scroll-bar-drag-position (portion-whole)
+ "Calculate new window start for drag event."
+ (save-excursion
+ (goto-char (+ (point-min)
+ (scroll-bar-scale portion-whole
+ (- (point-max) (point-min)))))
+ (beginning-of-line)
+ (point)))
+
+(defun scroll-bar-maybe-set-window-start (event)
+ "Set the window start according to where the scroll bar is dragged.
+Only change window start if the new start is substantially different.
+EVENT should be a scroll bar click or drag event."
+ (interactive "e")
+ (let* ((end-position (event-end event))
+ (window (nth 0 end-position))
+ (portion-whole (nth 2 end-position))
+ (next-portion-whole (cons (1+ (car portion-whole))
+ (cdr portion-whole)))
+ portion-start
+ next-portion-start
+ (current-start (window-start window)))
+ (save-excursion
+ (set-buffer (window-buffer window))
+ (setq portion-start (scroll-bar-drag-position portion-whole))
+ (setq next-portion-start (max
+ (scroll-bar-drag-position next-portion-whole)
+ (1+ portion-start)))
+ (if (or (>= current-start next-portion-start)
+ (< current-start portion-start))
+ (set-window-start window portion-start)
+ ;; Always set window start, to ensure scroll bar position is updated.
+ (set-window-start window current-start)))))
+
+;; Scroll the window to the proper position for EVENT.
+(defun scroll-bar-drag-1 (event)
+ (let* ((start-position (event-start event))
+ (window (nth 0 start-position))
+ (portion-whole (nth 2 start-position)))
+ (save-excursion
+ (set-buffer (window-buffer window))
+ ;; Calculate position relative to the accessible part of the buffer.
+ (goto-char (+ (point-min)
+ (scroll-bar-scale portion-whole
+ (- (point-max) (point-min)))))
+ (beginning-of-line)
+ (set-window-start window (point)))))
+
+(defun scroll-bar-drag (event)
+ "Scroll the window by dragging the scroll bar slider.
+If you click outside the slider, the window scrolls to bring the slider there."
+ (interactive "e")
+ (let* (done
+ (echo-keystrokes 0)
+ (end-position (event-end event))
+ (window (nth 0 end-position))
+ (before-scroll))
+ (with-current-buffer (window-buffer window)
+ (setq before-scroll point-before-scroll))
+ (save-selected-window
+ (select-window window)
+ (setq before-scroll
+ (or before-scroll (point))))
+ (scroll-bar-drag-1 event)
+ (track-mouse
+ (while (not done)
+ (setq event (read-event))
+ (if (eq (car-safe event) 'mouse-movement)
+ (setq event (read-event)))
+ (cond ((eq (car-safe event) 'scroll-bar-movement)
+ (scroll-bar-drag-1 event))
+ (t
+ ;; Exit when we get the drag event; ignore that event.
+ (setq done t)))))
+ (sit-for 0)
+ (with-current-buffer (window-buffer window)
+ (setq point-before-scroll before-scroll))))
+