;;; center-content.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: (require 'face-remap) (defgroup center-content () "Center buffer content." :group 'convinience :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 t "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.") (defvar-local center-content--original-header-line-format header-line-format "Stores the original `header-line-format` to restore upon disabling.") (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`.") (defun center-content--content-pixel-size () "Return the size of the content of the visible text in pixels." (window-text-pixel-size (selected-window) (window-start) (window-end nil t))) (defun center-content--calculate-left-margin (content-width) "Calculate the left margin relative to CONTENT-WIDTH." (let ((window-width (window-pixel-width)) (column-width (string-pixel-width " "))) (/ (max 0 (- window-width content-width)) (* 2 column-width)))) (defun center-content--calculate-top-margin (content-height) "Calculate the top margin relative to CONTENT-HEIGHT." (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))) (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)))) (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) (setq center-content--horizontal-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 (setq mode-line-format center-content--original-mode-line-format)) ;;;###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. The mode line is hidden for a clean appearance." :init-value nil :global nil :lighter " Center" (if center-content-mode (center-content--enable) (center-content--disable))) (provide 'center-content) ;;; center-content.el ends here