1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-26 15:21:51 -08:00

Use run-with-idle-timer instead of debounce for responsive image scaling.

* lisp/emacs-lisp/timer.el (debounce, debounce-reduce): Revert macro addition.
https://lists.gnu.org/archive/html/emacs-devel/2019-11/msg01133.html

* lisp/image.el (image-increase-size, image-decrease-size):
Use run-with-idle-timer.
(image--change-size): Rename back from image--change-size-function.

* lisp/image-mode.el (image-mode--setup-mode): Remove hooks
window-size-change-functions and window-selection-change-functions (bug#32672)
(image-fit-to-window): Rename from image--window-change-function.
(image--window-state-change): Rename from image--window-change.
Use run-with-idle-timer.
This commit is contained in:
Juri Linkov 2019-11-30 23:16:03 +02:00
parent 9ac78ef56c
commit d64ea182fb
4 changed files with 53 additions and 89 deletions

View file

@ -2849,11 +2849,6 @@ doing computations on a decoded time structure), 'make-decoded-time'
filled out), and 'encoded-time-set-defaults' (which fills in nil filled out), and 'encoded-time-set-defaults' (which fills in nil
elements as if it's midnight January 1st, 1970) have been added. elements as if it's midnight January 1st, 1970) have been added.
** New macros 'debounce' and 'debounce-reduce' postpone function call
until after specified time have elapsed since the last time it was invoked.
This improves performance of processing events occurring rapidly
in quick succession.
** 'define-minor-mode' automatically documents the meaning of ARG. ** 'define-minor-mode' automatically documents the meaning of ARG.
+++ +++

View file

@ -488,50 +488,6 @@ The argument should be a value previously returned by `with-timeout-suspend'."
If the user does not answer after SECONDS seconds, return DEFAULT-VALUE." If the user does not answer after SECONDS seconds, return DEFAULT-VALUE."
(with-timeout (seconds default-value) (with-timeout (seconds default-value)
(y-or-n-p prompt))) (y-or-n-p prompt)))
(defmacro debounce (secs function)
"Call FUNCTION after SECS seconds have elapsed.
Postpone FUNCTION call until after SECS seconds have elapsed since the
last time it was invoked. On consecutive calls within the interval of
SECS seconds, cancel all previous calls that occur rapidly in quick succession,
and execute only the last call. This improves performance of event processing."
(declare (indent 1) (debug t))
(let ((timer-sym (make-symbol "timer")))
`(let (,timer-sym)
(lambda (&rest args)
(when (timerp ,timer-sym)
(cancel-timer ,timer-sym))
(setq ,timer-sym
(run-with-timer
,secs nil (lambda ()
(apply ,function args))))))))
(defmacro debounce-reduce (secs initial-state state-function function)
"Call FUNCTION after SECS seconds have elapsed.
Postpone FUNCTION call until after SECS seconds have elapsed since the
last time it was invoked. On consecutive calls within the interval of
SECS seconds, cancel all previous calls that occur rapidly in quick succession,
and execute only the last call. This improves performance of event processing.
STATE-FUNCTION can be used to accumulate the state on consecutive calls
starting with the value of INITIAL-STATE, and then execute the last call
with the collected state value."
(declare (indent 1) (debug t))
(let ((timer-sym (make-symbol "timer"))
(state-sym (make-symbol "state")))
`(let (,timer-sym (,state-sym ,initial-state))
(lambda (&rest args)
(setq ,state-sym (apply ,state-function ,state-sym args))
(when (timerp ,timer-sym)
(cancel-timer ,timer-sym))
(setq ,timer-sym
(run-with-timer
,secs nil (lambda ()
(apply ,function (if (listp ,state-sym)
,state-sym
(list ,state-sym)))
(setq ,state-sym ,initial-state))))))))
(defconst timer-duration-words (defconst timer-duration-words
(list (cons "microsec" 0.000001) (list (cons "microsec" 0.000001)

View file

@ -599,9 +599,7 @@ Key bindings:
(add-hook 'change-major-mode-hook #'image-toggle-display-text nil t) (add-hook 'change-major-mode-hook #'image-toggle-display-text nil t)
(add-hook 'after-revert-hook #'image-after-revert-hook nil t) (add-hook 'after-revert-hook #'image-after-revert-hook nil t)
(add-hook 'window-size-change-functions #'image--window-change nil t) (add-hook 'window-state-change-functions #'image--window-state-change nil t)
(add-hook 'window-state-change-functions #'image--window-change nil t)
(add-hook 'window-selection-change-functions #'image--window-change nil t)
(run-mode-hooks 'image-mode-hook) (run-mode-hooks 'image-mode-hook)
(let ((image (image-get-display-property)) (let ((image (image-get-display-property))
@ -860,9 +858,17 @@ Otherwise, display the image by calling `image-mode'."
(get-buffer-window-list (current-buffer) 'nomini 'visible)) (get-buffer-window-list (current-buffer) 'nomini 'visible))
(image-toggle-display-image))) (image-toggle-display-image)))
(defvar image--window-change-function (defun image--window-state-change (window)
(debounce 1.0 ;; Wait for a bit of idle-time before actually performing the change,
(lambda (window) ;; so as to batch together sequences of closely consecutive size changes.
;; `image-fit-to-window' just changes one value in a plist. The actual
;; image resizing happens later during redisplay. So if those
;; consecutive calls happen without any redisplay between them,
;; the costly operation of image resizing should happen only once.
(run-with-idle-timer 1 nil #'image-fit-to-window window))
(defun image-fit-to-window (window)
"Adjust size of image to display it exactly in WINDOW boundaries."
(when (window-live-p window) (when (window-live-p window)
(with-current-buffer (window-buffer) (with-current-buffer (window-buffer)
(when (derived-mode-p 'image-mode) (when (derived-mode-p 'image-mode)
@ -876,10 +882,7 @@ Otherwise, display the image by calling `image-mode'."
(when (and image-width image-height (when (and image-width image-height
(or (not (= image-width window-width)) (or (not (= image-width window-width))
(not (= image-height window-height)))) (not (= image-height window-height))))
(image-toggle-display-image))))))))))) (image-toggle-display-image)))))))))
(defun image--window-change (window)
(funcall image--window-change-function window))
;;; Animated images ;;; Animated images

