Skip to content

Latest commit

 

History

History
719 lines (553 loc) · 20.1 KB

wal-visuals.org

File metadata and controls

719 lines (553 loc) · 20.1 KB

Visuals

#+AUTHOR @Walheimat

I like nice-looking things.

Header

;;; wal-visuals.el --- Visuals. -*- lexical-binding: t -*-

;;; Commentary:
;;
;; Provide visual packages.

;;; Code:

(eval-when-compile
  (require 'wal-useful nil t)
  (require 'wal-key-bindings nil t))

(declare-function dashboard-insert-startupify-lists "ext:dashboard.el")
(declare-function ligature-set-ligatures "ext:ligature.el")
(declare-function visual-fill-column-mode "ext:visual-fill-column.el")
(declare-function wal-biased-random "wal-useful.el")
(declare-function wal-modern-emacs-p "wal-useful.el")

(defvar dashboard-footer-messages)
(defvar wal-ascii-cachalot-whale)
(defvar wal-default-path)

(defgroup wal-visuals nil
  "Change settings used for visual packages."
  :group 'wal
  :tag "Visuals")

;;;; Customization:

(defcustom wal-transparency 90
  "The default frame transparency."
  :type 'integer
  :group 'wal-visuals)

(defcustom wal-theme nil
  "The theme."
  :type '(choice symbol (const nil))
  :group 'wal-visuals)

(defcustom wal-hidpi nil
  "Whether the display is considered HiDPI."
  :type 'boolean
  :group 'wal-visuals)

(defcustom wal-fixed-fonts
  '("JetBrains Mono"
    "Iosevka"
    "Fira Code"
    "mononoki"
    "Input Mono"
    "Source Code Pro"
    "DejaVu Sans Mono")
  "Fixed fonts ordered by preference."
  :type '(repeat string)
  :group 'wal-visuals)

(defcustom wal-variable-fonts
  '("DeJa Vu Sans"
    "Liberation Serif"
    "Ubuntu")
  "Variable fonts ordered by preference."
  :type '(repeat string)
  :group 'wal-visuals)

(defcustom wal-preferred-fonts nil
  "List of (fixed and variable width) font names that should be preferred."
  :type '(choice (repeat string) (const nil))
  :group 'wal-visuals)

(defcustom wal-fixed-font-height 120
  "The font height for fixed fonts.
The default value is 98."
  :type 'integer
  :group 'wal-visuals)

(defcustom wal-variable-font-height 140
  "The font height for variable fonts.
This has no default value."
  :type 'integer
  :group 'wal-visuals)

Fonts

This code sets up fonts. Fonts are set by matching preferred fonts against available ones. I have found that JetBrains Mono has the best support for ligatures.

There’s commands to set fonts and their height. Comments are globally italicized and keywords made bold.

(defvar wal-fonts-updated-hook nil
  "Functions to run when fonts were updated.")

(defun wal-font-update (attribute value faces &optional arg)
  "Set ATTRIBUTE to VALUE for FACES.

This returns the made updates. Affects all frames unless ARG is
t."
  (let ((frame (when arg (selected-frame))))

    (mapc (lambda (it)
            (when (internal-lisp-face-p it)
              (set-face-attribute it frame attribute value)))
          faces)

    (run-hooks 'wal-fonts-updated-hook)))

(defun wal-read-sensible-font-height (type)
  "Read a sensible font height for TYPE."
  (let* ((current (face-attribute type :height))
         (num (read-number (format "Set %s font (currently: %s): " type current))))

    (max (min num 300) 80)))

(defun wal-available-fonts (fonts)
  "Filter FONTS down to available fonts."
  (seq-filter (lambda (it) (find-font (font-spec :name it))) fonts))

(defun wal-read-font (type)
  "Read a font for TYPE."
  (let* ((name (intern (format "%s-pitch" type)))
         (prev (face-attribute name :family))
         (fonts (symbol-value (intern (format "wal-%s-fonts" type))))
         (font (completing-read (format "Select %s font (current: %s) " type prev) (wal-available-fonts fonts))))

    font))

(defun wal-select-fixed-font (font)
  "Select fixed (available) FONT."
  (interactive (list (wal-read-font 'fixed)))

  (wal-font-update :font font '(default fixed-pitch)))

(defun wal-select-variable-font (font)
  "Select variable (available) FONT."
  (interactive (list (wal-read-font 'variable)))

  (wal-font-update :font font '(variable-pitch)))

(defun wal-set-fixed-font-height (height &optional arg)
  "Set the HEIGHT for fixed fonts.

Affects all frames unless ARG is t."
  (interactive (list (wal-read-sensible-font-height 'default) current-prefix-arg))

  (setq wal-fixed-font-height height)

  (wal-font-update :height height '(default fixed-pitch) arg))

(defun wal-set-variable-font-height (height &optional arg)
  "Set the HEIGHT for variable fonts.

Affects all frames unless ARG is t."
  (interactive (list (wal-read-sensible-font-height 'variable-pitch) current-prefix-arg))

  (setq wal-variable-font-height height)

  (wal-font-update :height height '(variable-pitch) arg))

(defun wal-preferred-fonts (fonts)
  "Filter FONTS down to preferred fonts."
  (seq-filter (lambda (it) (member it wal-preferred-fonts)) fonts))

(defun wal-fonts-candidate (fonts &optional prefer)
  "Return the first available font from a list of FONTS.
If PREFER is true, variable `wal-preferred-fonts' is not nil and
preferred fonts are available, return the first of those
instead."
  (let* ((available-fonts (wal-available-fonts fonts))
         (preferred (and prefer (wal-preferred-fonts available-fonts))))

    (if preferred
        (car preferred)
      (car available-fonts))))

;; Slanted and enchanted.
(defun wal-font-lock ()
  "Set comment face to italic and keyword face to bold."
  (set-face-attribute 'font-lock-comment-face nil :slant 'italic :weight 'normal)
  (set-face-attribute 'font-lock-keyword-face nil :weight 'bold))

ligature

This sets up char-tables so that ligatures work (for fonts that support it). The ligatures are set up per-language using harpoon, see Languages. That package also provides common ligatures to all languages.

(use-package ligature
  :hook ((prog-mode harpoon-prog-like) . ligature-mode))

mixed-pitch

Allow using variable and fixed fonts in the same buffer. Makes sure that code in markdown-mode buffers uses fixed pitch.

(use-package mixed-pitch
  :config
  (add-to-list 'mixed-pitch-fixed-pitch-faces 'markdown-pre)

  :custom
  (mixed-pitch-variable-pitch-cursor nil)

  :delight " mpm"

  :defines (mixed-pitch-fixed-pitch-faces))

Themes

PeachMelpa has more themes.

(defvar wal-active-theme nil)

(defvar wal-theme-hook nil)

(defun wal-load-active-theme ()
  "Load the currently active theme."
  (interactive)

  (when wal-active-theme
    (condition-case err
        (progn
          (load-theme wal-active-theme t)
          (run-hooks 'wal-theme-hook))
      (error
       (message "Failed to load theme: %s" (error-message-string err))))))

doom-themes

The nicest theme packages out there. It supports many, many packages’ face variants.

(use-package doom-themes
  :config
  (doom-themes-org-config)

  :functions (doom-themes-org-config))

kaolin-themes

Another collection of beautiful themes, but it doesn’t color every face.

(use-package kaolin-themes
  :config
  (kaolin-treemacs-theme)

  :custom
  (kaolin-themes-italic-comments t)
  (kaolin-themes-git-gutter-solid t)
  (kaolin-themes-modeline-border nil)
  (kaolin-themes-distinct-fringe t)
  (kaolin-themes-org-scale-headings nil)

  :functions (kaolin-treemacs-theme))

modus-themes

Protesilaos’ super configurable themes.

(use-package modus-themes
  :custom
  (modus-themes-slanted-constructs t)
  (modus-themes-bold-constructs t)
  (modus-themes-mode-line '(borderless))
  (modus-themes-org-blocks 'tinted-background))

ef-themes

Color over configuration from the same author.

(use-package ef-themes)

base16-themes

Check out the gallery.

(use-package base16-theme
  :custom
  (base16-theme-distinct-fringe-background nil))

Guides

hl-todo

Highlight TODO, FIXME etc. in programming modes but only if they are followed by a colon.

(use-package hl-todo
  :hook ((prog-mode harpoon-prog-like) . hl-todo-mode)

  :custom
  (hl-todo-highlight-punctuation ":")
  (hl-todo-require-punctuation t))

rainbow-delimiters

Make delimiters in programming modes stand out colorfully.

(use-package rainbow-delimiters
  :hook ((prog-mode harpoon-prog-like) . rainbow-delimiters-mode))

rainbow-mode

Show colors colorfully. This sets the background of color names and hex codes to the respective color. Mostly useful when editing themes or editing CSS files.

(use-package rainbow-mode
  :delight " rbm")

visual-fill-column

Break lines visually at fill column. This allows the ergonomics of using fill-paragraph without physically breaking lines.

(defun wal-visual-fill-column-mode ()
  "Turn `visual-fill-column-mode' on or off."
  (if visual-line-mode
      (visual-fill-column-mode +1)
    (visual-fill-column-mode -1)))

(use-package visual-fill-column
  :hook (visual-line-mode . wal-visual-fill-column-mode)

  :custom
  (visual-fill-column-enable-sensible-window-split t)

  :delight " vfc")

Modeline

Minor modes are white-listed, hidden and delighted. Meaning that, if not white-listed, they are not shown, if they are shown, they are delighted.

delight

Allows renaming major and minor modes. For external packages this is done in their respective configuration.

(use-package delight
  :config
  (delight 'dired-mode "Dired" :major)
  (delight 'emacs-lisp-mode "Elisp" :major)
  (delight 'lisp-interaction-mode "Elisp?" :major)
  (delight 'wdired-mode "DirEd" :major)
  (delight 'c++-mode "CPP" :major)
  (delight 'compilation-shell-minor-mode " csh" "compile")
  (delight 'auto-fill-function " aff" t)
  (delight 'visual-line-mode " vis" t)

  :functions (delight))

minions

Sometimes the list of minor modes overcrowds the modeline. This minifies all but those you want to be visible and provides a menu on the mode-line to enable and disable them.

(use-package minions
  :defer 3

  :config
  (minions-mode 1)

  :custom
  (minions-prominent-modes '(auto-fill-function
                             flycheck-mode
                             flymake-mode
                             flyspell-mode
                             git-timemachine-mode
                             multiple-cursors-mode
                             org-tree-slide-mode
                             partial-recall-mode
                             pet-mode
                             prettier-mode
                             puni-mode
                             ship-mate-mode
                             typo-mode
                             verb-mode
                             verb-response-body-mode
                             visual-line-mode
                             wal-config-mode
                             with-editor-mode
                             smerge-mode))

  :functions (minions-mode))

Dashboard

dashboard

Let’s have a dash of board. This is what you see when starting up Emacs or creating a new frame. It shows recent files, projects and bookmarks as well as the current version of the configuration and an inspirational (self-deprecating) message.

Makes sure that the bookmarks file as well as the org-roam and Org tasks folders are ignored in the recent files list.

(defun wal-with-recent-files-excluded (fun &rest args)
  "Advise FUN to ignore certain directories, applying ARGS."
  (defvar recentf-exclude)

  (let ((recentf-exclude '("bookmarks\\'" "zettelkasten" "org/tasks")))

    (apply fun args)))

(defun wal-instead-show-biased-random (&rest _args)
  "Advise to use biased random footer message."
  (nth (wal-biased-random (length dashboard-footer-messages)) dashboard-footer-messages))

(defun wal-in-case-of-daemonp-add-different-hook ()
  "Setup the dashboard in a daemon-friendly way."
  (require 'all-the-icons nil t)
  (when (daemonp)
    (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))

    (add-hook
     'server-after-make-frame-hook
     #'dashboard-insert-startupify-lists)))

(defun wal-dashboard-get-buffer ()
  "Get the a refreshed dashboard buffer."
  (defvar dashboard-buffer-name)
  (defvar dashboard-force-refresh)

  (let ((dashboard-force-refresh t))

    (dashboard-insert-startupify-lists)
    (get-buffer dashboard-buffer-name)))

(defun wal-instead-use-custom-banner (&rest _args)
  "Choose the correct banner.

Try to use the local banners and only if that fails call the
original FUN."
  (declare-function dashboard--image-supported-p "ext:dashboard.el")

  (let ((png (expand-file-name "assets/logo.png" wal-default-path))
        (ascii (expand-file-name "assets/logo.txt" wal-default-path)))

    (if (dashboard--image-supported-p png)
        (list :image png :text ascii)
      (list :text ascii))))

(use-package dashboard
  :hook (after-init . dashboard-setup-startup-hook)

  :init
  (advice-add
   'dashboard-insert-startupify-lists :around
   #'wal-with-recent-files-excluded)
  (advice-add
   'dashboard-random-footer :override
   #'wal-instead-show-biased-random)
  (advice-add
   'dashboard-setup-startup-hook :before-until
   #'wal-in-case-of-daemonp-add-different-hook)
  (advice-add
   'dashboard-choose-banner :override
   #'wal-instead-use-custom-banner)

  :config
  (setq dashboard-banner-logo-title (wal-describe-config-version))

  ;; TEMP: Prevent empty dashboard for default sessions.
  (unless (daemonp)
    (dashboard-refresh-buffer))

  :custom
  (dashboard-items '((recents . 5)
                     (projects . 3)
                     (bookmarks . 3)))
  (dashboard-projects-backend 'project-el)

  (dashboard-image-banner-max-height (if wal-hidpi 0 300))

  (dashboard-footer-icon "🐋")
  (dashboard-footer-messages '("breaching your favorite stupid framework"
                               "I propel myself forward on nothing but flukes"
                               "devout and up the spout"
                               "krill, filter feeders and hit sulphur bottom"
                               "the founder of retiring gentlemen"
                               "the loud keyboard shall vanquish the muscular mouse"
                               "answering all C-calls in sweeping, overflowing song"
                               "infinite whale loop"
                               "from echo location to perimeter expansion"
                               "a mystic of profounder divings"
                               "superior, clear and fine code; but there's little of it"
                               "formed by intertwisting, slanting folds"
                               "a register for distant jets"
                               "the most majestic in affect, the most valuable in commiseration"
                               "unshared, sourceless immensities"))

  (dashboard-week-agenda nil)
  (dashboard-agenda-time-string-format "%d/%m/%y")
  (dashboard-agenda-release-buffers t)

  (dashboard-center-content t)
  (dashboard-set-file-icons t)
  (dashboard-set-navigator t)
  (dashboard-path-style 'truncate-beginning)

  :general
  (ambassador "0" '(dashboard-refresh-buffer :wk "dashboard")))

Icons

all-the-icons

Provides icons for buffers. You need to install the icons yourself[fn:1].

(use-package all-the-icons)

Transparency

Sets up transparency (by alpha-background or alpha if the prior isn’t available). You can change this per-frame through wal-set-transparency.

(defun wal-transparency--param ()
  "Get the transparency parameter for this Emacs version."
  (if (wal-modern-emacs-p 29)
      'alpha-background
    'alpha))

(defun wal-set-transparency (&optional value)
  "Set the transparency of the frame to VALUE.

1 being (almost) completely transparent, 100 being opaque.

This also updates variable `wal-transparency' during the session."
  (interactive
   (list
    (read-number (format "Set transparency (currently %s%%): " wal-transparency))))

  (let ((transparency (min (max (or value wal-transparency) 1) 100))
        (param (wal-transparency--param)))

    (setq wal-transparency transparency)

    (modify-all-frames-parameters `((,param . ,transparency)))))

Setups

This sets up transparency and fonts reliably for new start-ups and frame creation. See useful for how this is done.

(wal-define-init-setup visuals
  "Set up visual frills like theme and transparency."
  :initial
  ((add-to-list 'default-frame-alist `(,(wal-transparency--param) . ,wal-transparency))

   ;; Mix of old and new.
   (setq frame-title-format '(multiple-frames "%b" ("%b@" system-name)))

   ;; Some themes require configuration, so we only load after initialization.
   (when wal-theme
     (setq wal-active-theme wal-theme)
     (wal-load-active-theme))

   (when wal-hidpi
     (set-fringe-mode 18)))
  :always
  ((wal-set-transparency)
   (wal-load-active-theme))
  :immediately t)

(wal-define-init-setup fonts
  "Set up fonts for GUI Emacs.

This sets `default' and `fixed-pitch' fonts to the first
available candidate from `wal-fixed-fonts'. Does the same for
`variable-pitch' using `wal-variable-fonts'."
  :initial
  ((when (or (daemonp) (display-graphic-p))
     (mapc (lambda (it)
             (when (internal-lisp-face-p it)
               (set-face-attribute it nil
                                   :font (wal-fonts-candidate wal-fixed-fonts t)
                                   :height wal-fixed-font-height)))
           '(default fixed-pitch))
     (mapc (lambda (it)
             (when (internal-lisp-face-p it)
               (set-face-attribute it nil :inherit 'mode-line)))
           '(mode-line-active mode-line-inactive))

     ;; Variable pitch face.
     (set-face-attribute 'variable-pitch nil
                         :font (wal-fonts-candidate wal-variable-fonts t)
                         :height wal-variable-font-height))

   (administrator
     "sf" 'wal-set-fixed-font-height
     "sv" 'wal-set-variable-font-height
     "sF" 'wal-select-fixed-font
     "sV" 'wal-select-variable-font)

   (add-hook 'font-lock-mode-hook #'wal-font-lock))
  :always
  ((run-hooks 'wal-fonts-updated-hook)))

Footer

(provide 'wal-visuals)

;;; wal-visuals.el ends here

Footnotes

[fn:1] The all-the-icons icons need to be downloaded manually by running M-x all-the-icons-install-fonts and selecting yes.

If the installation process should fail for any reason, close Emacs and re-run it.