#+TITLE:  Emacs Graphical Display Configuration
#+AUTHOR: Howard X. Abrams
#+DATE:   2020-09-10
#+TAGS:   emacs macos

A literate programming file to configure the Emacs UI.

#+begin_src emacs-lisp :exports none
  ;;; ha-display --- Emacs UI configuration. -*- lexical-binding: t; -*-
  ;;
  ;; © 2020-2023 Howard X. Abrams
  ;;   Licensed under a Creative Commons Attribution 4.0 International License.
  ;;   See http://creativecommons.org/licenses/by/4.0/
  ;;
  ;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
  ;; Maintainer: Howard X. Abrams
  ;; Created: September 10, 2020
  ;;
  ;; This file is not part of GNU Emacs.
  ;;
  ;; *NB:* Do not edit this file. Instead, edit the original literate file at:
  ;;           ~/src/hamacs/ha-display.org
  ;;       Using `find-file-at-point', and tangle the file to recreate this one .
  ;;
  ;;; Code:
#+end_src

Let's turn off the menu and other settings:
#+begin_src emacs-lisp
  (when (display-graphic-p)
    (context-menu-mode 1)
    (tool-bar-mode -1)
    (scroll-bar-mode -1)
    (horizontal-scroll-bar-mode -1)
    (setq visible-bell 1
          frame-inhibit-implied-resize t))
#+end_src

And let’s make this Emacs look more like a fancy IDE with [[https://github.com/domtronn/all-the-icons.el][all-the-icons]]:

#+begin_src emacs-lisp
  (use-package all-the-icons
    :if (display-graphic-p)
    :config
    (setq major-mode-hydra-title-generator
          '(lambda (mode)
             (let ((title (thread-last mode
                                       (symbol-name)
                                       (string-replace "-" " ")
                                       (string-replace " mode" "")
                                       (s-titleize))))
               (s-concat ; (s-repeat 5 " ")
                (all-the-icons-icon-for-mode mode :v-adjust 0.05)
                " " title " Commands")))))
#+end_src

This also expands the [[file:ha-config.org::*Leader Sequences][Major Mode Hydra]] title sequence with a pretty icon.
* Mode Line
Let's install and load some of packages from the [[https://github.com/hlissner/doom-emacs][Doom Emacs]] project, like [[https://github.com/seagle0128/doom-modeline][doom-modeline]] and maybe the themes:
#+begin_src emacs-lisp
  (use-package doom-modeline
    :init
    (setq doom-modeline-minor-modes nil
          doom-modeline-buffer-encoding nil
          doom-modeline-major-mode-color-icon t
          doom-modeline-buffer-state-icon t
          doom-modeline-buffer-modification-icon t
          doom-modeline-modal 'evil
          doom-modeline-lsp-icon t
          doom-modeline-percent-position nil)
    (doom-modeline-mode 1))
#+end_src
* Window Dimmer
To make the active window /more noticeable/, we /dim/ the in-active windows with the [[https://github.com/gonewest818/dimmer.el][dimmer project]].

#+begin_src emacs-lisp
  (use-package dimmer
    :custom (dimmer-adjustment-mode :foreground))
#+end_src

I get issues with Magic and Dimmer, so let’s turn off this feature in certain windows:

#+begin_src emacs-lisp
   (use-package dimmer
     :config
     (dimmer-configure-which-key)    ; Do not dim these special windows
     (dimmer-configure-hydra)
     (dimmer-configure-magit)

     (dimmer-mode t))
#+end_src

As an interesting alternative, check out the [[https://www.emacs.dyerdwelling.family/emacs/20240208164549-emacs-selected-window-accent-mode-now-on-melpa/][selected-window-accent]] project.
* Find the Bloody Cursor
Large screen, lots of windows, so where is the cursor? While I used to use =hl-line+=, I found that the prolific [[https://protesilaos.com/][Protesilaos Stavrou]] [[https://protesilaos.com/codelog/2022-03-14-emacs-pulsar-demo/][introduced his Pulsar project]] is just what I need. Specifically, I might /loose the cursor/ and need to have it highlighted (using ~F8~), but also, this automatically highlights the cursor line with specific /actions/ , like changing windows.

#+begin_src emacs-lisp
  (use-package pulsar
    :straight (:host github :repo "protesilaos/pulsar")
    :custom
    (pulsar-face 'pulsar-generic)
    (pulsar-delay 0.15)

    :config
    (dolist (built-in-function '(recenter-top-bottom move-to-window-line-top-bottom reposition-window
                                 bookmark-jump other-window delete-window delete-other-windows
                                 forward-page backward-page scroll-up-command scroll-down-command
                                 ha-new-window tab-new tab-close tab-next org-next-visible-heading
                                 org-previous-visible-heading org-forward-heading-same-level
                                 org-backward-heading-same-level outline-backward-same-level
                                 outline-forward-same-level outline-next-visible-heading
                                 outline-previous-visible-heading outline-up-heading))
      (add-to-list 'pulsar-pulse-functions built-in-function))

    (when (fboundp 'winner-undo)
      (add-to-list 'pulsar-pulse-functions 'winner-undo)
      (add-to-list 'pulsar-pulse-functions 'winner-redo))

    (when (fboundp 'winum-select-window-1)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-1)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-2)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-3)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-4)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-5)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-6)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-7)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-8)
      (add-to-list 'pulsar-pulse-functions 'winum-select-window-9))

    (when (fboundp 'aw-delete-window)
      (add-to-list 'pulsar-pulse-functions 'aw-move-window)
      (add-to-list 'pulsar-pulse-functions 'aw-swap-window)
      (add-to-list 'pulsar-pulse-functions 'aw-copy-window)
      (add-to-list 'pulsar-pulse-functions 'aw-split-window-vert)
      (add-to-list 'pulsar-pulse-functions 'aw-split-window-horz)
      (add-to-list 'pulsar-pulse-functions 'aw-split-window-fair)
      (add-to-list 'pulsar-pulse-functions 'aw-delete-window))

    (when (fboundp 'evil-window-right)
      (add-to-list 'pulsar-pulse-functions 'evil-window-right)
      (add-to-list 'pulsar-pulse-functions 'evil-window-left)
      (add-to-list 'pulsar-pulse-functions 'evil-window-up)
      (add-to-list 'pulsar-pulse-functions 'evil-window-down))

    (pulsar-global-mode 1))
#+end_src

And if I can’t find the cursor, and don’t want to move it to see it, I can hit a key:
#+begin_src emacs-lisp
  (use-package pulsar
    :bind ("<f8>" . pulsar-pulse-line))
#+end_src
* Font Configuration
Am I ever really ever satisfied with any font? I regularly change my font based on the monospace du jour... [[http://blogs.adobe.com/typblography/2012/09/source-code-pro.html][Source Code Pro]] is attractive, and has been a staple on every programmers' screen. However, we all want ligatures, [[https://github.com/i-tu/Hasklig][Hasklig]] is a nice font that is thinner and easier to read than [[https://github.com/tonsky/FiraCode][Fira]], but [[https://typeof.net/Iosevka/][Iosevka]] seems to have it all. Oh, Microsoft just gave us [[https://docs.microsoft.com/en-us/windows/terminal/cascadia-code][Cascadia]] and that seems shiny. However, the [[https://github.com/ryanoasis/nerd-fonts][Nerd Font project]] adds the ligatures as well as all the other niceties to a font.

** Choosing a Font
I stole the following idea from [[https://protesilaos.com/dotemacs/#h:9035a1ed-e988-4731-89a5-0d9e302c3dea][Protesilaos Stavrou's dotfile configuration]], and the following should minimally be /readable/:
#+begin_example
  | Similarities | Regular                    |
  |--------------+----------------------------|
  | ()[]{}<>«»‹› | ABCDEFGHIJKLMNOPQRSTUVWXYZ |
  | 6bB8&        | abcdefghijklmnopqrstuvwxyz |
  | 0ODdoaoOQGC  | 0123456789                 |
  | I1tilIJL     | ~!@#$%^&*+                 |
  | !¡ij         | `'"‘’“”.,;:…               |
  | 5$§SsS5      | ()[]{}—-_=<>/\             |
  | 17ZzZ2       | ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ   |
  | 9gqpG6       | αβγδεζηθικλμνξοπρστυφχψω   |
  | hnmMN        |                            |
  | uvvwWuuwvy   |                            |
  | x×X          |                            |
  | .,·°%        |                            |
  | ¡!¿?         |                            |
  | :;           |                            |
  | `''"‘’“”     |                            |
  | —-~≈=≠+*_    |                            |
  | …⋯           |                            |
  | ...          |                            |

#+end_example

The following is from [[https://source-foundry.github.io/Hack/font-specimen.html][Hack's website]]:
#+begin_src c
//  The four boxing wizards jump
#include <stdio.h> // <= quickly.
int main(int argc, char **argv) {
  long il1[]={1-2/3.4,5+6==7/8};
  int OxFaced=0xBAD||"[{(CQUINE";
  unsigned O0,l1,Z2,S5,G6,B8__XY;
  printf("@$Hamburgefo%c`",'\n');
  return ~7&8^9?0:l1|!"j->k+=*w";
}
#+end_src