View file

@ -1017,7 +1017,14 @@ has no effect."
If N is 3, then the image size will be increased by 30%. The If N is 3, then the image size will be increased by 30%. The
default is 20%." default is 20%."
(interactive "P") (interactive "P")
(funcall image--change-size-function ;; Wait for a bit of idle-time before actually performing the change,
;; so as to batch together sequences of closely consecutive size changes.
;; `image--change-size' just changes one value in a plist. The actual
;; image resizing happens later during redisplay. So if those
;; consecutive calls happen without any redisplay between them,
;; the costly operation of image resizing should happen only once.
(run-with-idle-timer 0.3 nil
#'image--change-size
(if n (if n
(1+ (/ (prefix-numeric-value n) 10.0)) (1+ (/ (prefix-numeric-value n) 10.0))
1.2))) 1.2)))
@ -1027,7 +1034,14 @@ default is 20%."
If N is 3, then the image size will be decreased by 30%. The If N is 3, then the image size will be decreased by 30%. The
default is 20%." default is 20%."
(interactive "P") (interactive "P")
(funcall image--change-size-function ;; Wait for a bit of idle-time before actually performing the change,
;; so as to batch together sequences of closely consecutive size changes.
;; `image--change-size' just changes one value in a plist. The actual
;; image resizing happens later during redisplay. So if those
;; consecutive calls happen without any redisplay between them,
;; the costly operation of image resizing should happen only once.
(run-with-idle-timer 0.3 nil
#'image--change-size
(if n (if n
(- 1 (/ (prefix-numeric-value n) 10.0)) (- 1 (/ (prefix-numeric-value n) 10.0))
0.8))) 0.8)))
@ -1065,16 +1079,12 @@ default is 20%."
(plist-put (cdr image) :type 'imagemagick)) (plist-put (cdr image) :type 'imagemagick))
image)) image))
(defvar image--change-size-function (defun image--change-size (factor)
(debounce-reduce 0.3 1
(lambda (state factor)
(* state factor))
(lambda (factor)
(let* ((image (image--get-imagemagick-and-warn)) (let* ((image (image--get-imagemagick-and-warn))
(new-image (image--image-without-parameters image)) (new-image (image--image-without-parameters image))
(scale (image--current-scaling image new-image))) (scale (image--current-scaling image new-image)))
(setcdr image (cdr new-image)) (setcdr image (cdr new-image))
(plist-put (cdr image) :scale (* scale factor)))))) (plist-put (cdr image) :scale (* scale factor))))
(defun image--image-without-parameters (image) (defun image--image-without-parameters (image)
(cons (pop image) (cons (pop image)