summaryrefslogtreecommitdiff
path: root/modules/ui-theme.el
blob: 8d83ac50f8adf15d7e8afab99c6cff6db24aacfb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
;;; ui-theme.el ---  UI Theme Configuration and Persistence -*- lexical-binding: t; -*-
;; Craig Jennings <c@cjennings.net>
;;
;;; Commentary:

;; Add custom theme files to "themes" subdirectory.
;; Load other preferred themes via use-package.
;; cj/switch-theme function unloads previous themes then applies the chosen theme.
;; Persist themes between sessions using a file to store the theme name.
;; The persist file can live in a sync'd dir, so theme choice may persist across machines.

;;; Code:

;; ----------------------------------- Themes ----------------------------------
;; theme choices and settings

;; downloaded custom themes go in themes subdirectory
(setq custom-safe-themes t)  ;; trust all custom themes
(add-to-list 'custom-theme-load-path
			 (concat user-emacs-directory "themes"))

(use-package ef-themes)
(use-package github-dark-vscode-theme)
(use-package madhat2r-theme)
(use-package adwaita-dark-theme)

;; ------------------------------- Switch Themes -------------------------------
;; loads themes in completing read, then persists via the functions below

(defun cj/switch-themes ()
  "Function to switch themes and save chosen theme name for persistence.
Unloads any other applied themes before applying the chosen theme."
  (interactive)
  (let ((chosentheme ""))
    (setq chosentheme
		  (completing-read "Load custom theme: "
						   (mapcar 'symbol-name
								   (custom-available-themes))))
    (mapcar #'disable-theme custom-enabled-themes)
    (load-theme (intern chosentheme) t))
  (cj/save-theme-to-file))
(global-set-key (kbd "M-L") 'cj/switch-themes)

;; ----------------------------- Theme Persistence -----------------------------
;; persistence utility functions used by switch themes.

(defvar theme-file (concat sync-dir "emacs-theme.persist")
  "The location of the file to persist the theme name.
If you want your theme change to persist across instances, put this in a
directory that is sync'd across machines with this configuration.")

(defvar fallback-theme-name "modus-vivendi"
  "The name of the theme to fallback on.
This is used then there's no file, or the theme name doesn't match
any of the installed themes. This should be a built-in theme. If theme name is
'nil', there will be no theme.")

(defun cj/read-file-contents (filename)
  "Read FILENAME and return its content as a string.
If FILENAME isn't readable, return nil."
  (if (file-readable-p filename)
      (with-temp-buffer
		(insert-file-contents filename)
		(buffer-string))
	nil))

(defun cj/write-file-contents (content filename)
  "Write CONTENT to FILENAME.
If FILENAME isn't writeable, return nil. If successful, return t."
  (if (file-writable-p filename)
      (progn
		(with-temp-buffer
		  (insert content)
		  (write-file filename))
		t)
	nil))

(defun cj/get-active-theme-name ()
  "Return the name of the active UI theme as a string."
  (symbol-name (car custom-enabled-themes)))

(defun cj/save-theme-to-file ()
  "Save the string representing the current theme to the theme-file."
  (if (equal (cj/write-file-contents (cj/get-active-theme-name) theme-file) nil)
      (message "Cannot save theme: %s is unwriteable" theme-file)
    (message "%s theme saved to %s" (cj/get-active-theme-name) theme-file)))

(defun cj/load-fallback-theme (msg)
  "Display MSG and load ui-theme fallback-theme-name.
Used to handle errors with loading persisted theme."
  (message (concat msg (format " Loading fallback theme %s" fallback-theme-name)))
  (load-theme (intern fallback-theme-name) t))

(defun cj/load-theme-from-file ()
  "Apply the thame name contained in theme-file as the active UI theme.
If the theme is nil, it disables all current themes. If an error occurs
loading the file name, the fallback-theme-name is applied and saved."
  (let ((theme-name (cj/read-file-contents theme-file)))
	;; if theme-name is nil, unload all themes and load fallback theme
	(if (or (string= theme-name "nil") (not theme-name))
		(progn
		  (mapcar #'disable-theme custom-enabled-themes)
		  (cj/load-fallback-theme "Theme file not found or theme name in it is nil."))
	  ;; apply theme name or if error, load fallback theme
	  (condition-case err
		  (load-theme (intern theme-name) t)
		(error
		 (cj/load-fallback-theme (concat "Error loading " theme-name
										 ".")))))))

(cj/load-theme-from-file)

(provide 'ui-theme)
;;; ui-theme.el ends here.