2

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.

Matt Harrison
  • 1,225
  • 11
  • 12
  • Here is a related thread: http://stackoverflow.com/a/12632805/2112489 that suggests something like: `(set-frame-parameter nil 'internal-border-width 10)` – lawlist Jul 30 '14 at 15:40
  • Thanks, sadly fullscreen on Mac only pads the top and left. (I want it when fullscreen) – Matt Harrison Jul 30 '14 at 16:03
  • Here are two ideas: (1) create two additional windows, one above, and one below, and make the inactive mode-lines invisible (e.g., the text and background of the inactive mode-line can match the default buffer background color) with no head-lines; or, (2) create a minor mode that inserts an invisible spacer overlay at `window-start` and a certain number of lines before `window-end`. Here is a link to a thread that contains a minor mode to demonstrate calculating the *new* `window-start` and `window-end` before `redisplay` occurs: http://stackoverflow.com/a/24216247/2112489 – lawlist Jul 30 '14 at 18:24

3 Answers3

2

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) )
lawlist
  • 13,099
  • 3
  • 49
  • 158
1

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`.") )))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lawlist
  • 13,099
  • 3
  • 49
  • 158
1

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

TopSpace - Recenter line 1 with scrollable upper margin/padding

topspace

Trevor Pogue
  • 166
  • 2
  • 10