'Vertical padding or margin on emacs buffer

There appears to be a plethora of emacs modes for adding horizontal padding (see https://github.com/ikame/centered-window-mode ), but none for vertical.

I would like to be able to specify a height to pad the top and bottom of an emacs buffer. My motivation is for screencasts and presentations in full screen mode where being in very top or bottom can be problematic as a projector might not show that content.



Solution 1:[1]

Here is an example of one of the concepts mentioned in my comments above. This example creates a window above and a window below the working window -- the top / bottom windows have invisible inactive mode-lines, and no right scroll bars, and the cursor-type is nil. The alist window-height may be adjusted manually inside the function named screencast-function.

(defun lawlist-split-below (buffer alist)
  (let ((window (split-window (selected-window) nil 'below)))
    (window--display-buffer buffer window
      'window alist display-buffer-mark-dedicated)))

(defun lawlist-split-above (buffer alist)
  (let ((window (split-window (selected-window) nil 'above)))
    (window--display-buffer buffer window
      'window alist display-buffer-mark-dedicated)))

(defun screencast-function ()
(interactive)
  (toggle-frame-fullscreen)
  (set-face-attribute 'default nil
    :background "black" :foreground "white" :font "Courier" :height 180)
  (set-face-attribute 'mode-line nil
    :height 140 :foreground "black" :background "#eab700" :box nil)
  (set-face-attribute 'mode-line-inactive nil
    :height 140 :foreground "black" :background "black" :box nil)
  (lawlist-split-below (get-buffer-create "foo") '((window-height . 2)))
  (with-current-buffer (get-buffer "foo")
    (setq cursor-type nil))
  (set-window-scroll-bars (get-buffer-window "foo") 0 'right nil)
  (lawlist-split-above (get-buffer-create "bar") '((window-height . 2)))
  (with-current-buffer (get-buffer "bar")
    (setq cursor-type nil))
  (set-window-scroll-bars (get-buffer-window "bar") 0 'right nil) )

Solution 2:[2]

First Rough Draft (July 30, 2014):  This example creates an invisible overlay spacer at the beginning and ending of the visible window. The mode-line, however, remains at the very bottom. Some modes that use overlays may not be compatible with this example. To see this example in action, just type M-x screencast-mode.

(defun screencast-function
  (&optional
    screencast-old-window-start
    screencast-old-window-end
    screencast-old-window-end-forced
    screencast-new-window-start
    screencast-new-window-end)
  (save-excursion
    (let* (
        (window-start
          (cond
            (screencast-old-window-start
              screencast-old-window-start)
            (screencast-new-window-start
              screencast-new-window-start)
            (t (window-start))))
        (window-end
          (cond
            ((and
                screencast-old-window-end
                screencast-old-window-end-forced
                (= screencast-old-window-end screencast-old-window-end-forced))
              screencast-old-window-end)
            ((and
                screencast-old-window-end
                screencast-old-window-end-forced
                (> screencast-old-window-end-forced screencast-old-window-end))
              screencast-old-window-end-forced)
            (screencast-new-window-end
              screencast-new-window-end)
            (t (window-end (selected-window) t))))
        (top-margin 5)
        (bottom-margin 5)
        end-point
        (top-margin-overlay
          (propertize (char-to-string ?\uE001)
            'display `((space :align-to 0 :height ,top-margin))))
        (bottom-margin-overlay
          (propertize (char-to-string ?\uE001)
            'display `((space :align-to 0 :height ,bottom-margin)))) )
      (screencast-delete-overlays (point-min) (point-max))
      (setq top-margin-overlay-string top-margin-overlay)
      (setq bottom-margin-overlay-string bottom-margin-overlay)
      (overlay-put (make-overlay window-start window-start)
        'before-string top-margin-overlay)
      (setq window-end (window-end nil t))
      (goto-char window-end)
      (vertical-motion (- 2 bottom-margin))
      (setq end-point (point))
      (overlay-put (make-overlay end-point end-point)
        'before-string bottom-margin-overlay)
      (setq screencast-overlays-exist-p t) )))

(defun screencast-remove-overlays (beg end name val)
  "Remove the overlays."
     ;; DEBUGGING
  ;; (unless (and beg end name val)
  ;;   (message "ERROR -- beg:  %s | end:  %s | name:  %s | val:  %s" beg end name val))
  (when (and beg end name val)
    (overlay-recenter end)
    (dolist (o (overlays-in beg end))
      (when (eq (overlay-get o name) val)
        (delete-overlay o)))))

(defun screencast-delete-overlays (start end)
"Delete overlays from `start` to `end`."
  (when screencast-overlays-exist-p
    (dolist (ov `(
        ,top-margin-overlay-string
        ,bottom-margin-overlay-string  ))
    (screencast-remove-overlays start end 'before-string ov)) 
    (setq screencast-overlays-exist-p nil)))

(defvar top-margin-overlay-string nil
"This local variable stores the `top-margin-overlay-string`.")
(make-variable-buffer-local 'top-margin-overlay-string)

(defvar bottom-margin-overlay-string nil
"This local variable stores the `bottom-margin-overlay-string`.")
(make-variable-buffer-local 'bottom-margin-overlay-string)

(defvar screencast-overlays-exist-p nil
"A simple test for the existence of screencast overlays.")
(make-variable-buffer-local 'screencast-overlays-exist-p)

(defvar screencast-old-window-start nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'screencast-old-window-start)

(defvar screencast-old-window-end nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'screencast-old-window-end)

(defvar screencast-old-window-end-forced nil
"This local variable is set within the `post-command-hook`; and,
is also used by the `window-scroll-functions` hook.")
(make-variable-buffer-local 'screencast-old-window-end-forced)

(defvar screencast-new-window-start nil
"This local variable is set within the `window-scroll-functions`.")
(make-variable-buffer-local 'screencast-new-window-start)

(defvar screencast-new-window-end nil
"This local variable is set within the `window-scroll-functions`.")
(make-variable-buffer-local 'screencast-new-window-end)

(defun screencast-post-command-hook ()
"NOT good for things like: `beginning-of-buffer`; `end-of-buffer`; `yank`; etc.
NOTE:  When using `this-command` in conjunction with the `post-command-hook`,
the `window-scroll-functions` hook would need to use `last-command`."
  (when
      (and
        (not (minibufferp))
        (window-live-p (get-buffer-window (current-buffer))))
    (setq screencast-old-window-start (window-start))
    (setq screencast-old-window-end (window-end))
    (setq screencast-old-window-end-forced (window-end nil t))
    (when
        (or
          (and
            (not (< (point) screencast-old-window-start))
            (not (> (point) screencast-old-window-end))
            (not (> (point) screencast-old-window-end-forced)))
          ;; special situation when deleting region greater than size of window.
          (and
            (region-active-p)
            (< screencast-old-window-end 0))
          ;; special situation when deleting region greater than size of window.
          (and
            (region-active-p)
            (> (point) screencast-old-window-start)
            (> (point) screencast-old-window-end)
            (< (point) screencast-old-window-end-forced)) )
      (screencast-function screencast-old-window-start screencast-old-window-end screencast-old-window-end-forced nil nil) )))

(defun screencast-window-scroll-functions (win _start)
"Good for things like: `beginning-of-buffer`; `end-of-buffer`; `yank`; etc.
NOTE:  When using `this-command` in conjunction with the `post-command-hook`,
the `window-scroll-functions` hook would need to use `last-command`."
  (when
      (and
        screencast-old-window-start
        screencast-old-window-end
        screencast-old-window-end-forced
        (not (minibufferp))
        (window-live-p (get-buffer-window (current-buffer))))
    (when
        (or
          (not (= _start screencast-old-window-start))
          (< (point) screencast-old-window-start)
          (> (point) screencast-old-window-end)
          (> (point) screencast-old-window-end-forced))
      (setq screencast-new-window-start _start)
      (setq screencast-new-window-end (window-end win t))
      (screencast-function nil nil nil screencast-new-window-start screencast-new-window-end)
      (setq screencast-old-window-start nil)
      (setq screencast-old-window-end nil)
      (setq screencast-old-window-end-forced nil))))

(define-minor-mode screencast-mode
"A minor-mode for screencasts."
  :init-value nil
  :lighter " screencast"
  :keymap nil
  :global nil
  :group nil
  (cond
    (screencast-mode
      (condition-case error
        (progn
          (setq scroll-conservatively 101)
          (add-hook 'post-command-hook 'screencast-post-command-hook nil t)
          (add-hook 'window-scroll-functions 'screencast-window-scroll-functions nil t)
          (message "Turned ON `screencast-mode`."))
        (error
         (screencast-mode 0)
         (signal (car error) (cdr error)))))
    ((not screencast-mode)
      (remove-hook 'post-command-hook 'screencast-post-command-hook t)
      (remove-hook 'window-scroll-functions 'screencast-window-scroll-functions t)
      (screencast-delete-overlays (point-min) (point-max))
      (message "Turned OFF `screencast-mode`.") )))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Solution 3:[3]

I have created a minor mode related to this which is now available on MELPA:

TopSpace - Scroll down & recenter top lines in Emacs / get upper margins/padding

topspace

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 lawlist
Solution 3