To install a font, I use the following command on my Mac:
#+begin_src sh
brew tap homebrew/cask-fonts
brew install --cask font-hack-nerd-font
#+end_src
** Specifying a Font
My /current/ favorite font is actually the top list of fonts that may be installed on my system:
#+begin_src emacs-lisp
  (defvar ha-fixed-font
    (when window-system
      (or
       (seq-first
        (seq-filter (lambda (font) (when (x-list-fonts font) font))
                    '("CaskaydiaCove Nerd Font"  ; finally found it
                      ;; funky font with litagures and a dotted 0
                      "Cascadia Code PL"
                      ;; clean font, but no litagures!?
                      "Hack Nerd Font"
                      "FiraCode Nerd Font"       ; has litagures
                      "Cousine Nerd Font"
                      "Iosevka Nerd Font"
                      "FantasqueSansMono Nerd Font"
                      "Monoid Nerd Font"
                      "Hasklig"
                      "Source Code Pro")))
       "monospaced"))
    "My fixed width font based on what I have installed.")
#+end_src

I probably don't need to have such a ranking system, as chances are good I have them all installed.
#+begin_src emacs-lisp
  (defvar ha-variable-font
    (when window-system
      (or
       (seq-first
        (seq-filter (lambda (font) (when (x-list-fonts font) font))
                    '("Atkinson Hyperlegible"
                      "SN Pro"   ; https://supernotes.app/open-source/sn-pro
                      "Literata" ; Clean, readable with litagures
                      ;; Next best can be downloaded here:
                      ;; https://fontesk.com/xcharter-typeface/
                      "XCharter"
                      "Charter"
                      ;;  Interesting idea: "Iosevka Comfy Motion Duo"
                      "Serif")))
       (warn "Cannot find a Serif Font.  Install Source Sans Pro."))))

  (defvar ha-variable-header-font
    (when window-system
      (or
       (seq-first
        (seq-filter (lambda (font) (when (x-list-fonts font) font))
                    '("SN Pro" "Overpass" "DejaVu Sans"
                      "Verdana" "Overpass"
                      "Source Sans Pro"
                      "Lucida Grande"
                      "Sans Serif")))
       (warn "Cannot find a Sans Serif Font.  Install Source Sans Pro."))))
