center-content-mode/center-content.el
2024-09-14 23:25:33 +02:00

101 lines
4.1 KiB
EmacsLisp

;;; center-content.el --- Center buffer content horizontally and vertically -*- 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)
(defvar-local center-content--horizontal-overlay nil
"Overlay used to add left margin for horizontal centering.")
(defvar-local center-content--original-header-line-format nil
"Stores the original `header-line-format` to restore upon disabling.")
(defvar-local center-content--original-mode-line-format nil
"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)))
(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--enable ()
"Enable horizontal and vertical centering of content."
(let* ((content-size (center-content--content-pixel-size))
(left-margin (center-content--calculate-left-margin (car content-size)))
(top-margin (center-content--calculate-top-margin (cdr content-size))))
;; Apply horizontal centering using overlay
(let ((overlay (make-overlay (point-min) (point-max))))
(overlay-put overlay 'line-prefix
(propertize " " 'display `(space :width ,left-margin)))
(setq center-content--horizontal-overlay overlay))
;; Apply vertical centering using header-line-format
;; Save and remap the header-line face to match the default face
(setq center-content--original-header-line-format header-line-format)
(setq center-content--header-line-face-remap-cookie
(face-remap-add-relative 'header-line 'default))
(setq header-line-format
(propertize " " 'display `(height ,top-margin)))
;; Save and hide the mode line
(setq center-content--original-mode-line-format mode-line-format)
(setq mode-line-format nil)
;; Force redisplay
(force-mode-line-update)
(redisplay)))
(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))
;; 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)
(setq center-content--original-mode-line-format nil)
;; Restore the original mode-line-format
(setq mode-line-format center-content--original-mode-line-format)
(setq center-content--original-header-line-format nil)
;; Force redisplay
(force-mode-line-update)
(redisplay))
;;;###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 nil ;; Remove the lighter to keep the mode line clean
(if center-content-mode
(center-content--enable)
(center-content--disable)))
(provide 'center-content)
;;; center-content.el ends here