center-content-mode/center-content-mode.el

158 lines
6.2 KiB
EmacsLisp
Raw Normal View History

2024-09-24 06:18:31 +00:00
;;; center-content-mode.el --- Center buffer content -*- lexical-binding: t; -*-
;;; Commentary:
;; This package provides a minor mode `center-content-mode' that centers the
;; content of the current buffer both horizontally and vertically.
;;; Code:
2024-09-14 21:25:33 +00:00
(require 'face-remap)
2024-09-14 23:52:25 +00:00
(defgroup center-content ()
"Center buffer content."
2024-09-20 21:51:28 +00:00
:group 'convenience
2024-09-14 23:52:25 +00:00
:prefix "center-content-")
(defcustom center-content-horizontal t
"Enable horizontal centering."
:type 'boolean
:group 'center-content)
(defcustom center-content-vertical t
"Enable vertical centering."
:type 'boolean
:group 'center-content)
(defcustom center-content-vertical-by-overlay nil
"Enable vertical by using overlays.
Using overlays for vertical centering may give some undesirable
effects on the first line. By default, vertical centering is achieved
by using setting `header-line-format'."
:type 'boolean
:group 'center-content)
(defcustom center-content-hide-mode-line nil
2024-09-14 23:52:25 +00:00
"Hide mode-line when centering."
:type 'boolean
:group 'center-content)
(defvar-local center-content--horizontal-overlay nil
"Overlay used to add left margin for horizontal centering.")
(defvar-local center-content--vertical-overlay nil
"Overlay used to add top margin for vertical centering.")
2024-09-14 23:52:25 +00:00
(defvar-local center-content--original-header-line-format header-line-format
"Stores the original `header-line-format` to restore upon disabling.")
2024-09-14 23:52:25 +00:00
(defvar-local center-content--original-mode-line-format mode-line-format
"Stores the original `mode-line-format` to restore upon disabling.")
(defvar-local center-content--header-line-face-remap-cookie nil
"Cookie for the face remapping of `header-line`.")
2024-09-14 21:22:11 +00:00
(defun center-content--content-pixel-size ()
2024-09-14 21:25:33 +00:00
"Return the size of the content of the visible text in pixels."
2024-09-14 21:37:52 +00:00
(window-text-pixel-size (selected-window) (window-start) (window-end nil t)))
2024-09-14 21:22:11 +00:00
(defun center-content--calculate-left-margin (content-width)
2024-09-14 21:25:33 +00:00
"Calculate the left margin relative to CONTENT-WIDTH."
2024-09-14 21:22:11 +00:00
(let ((window-width (window-pixel-width))
(column-width (string-pixel-width " ")))
(/ (max 0 (- window-width content-width)) (* 2 column-width))))
2024-09-14 21:22:11 +00:00
(defun center-content--calculate-top-margin (content-height)
2024-09-14 21:25:33 +00:00
"Calculate the top margin relative to CONTENT-HEIGHT."
2024-09-14 21:22:11 +00:00
(let ((window-height (window-pixel-height))
(line-height (default-line-height)))
(/ (max 0 (- window-height content-height)) (* 2 line-height))))
(defun center-content--horizontal (content-width)
"Add overlay for horizontal centering relative to CONTENT-WIDTH."
(let* ((left-margin (center-content--calculate-left-margin content-width))
(overlay (make-overlay (point-min) (point-max)))
(space (propertize " " 'display `(space :width ,left-margin))))
(overlay-put overlay 'line-prefix space)
(setq center-content--horizontal-overlay overlay)))
(defun center-content--vertical (content-height)
"Add overlay for vertical centering relative to CONTENT-HEIGHT."
(let* ((top-margin (center-content--calculate-top-margin content-height)))
;; Save and remap the header-line face to match the default face
(setq center-content--header-line-face-remap-cookie
(face-remap-add-relative 'header-line 'default))
(setq header-line-format (propertize " " 'display `(height ,top-margin)))))
(defun center-content--vertical-by-overlay (content-height)
"Add overlay for vertical centering relative to CONTENT-HEIGHT."
(let* ((top-margin (center-content--calculate-top-margin content-height))
(overlay (make-overlay (window-start) (window-start) nil t t))
(space (propertize " " 'display `(space :width 0 :height ,top-margin))))
(overlay-put overlay 'intangible t)
(overlay-put overlay 'before-string space)
(setq center-content--vertical-overlay overlay)))
(defun center-content--enable ()
"Enable horizontal and vertical centering of content."
(setq center-content--original-header-line-format header-line-format)
(setq center-content--original-mode-line-format mode-line-format)
(let* ((content-size (center-content--content-pixel-size)))
2024-09-14 23:52:25 +00:00
(when center-content-horizontal
(center-content--horizontal (car content-size)))
(when center-content-vertical
(if center-content-vertical-by-overlay
(center-content--vertical-by-overlay (cdr content-size))
(center-content--vertical (cdr content-size))))
2024-09-14 23:52:25 +00:00
(when center-content-hide-mode-line
(setq mode-line-format nil))))
(defun center-content--disable ()
"Disable centering of content."
;; Remove horizontal centering overlay
(when (overlayp center-content--horizontal-overlay)
(delete-overlay center-content--horizontal-overlay)
(setq center-content--horizontal-overlay nil))
;; Remove vertical centering overlay
(when (overlayp center-content--vertical-overlay)
(delete-overlay center-content--vertical-overlay)
2024-09-20 21:51:28 +00:00
(setq center-content--vertical-overlay nil))
;; Restore the header-line face remapping
(when center-content--header-line-face-remap-cookie
(face-remap-remove-relative center-content--header-line-face-remap-cookie)
(setq center-content--header-line-face-remap-cookie nil))
;; Restore the header-line-format
(setq header-line-format center-content--original-header-line-format)
;; Restore the original mode-line-format
2024-09-14 23:52:25 +00:00
(setq mode-line-format center-content--original-mode-line-format))
2024-09-20 22:26:46 +00:00
(defun center-content-update ()
"Updates the centering if `center-content-mode' is enabled."
(interactive)
(when (bound-and-true-p center-content-mode)
(center-content--disable)
(center-content--enable)))
;;;###autoload
(define-minor-mode center-content-mode
"Toggle horizontal and vertical centering of buffer content.
When enabled, the content of the buffer is centered both horizontally
and vertically within the window."
:init-value nil
:global nil
2024-09-15 00:05:53 +00:00
:lighter " Center"
(if center-content-mode
2024-09-24 06:19:27 +00:00
(progn
(center-content--enable)
(add-hook 'post-command-hook #'center-content-update 50 t)
(add-hook 'window-configuration-change-hook #'center-content-update 50 t))
(center-content--disable)
(remove-hook 'post-command-hook #'center-content-update t)
(remove-hook 'window-configuration-change-hook #'center-content-update t)))
2024-09-24 06:18:31 +00:00
(provide 'center-content-mode)
2024-09-24 06:18:31 +00:00
;;; center-content-mode.el ends here