#+end_src

Simple function that gives me the font information based on the size I need.  Recently updated after reading [[https://protesilaos.com/codelog/2020-09-05-emacs-note-mixed-font-heights/][this essay]], as I wanted my =fixed-pitch= to scale along with my =variable-pitch= font.

#+begin_src emacs-lisp
  (defun ha-set-favorite-font-size (size)
    "Set the default font size as well as equalize the fixed and variable fonts."
    (let ((fav-font (format "%s-%d" ha-fixed-font size)))
      (set-face-attribute 'default nil :font fav-font)
      (set-face-attribute 'fixed-pitch nil :family ha-fixed-font :inherit 'default :height 1.0)
      (set-face-attribute 'variable-pitch nil :family ha-variable-font :inherit 'default :height 1.2)))
#+end_src

Define /interactive/ functions to quickly adjusting the font size based on my computing scenario:

#+begin_src emacs-lisp
  (defun ha-mac-monitor-fontsize ()
    "Quickly set reset my font size when I connect my laptop to a monitor on a Mac."
    (interactive)
    (ha-set-favorite-font-size 13))

  (defun ha-linux-monitor-fontsize ()
    "Quickly set reset my font size when I connect my laptop to a monitor on Linux."
    (interactive)
    (ha-set-favorite-font-size 12))

  (defun ha-mac-laptop-fontsize ()
    "Quickly set reset my font size when I disconnect my laptop to a monitor from a Mac."
    (interactive)
    (ha-set-favorite-font-size 32))

  (defun ha-linux-laptop-fontsize ()
    "Quickly set reset my font size when I disconnect my laptop to a monitor from Linux."
    (interactive)
    (ha-set-favorite-font-size 10))

  (defun ha-imac-fontsize ()
    "Quickly set reset my font size when I am on my iMac."
    (interactive)
    (ha-set-favorite-font-size 16))
#+end_src

Which font to choose?

#+begin_src emacs-lisp
  (defun font-monitor-size-default ()
    "Set the default size according to my preference."
    (interactive)
    (cond
     ((eq system-type 'gnu/linux)         (ha-linux-monitor-fontsize))
     ((s-starts-with? "imac" system-name) (ha-imac-fontsize))
     (t                                   (ha-mac-monitor-fontsize))))

  (defun font-laptop-size-default ()
    "Set the default size according to my preference."
    (interactive)
    (if (eq system-type 'gnu/linux)
        (ha-linux-laptop-fontsize)
      (ha-mac-laptop-fontsize)))

  (font-monitor-size-default)
#+end_src
** Mixed Pitch
[[https://github.com/emacsmirror/mixed-pitch][Mixed pitch]] is a minor mode that enables mixing fixed-pitch (also known as fixed-width or monospace) and variable-pitch (AKA “proportional”) fonts. It tries to be smart about which fonts get which face.

#+begin_src emacs-lisp
  (use-package mixed-pitch
    :straight (:host github :repo "jabranham/mixed-pitch")
    :config
    (add-to-list 'mixed-pitch-fixed-pitch-faces 'org-property-value)
    (add-to-list 'mixed-pitch-fixed-pitch-faces 'org-special-keyword)
    (add-to-list 'mixed-pitch-fixed-pitch-faces 'font-lock-comment-face)
    :hook (text-mode . mixed-pitch-mode))
#+end_src


** Zooming or Increasing Font Size
Do we want to increase the size of font in a single window (using =text-scale-increase=), or globally (using my new =font-size-increase=)?

Increase or decrease the set size of the face:
#+begin_src emacs-lisp
  (defun font-size-adjust (delta)
    "Adjust the current frame's font size.
  DELTA would be something like 1 or -1."
    (interactive "nFont size difference: ")
    (when (null delta) (setq delta 1))

    (let* ((font-family (face-attribute 'default :font))
           (font-size   (font-get font-family :size))
           (new-size    (+ delta font-size)))
      (ha-set-favorite-font-size new-size)))

  (defun font-size-increase ()
     "Increase the `default' font size of all frames."
     (interactive)
     (font-size-adjust 1))

  (defun font-size-decrease ()
     "Decrease the `default' font size of all frames."
     (interactive)
     (font-size-adjust -1))
#+end_src
And some keybindings to call them:
#+begin_src emacs-lisp
  (global-set-key (kbd "s-+") 'font-size-increase)
  (global-set-key (kbd "s-=") 'font-size-increase)
  (global-set-key (kbd "s--") 'font-size-decrease)
#+end_src
* Themes
One does get used to a particular collection of colors. After happily using Steve Purcell’s port of the Tomorrow theme for years, I decided I needed a change, so I made [[file:ha-theme.org][made my own theme]].

Most of the time, Emacs is on my desk is a darkened room, so I choose the dark theme:

#+begin_src emacs-lisp
  (defun laptop-inside ()
    "Customize the theme for inside programming."
    (interactive)
    (load-theme 'hamacs)
    (ha-word-processor-fonts))
#+end_src

But, when feeling adventurous, I /sometimes/ take my laptop outside:

#+begin_src emacs-lisp
  (defun laptop-in-the-sun ()
    "Customize the theme for outside programming."
    (interactive)

    (use-package doom-themes
      :config
      (setq doom-themes-enable-bold t ; if nil, bold is universally disabled
            doom-themes-enable-italic t)
      (load-theme 'doom-ayu-light t)

      ;; This theme needs a bit of help:
      (set-face-attribute 'default nil :foreground "#0c0906")
      (set-face-attribute 'org-block nil :background "#f2f1ef")
      (set-face-attribute 'org-block-begin-line nil :foreground "#999491" :background "#e5e4e3")
      (set-face-attribute 'font-lock-comment-face nil :foreground "#888888" :slant 'italic :weight 'normal)
      (set-face-attribute 'font-lock-comment-delimiter-face nil :foreground "#aaaaaa" :slant 'italic :weight 'bold))

    (ha-word-processor-fonts))
#+end_src

I’ve been playing around with making the current window more pronounced.
This isn’t needed as much with the [[*Window Dimmer][Window Dimmer]] feature, but if I do, this would be the settings:

Oh, and turn off the line highlighting:

#+begin_src emacs-lisp
  (global-hl-line-mode -1)
#+end_src

And of course, the default is /inside/ where my mind is dark and safe:

#+begin_src emacs-lisp
  (add-to-list 'custom-theme-load-path hamacs-source-dir)
  (laptop-inside)
#+end_src
** Highlight Task Labels
In code, if you drop a specific /text/ labels, we can highlight them with [[https://github.com/tarsius/hl-todo][hl-todo package]]:

#+begin_src emacs-lisp
  (use-package hl-todo
    :straight (:host github :repo "tarsius/hl-todo")
    :init
    (setq hl-todo-keyword-faces
      `(("TODO"   . ,(face-foreground 'warning))
        ("FIXME"  . ,(face-foreground 'error))
        ("NOTE"   . ,(face-foreground 'success))))
    (global-hl-todo-mode 1))
#+end_src

This package visually standout comments like:
TODO Fix bug where highlight isn’t loading

Suggests to bind some keys to =hl-todo-next= in order to jump from tag to tag, but the [[https://github.com/liuyinz/consult-todo][consult-todo]] implements that in a more visual way:

#+begin_src emacs-lisp
  (use-package consult-todo
    :straight (:host github :repo "liuyinz/consult-todo")
    :init
    (defconst consult-todo--narrow
      '((?t . "TODO")
        (?f . "FIXME")
        (?n . "NOTE"))
      "Mapping of narrow and keywords.")
    :general (:states 'normal "g t" '("jump todos" . consult-todo)))
#+end_src
* Full Size Frame
Taken from [[https://emacsredux.com/blog/2020/12/04/maximize-the-emacs-frame-on-startup/][this essay]], I figured I would start the initial frame automatically in fullscreen, but not any subsequent frames (as this could be part of the capturing system).
#+begin_src emacs-lisp
  (add-to-list 'initial-frame-alist '(fullscreen . maximized))
#+end_src

But when capturing, I subsequently open smaller frames that shouldn’t be /odd looking/:
#+begin_src emacs-lisp
  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
  (add-to-list 'default-frame-alist '(ns-appearance . dark))
#+end_src

Now that I’m using v29 of Emacs, I can /un-decorate/ the non-full-sized frames:
#+begin_src emacs-lisp
  (add-to-list 'default-frame-alist '(undecorated-round . t))
#+end_src
* Emojis, Icons and Whatnot
Display these two symbols as one:
#+begin_src emacs-lisp
  (add-hook 'text-mode-hook (lambda ()
                              (dolist (pair '(("!?" . "‽")
                                              ("ae" . "æ")
                                              ("AE" . "Æ")

                                              ;; If we have ligatures, why these?
                                              ;; ("->" . ?→)
                                              ;; ("<-" . ?←)
                                              ;; ("=>" . ?⇒)
                                              ))
                                (push pair prettify-symbols-alist))))
#+end_src

And turn the prettifier on:

#+begin_src emacs-lisp
  (global-prettify-symbols-mode 1)
#+end_src

Also, we need a font for the symbols, and both Apple and Linux supplies different ones:

#+BEGIN_SRC emacs-lisp
  (set-fontset-font t 'symbol
   (cond
    ((ha-running-on-macos?)
     (cond
      ((member "Apple Symbols" (font-family-list)) "Apple Symbols")))
    ((ha-running-on-linux?)
     (cond
      ((member "Symbola" (font-family-list)) "Symbola")))))

#+END_SRC

In Emacs 28.1, we have better Unicode 14 support. Which means, we need to install either [[https://fonts.google.com/noto/specimen/Noto+Emoji][Noto Emoji]] or [[https://github.com/googlefonts/noto-emoji][Noto Color Emoji]]. Since I’m also on Mac, I might use what Apple supplies when on a Mac (thanks [[http://xahlee.info/emacs/emacs/emacs_list_and_set_font.html][Xah Lee]]):

#+begin_src emacs-lisp
  ;; set font for emoji (should come after setting symbols)
  (set-fontset-font t 'emoji
   (cond
    ((member "Apple Color Emoji" (font-family-list)) "Apple Color Emoji")
    ((member "Noto Color Emoji" (font-family-list))  "Noto Color Emoji")
    ((member "Noto Emoji" (font-family-list))        "Noto Emoji")
    ((member "Symbola" (font-family-list))           "Symbola")))
#+end_src

Test this out: 😄 😱 😸 👸 👽 🙋

Not use what I'm doing with the [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] package, but the Doom Modeline uses much of this.

#+begin_src emacs-lisp
  (use-package all-the-icons)
#+end_src

*Note:* Install everything with the function, =all-the-icons-install-fonts=.
* Ligatures
Seems like getting ligatures to work in Emacs has been a Holy Grail. On Mac, I've used special builds that have hacks, but now with Emacs 27 and Harfbuzz, I should be able to get --> to look like it should.

#+begin_src emacs-lisp :tangle no
  (setq prettify-symbols-unprettify-at-point 'right-edge)

  (global-prettify-symbols-mode +1)
  (prettify-symbols-mode +1)
#+end_src

We'll start using that instead, but setting this [[file:ha-programming.org::*Ligatures][over here]] in the programming section.

Also note that adding a /little/ extra space between lines makes text files easier to read.
#+begin_src emacs-lisp
  (add-hook 'text-mode-hook (lambda () (setq-local line-spacing 0.1)))
#+end_src
* Technical Artifacts :noexport:

Let's =provide= a name so we can =require= this file:
#+begin_src emacs-lisp :exports none
  (provide 'ha-display)
  ;;; ha-display.el ends here
#+end_src

Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~

#+description: A literate programming file to configure the Emacs UI.

#+property:    header-args:sh :tangle no
#+property:    header-args:emacs-lisp :tangle yes
#+property:    header-args    :results none :eval no-export :comments no :mkdirp yes

#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
