diff options
| author | Juri Linkov | 2019-11-30 23:16:03 +0200 |
|---|---|---|
| committer | Juri Linkov | 2019-11-30 23:16:03 +0200 |
| commit | d64ea182fb6e2bf3af8ac8a289e8029ded36407e (patch) | |
| tree | 0e7835bc8f2f2b62a64a1fed3f72871abf3ad611 /lisp | |
| parent | 9ac78ef56c184b757f9866edc3092eb62e259c90 (diff) | |
| download | emacs-d64ea182fb6e2bf3af8ac8a289e8029ded36407e.tar.gz emacs-d64ea182fb6e2bf3af8ac8a289e8029ded36407e.zip | |
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.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/emacs-lisp/timer.el | 44 | ||||
| -rw-r--r-- | lisp/image-mode.el | 49 | ||||
| -rw-r--r-- | lisp/image.el | 46 |
3 files changed, 54 insertions, 85 deletions
diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el index 5fdf9a426a7..561cc70078f 100644 --- a/lisp/emacs-lisp/timer.el +++ b/lisp/emacs-lisp/timer.el | |||
| @@ -488,50 +488,6 @@ The argument should be a value previously returned by `with-timeout-suspend'." | |||
| 488 | If the user does not answer after SECONDS seconds, return DEFAULT-VALUE." | 488 | If the user does not answer after SECONDS seconds, return DEFAULT-VALUE." |
| 489 | (with-timeout (seconds default-value) | 489 | (with-timeout (seconds default-value) |
| 490 | (y-or-n-p prompt))) | 490 | (y-or-n-p prompt))) |
| 491 | |||
| 492 | (defmacro debounce (secs function) | ||
| 493 | "Call FUNCTION after SECS seconds have elapsed. | ||
| 494 | Postpone FUNCTION call until after SECS seconds have elapsed since the | ||
| 495 | last time it was invoked. On consecutive calls within the interval of | ||
| 496 | SECS seconds, cancel all previous calls that occur rapidly in quick succession, | ||
| 497 | and execute only the last call. This improves performance of event processing." | ||
| 498 | (declare (indent 1) (debug t)) | ||
| 499 | (let ((timer-sym (make-symbol "timer"))) | ||
| 500 | `(let (,timer-sym) | ||
| 501 | (lambda (&rest args) | ||
| 502 | (when (timerp ,timer-sym) | ||
| 503 | (cancel-timer ,timer-sym)) | ||
| 504 | (setq ,timer-sym | ||
| 505 | (run-with-timer | ||
| 506 | ,secs nil (lambda () | ||
| 507 | (apply ,function args)))))))) | ||
| 508 | |||
| 509 | (defmacro debounce-reduce (secs initial-state state-function function) | ||
| 510 | "Call FUNCTION after SECS seconds have elapsed. | ||
| 511 | Postpone FUNCTION call until after SECS seconds have elapsed since the | ||
| 512 | last time it was invoked. On consecutive calls within the interval of | ||
| 513 | SECS seconds, cancel all previous calls that occur rapidly in quick succession, | ||
| 514 | and execute only the last call. This improves performance of event processing. | ||
| 515 | |||
| 516 | STATE-FUNCTION can be used to accumulate the state on consecutive calls | ||
| 517 | starting with the value of INITIAL-STATE, and then execute the last call | ||
| 518 | with the collected state value." | ||
| 519 | (declare (indent 1) (debug t)) | ||
| 520 | (let ((timer-sym (make-symbol "timer")) | ||
| 521 | (state-sym (make-symbol "state"))) | ||
| 522 | `(let (,timer-sym (,state-sym ,initial-state)) | ||
| 523 | (lambda (&rest args) | ||
| 524 | (setq ,state-sym (apply ,state-function ,state-sym args)) | ||
| 525 | (when (timerp ,timer-sym) | ||
| 526 | (cancel-timer ,timer-sym)) | ||
| 527 | (setq ,timer-sym | ||
| 528 | (run-with-timer | ||
| 529 | ,secs nil (lambda () | ||
| 530 | (apply ,function (if (listp ,state-sym) | ||
| 531 | ,state-sym | ||
| 532 | (list ,state-sym))) | ||
| 533 | (setq ,state-sym ,initial-state)))))))) | ||
| 534 | |||
| 535 | 491 | ||
| 536 | (defconst timer-duration-words | 492 | (defconst timer-duration-words |
| 537 | (list (cons "microsec" 0.000001) | 493 | (list (cons "microsec" 0.000001) |
diff --git a/lisp/image-mode.el b/lisp/image-mode.el index 09d7828047e..b9ba376cafc 100644 --- a/lisp/image-mode.el +++ b/lisp/image-mode.el | |||
| @@ -599,9 +599,7 @@ Key bindings: | |||
| 599 | 599 | ||
| 600 | (add-hook 'change-major-mode-hook #'image-toggle-display-text nil t) | 600 | (add-hook 'change-major-mode-hook #'image-toggle-display-text nil t) |
| 601 | (add-hook 'after-revert-hook #'image-after-revert-hook nil t) | 601 | (add-hook 'after-revert-hook #'image-after-revert-hook nil t) |
| 602 | (add-hook 'window-size-change-functions #'image--window-change nil t) | 602 | (add-hook 'window-state-change-functions #'image--window-state-change nil t) |
| 603 | (add-hook 'window-state-change-functions #'image--window-change nil t) | ||
| 604 | (add-hook 'window-selection-change-functions #'image--window-change nil t) | ||
| 605 | 603 | ||
| 606 | (run-mode-hooks 'image-mode-hook) | 604 | (run-mode-hooks 'image-mode-hook) |
| 607 | (let ((image (image-get-display-property)) | 605 | (let ((image (image-get-display-property)) |
| @@ -860,26 +858,31 @@ Otherwise, display the image by calling `image-mode'." | |||
| 860 | (get-buffer-window-list (current-buffer) 'nomini 'visible)) | 858 | (get-buffer-window-list (current-buffer) 'nomini 'visible)) |
| 861 | (image-toggle-display-image))) | 859 | (image-toggle-display-image))) |
| 862 | 860 | ||
| 863 | (defvar image--window-change-function | 861 | (defun image--window-state-change (window) |
| 864 | (debounce 1.0 | 862 | ;; Wait for a bit of idle-time before actually performing the change, |
| 865 | (lambda (window) | 863 | ;; so as to batch together sequences of closely consecutive size changes. |
| 866 | (when (window-live-p window) | 864 | ;; `image-fit-to-window' just changes one value in a plist. The actual |
| 867 | (with-current-buffer (window-buffer) | 865 | ;; image resizing happens later during redisplay. So if those |
| 868 | (when (derived-mode-p 'image-mode) | 866 | ;; consecutive calls happen without any redisplay between them, |
| 869 | (let ((spec (image-get-display-property))) | 867 | ;; the costly operation of image resizing should happen only once. |
| 870 | (when (eq (car-safe spec) 'image) | 868 | (run-with-idle-timer 1 nil #'image-fit-to-window window)) |
| 871 | (let* ((image-width (plist-get (cdr spec) :max-width)) | 869 | |
| 872 | (image-height (plist-get (cdr spec) :max-height)) | 870 | (defun image-fit-to-window (window) |
| 873 | (edges (window-inside-pixel-edges window)) | 871 | "Adjust size of image to display it exactly in WINDOW boundaries." |
| 874 | (window-width (- (nth 2 edges) (nth 0 edges))) | 872 | (when (window-live-p window) |
| 875 | (window-height (- (nth 3 edges) (nth 1 edges)))) | 873 | (with-current-buffer (window-buffer) |
| 876 | (when (and image-width image-height | 874 | (when (derived-mode-p 'image-mode) |
| 877 | (or (not (= image-width window-width)) | 875 | (let ((spec (image-get-display-property))) |
| 878 | (not (= image-height window-height)))) | 876 | (when (eq (car-safe spec) 'image) |
| 879 | (image-toggle-display-image))))))))))) | 877 | (let* ((image-width (plist-get (cdr spec) :max-width)) |
| 880 | 878 | (image-height (plist-get (cdr spec) :max-height)) | |
| 881 | (defun image--window-change (window) | 879 | (edges (window-inside-pixel-edges window)) |
| 882 | (funcall image--window-change-function window)) | 880 | (window-width (- (nth 2 edges) (nth 0 edges))) |
| 881 | (window-height (- (nth 3 edges) (nth 1 edges)))) | ||
| 882 | (when (and image-width image-height | ||
| 883 | (or (not (= image-width window-width)) | ||
| 884 | (not (= image-height window-height)))) | ||
| 885 | (image-toggle-display-image))))))))) | ||
| 883 | 886 | ||
| 884 | 887 | ||
| 885 | ;;; Animated images | 888 | ;;; Animated images |
diff --git a/lisp/image.el b/lisp/image.el index c4304782327..f4ed4e79fc0 100644 --- a/lisp/image.el +++ b/lisp/image.el | |||
| @@ -1017,20 +1017,34 @@ has no effect." | |||
| 1017 | If N is 3, then the image size will be increased by 30%. The | 1017 | If N is 3, then the image size will be increased by 30%. The |
| 1018 | default is 20%." | 1018 | default is 20%." |
| 1019 | (interactive "P") | 1019 | (interactive "P") |
| 1020 | (funcall image--change-size-function | 1020 | ;; Wait for a bit of idle-time before actually performing the change, |
| 1021 | (if n | 1021 | ;; so as to batch together sequences of closely consecutive size changes. |
| 1022 | (1+ (/ (prefix-numeric-value n) 10.0)) | 1022 | ;; `image--change-size' just changes one value in a plist. The actual |
| 1023 | 1.2))) | 1023 | ;; image resizing happens later during redisplay. So if those |
| 1024 | ;; consecutive calls happen without any redisplay between them, | ||
| 1025 | ;; the costly operation of image resizing should happen only once. | ||
| 1026 | (run-with-idle-timer 0.3 nil | ||
| 1027 | #'image--change-size | ||
| 1028 | (if n | ||
| 1029 | (1+ (/ (prefix-numeric-value n) 10.0)) | ||
| 1030 | 1.2))) | ||
| 1024 | 1031 | ||
| 1025 | (defun image-decrease-size (&optional n) | 1032 | (defun image-decrease-size (&optional n) |
| 1026 | "Decrease the image size by a factor of N. | 1033 | "Decrease the image size by a factor of N. |
| 1027 | If N is 3, then the image size will be decreased by 30%. The | 1034 | If N is 3, then the image size will be decreased by 30%. The |
| 1028 | default is 20%." | 1035 | default is 20%." |
| 1029 | (interactive "P") | 1036 | (interactive "P") |
| 1030 | (funcall image--change-size-function | 1037 | ;; Wait for a bit of idle-time before actually performing the change, |
| 1031 | (if n | 1038 | ;; so as to batch together sequences of closely consecutive size changes. |
| 1032 | (- 1 (/ (prefix-numeric-value n) 10.0)) | 1039 | ;; `image--change-size' just changes one value in a plist. The actual |
| 1033 | 0.8))) | 1040 | ;; image resizing happens later during redisplay. So if those |
| 1041 | ;; consecutive calls happen without any redisplay between them, | ||
| 1042 | ;; the costly operation of image resizing should happen only once. | ||
| 1043 | (run-with-idle-timer 0.3 nil | ||
| 1044 | #'image--change-size | ||
| 1045 | (if n | ||
| 1046 | (- 1 (/ (prefix-numeric-value n) 10.0)) | ||
| 1047 | 0.8))) | ||
| 1034 | 1048 | ||
| 1035 | (defun image-mouse-increase-size (&optional event) | 1049 | (defun image-mouse-increase-size (&optional event) |
| 1036 | "Increase the image size using the mouse." | 1050 | "Increase the image size using the mouse." |
| @@ -1065,16 +1079,12 @@ default is 20%." | |||
| 1065 | (plist-put (cdr image) :type 'imagemagick)) | 1079 | (plist-put (cdr image) :type 'imagemagick)) |
| 1066 | image)) | 1080 | image)) |
| 1067 | 1081 | ||
| 1068 | (defvar image--change-size-function | 1082 | (defun image--change-size (factor) |
| 1069 | (debounce-reduce 0.3 1 | 1083 | (let* ((image (image--get-imagemagick-and-warn)) |
| 1070 | (lambda (state factor) | 1084 | (new-image (image--image-without-parameters image)) |
| 1071 | (* state factor)) | 1085 | (scale (image--current-scaling image new-image))) |
| 1072 | (lambda (factor) | 1086 | (setcdr image (cdr new-image)) |
| 1073 | (let* ((image (image--get-imagemagick-and-warn)) | 1087 | (plist-put (cdr image) :scale (* scale factor)))) |
| 1074 | (new-image (image--image-without-parameters image)) | ||
| 1075 | (scale (image--current-scaling image new-image))) | ||
| 1076 | (setcdr image (cdr new-image)) | ||
| 1077 | (plist-put (cdr image) :scale (* scale factor)))))) | ||
| 1078 | 1088 | ||
| 1079 | (defun image--image-without-parameters (image) | 1089 | (defun image--image-without-parameters (image) |
| 1080 | (cons (pop image) | 1090 | (cons (pop image) |