diff --git a/lisp/emacs-lisp/icons.el b/lisp/emacs-lisp/icons.el index ff4f20c207..96f5ea6b08 100644 --- a/lisp/emacs-lisp/icons.el +++ b/lisp/emacs-lisp/icons.el @@ -202,7 +202,9 @@ icons--create :height (if (eq height 'line) (window-default-line-height) height) - :scale 1 :ascent 'center) + :scale 1 + :rotation (plist-get keywords :rotation) + :ascent (or (plist-get keywords :ascent) 'center)) (create-image file)))))) (cl-defmethod icons--create ((_type (eql 'emoji)) icon _keywords) diff --git a/lisp/outline.el b/lisp/outline.el index 25ef1616b9..32c5799814 100644 --- a/lisp/outline.el +++ b/lisp/outline.el @@ -292,6 +292,16 @@ outline-minor-mode-use-buttons :safe #'booleanp :version "29.1") +(defcustom outline-minor-mode-use-margins '(derived-mode . special-mode) + "Whether to display clickable buttons on the margins. +The value should be a `buffer-match-p' condition. + +These buttons can be used to hide and show the body under the heading. +Note that this feature is meant to be used in editing buffers." + :type 'buffer-predicate + :safe #'booleanp + :version "29.1") + (define-icon outline-open nil '((image "outline-open.svg" "outline-open.pbm" :height 15 :ascent center) @@ -312,6 +322,27 @@ outline-close :version "29.1" :help-echo "Close this section") +(define-icon outline-open-in-margins outline-open + '((image "outline-open.svg" "outline-open.pbm" + :height 10 :ascent center)) + "Icon used for buttons for opening a section in outline buffers." + :version "29.1" + :help-echo "Open this section") + +(define-icon outline-close-in-margins outline-close + '((image "outline-open.svg" "outline-open.pbm" + :height 10 :ascent center :rotation -90)) + "Icon used for buttons for closing a section in outline buffers." + :version "29.1" + :help-echo "Close this section") + +(define-icon outline-close-rtl-in-margins outline-close + '((image "outline-open.svg" "outline-open.pbm" + :height 10 :ascent center :rotation 90)) + "Icon used for buttons for closing a section in outline buffers." + :version "29.1" + :help-echo "Close this section") + (defvar outline-level #'outline-level "Function of no args to compute a header's nesting level in an outline. @@ -453,6 +486,11 @@ outline-minor-mode (key-description outline-minor-mode-prefix) outline-mode-prefix-map) (if outline-minor-mode (progn + (when (outline--use-margins-p) + (if (eq (current-bidi-paragraph-direction) 'right-to-left) + (setq-local right-margin-width (1+ right-margin-width)) + (setq-local left-margin-width (1+ left-margin-width))) + (setq-local fringes-outside-margins t)) (when outline-minor-mode-highlight (if (and global-font-lock-mode (font-lock-specified-p major-mode)) (progn @@ -473,6 +511,11 @@ outline-minor-mode (font-lock-remove-keywords nil outline-font-lock-keywords)) (remove-overlays nil nil 'outline-overlay t) (font-lock-flush)) + (when (outline--use-margins-p) + (if (eq (current-bidi-paragraph-direction) 'right-to-left) + (setq-local right-margin-width (1- right-margin-width)) + (setq-local left-margin-width (1- left-margin-width))) + (setq-local fringes-outside-margins nil)) (setq line-move-ignore-invisible nil) ;; Cause use of ellipses for invisible text. (remove-from-invisibility-spec '(outline . t)) @@ -483,6 +526,10 @@ outline--use-buttons-p (and outline-minor-mode (buffer-match-p outline-minor-mode-use-buttons (current-buffer)))) +(defun outline--use-margins-p () + (and outline-minor-mode + (buffer-match-p outline-minor-mode-use-margins (current-buffer)))) + (defvar-local outline-heading-alist () "Alist associating a heading for every possible level. Each entry is of the form (HEADING . LEVEL). @@ -1012,10 +1061,43 @@ outline--make-button-overlay (overlay-put o 'face (plist-get icon 'face)))) o)) -(defun outline--insert-open-button () +(defun outline--make-margin-overlay (type) + (let ((o (seq-find (lambda (o) + (overlay-get o 'outline-margin)) + (overlays-at (point))))) + (unless o + (setq o (make-overlay (point) (1+ (point)))) + (overlay-put o 'follow-link 'mouse-face) + (overlay-put o 'mouse-face 'highlight) + (overlay-put o 'outline-margin t)) + (let ((icon + (icon-elements (if (eq type 'close) + (if (eq (current-bidi-paragraph-direction) + 'right-to-left) + 'outline-close-rtl-in-margins + 'outline-close-in-margins) + 'outline-open-in-margins))) + (inhibit-read-only t)) + (overlay-put + o 'before-string + (propertize " " 'display + `((margin ,(if (eq (current-bidi-paragraph-direction) + 'right-to-left) + 'right-margin 'left-margin)) + ,(or (plist-get icon 'image) + (plist-get icon 'string)))))) + o)) + +(defun outline--insert-open-button (&optional margins-p) (with-silent-modifications (save-excursion - (beginning-of-line) + (beginning-of-line) + (if margins-p + (let ((o (outline--make-margin-overlay 'open))) + (overlay-put o 'help-echo "Click to hide") + (overlay-put o 'keymap + (define-keymap + "" #'outline-hide-subtree))) (when (derived-mode-p 'special-mode) (let ((inhibit-read-only t)) (insert " ") @@ -1025,12 +1107,18 @@ outline--insert-open-button (overlay-put o 'keymap (define-keymap "RET" #'outline-hide-subtree - "" #'outline-hide-subtree)))))) + "" #'outline-hide-subtree))))))) -(defun outline--insert-close-button () +(defun outline--insert-close-button (&optional margins-p) (with-silent-modifications (save-excursion - (beginning-of-line) + (beginning-of-line) + (if margins-p + (let ((o (outline--make-margin-overlay 'close))) + (overlay-put o 'help-echo "Click to show") + (overlay-put o 'keymap + (define-keymap + "" #'outline-show-subtree))) (when (derived-mode-p 'special-mode) (let ((inhibit-read-only t)) (insert " ") @@ -1040,23 +1128,25 @@ outline--insert-close-button (overlay-put o 'keymap (define-keymap "RET" #'outline-show-subtree - "" #'outline-show-subtree)))))) + "" #'outline-show-subtree))))))) (defun outline--fix-up-all-buttons (&optional from to) - (when (outline--use-buttons-p) - (when from - (save-excursion - (goto-char from) - (setq from (line-beginning-position)))) - (outline-map-region - (lambda () - (if (save-excursion - (outline-end-of-heading) - (seq-some (lambda (o) (eq (overlay-get o 'invisible) 'outline)) - (overlays-at (point)))) - (outline--insert-close-button) - (outline--insert-open-button))) - (or from (point-min)) (or to (point-max))))) + (let ((buttons-p (outline--use-buttons-p)) + (margins-p (outline--use-margins-p))) + (when (or buttons-p margins-p) + (when from + (save-excursion + (goto-char from) + (setq from (line-beginning-position)))) + (outline-map-region + (lambda () + (if (save-excursion + (outline-end-of-heading) + (seq-some (lambda (o) (eq (overlay-get o 'invisible) 'outline)) + (overlays-at (point)))) + (outline--insert-close-button margins-p) + (outline--insert-open-button margins-p))) + (or from (point-min)) (or to (point-max)))))) (define-obsolete-function-alias 'hide-subtree #'outline-hide-subtree "25.1")