protesilaos / modus-themes

Highly accessible themes for GNU Emacs, conforming with the highest standard for colour contrast between background and foreground values (WCAG AAA).
https://protesilaos.com/emacs/modus-themes
GNU General Public License v3.0
584 stars 31 forks source link

Recommended evil cursor colors #125

Open Luis-Henriquez-Perez opened 1 week ago

Luis-Henriquez-Perez commented 1 week ago

I am for the past week I have been working on ricing my modeline. The journey was long. I went from your youtube video to telephone-line then to powerline briefly tried doom-modeline, finally settling with spaceline. On the way I noticed a cool commonality among these modelines--the display of the current evil-state, each with its own color. Inevitably this color-coting drove me to want to the emacs cursor based on the current evil state (normal, visual, operator...). At first I started by using static faces for all themes, but of course one set of static faces cannot blend in seamlessly with the diverse color schemes of different themes. I wanted the cursor's evil state color to change accordingly to match the style of the particular theme I was using. Not keen on going through each of my themes and determining the proper faces for each of the states, I wondered whether there was some common face that is frequently configured. When I had been using telephone-line I used the faces it provided for evil states (telephone-line-evil-normal, telephone-line-evil-visual, etc.) and there I had relative success with doom-themes which defined faces for telephone-line evil state faces. I am having similar success with the spaceline-evil-STATE faces I am currently using although annoyingly there is not spaceline-evil-operator state. Alternatively, considered using the org-level faces for themes that do not support telephone-line or spaceline evil faces because they are present in virtually all themes.

In any case the following code is what I have right now. Since I am using spaceline and not telephone-line anymore the cursors will match the spaceline-evil faces. I've forked spaceline and added spaceline-evil-operator face with a background of MediumPurple3 (a color recommended by chatgpt). This isnt a perfect solution because no theme actually supports this new face.

;;;; change cursor color and shape according to current evil state
;; Did not realize for the longest time that evil cursor can be a function that
;; changes the cursor.  With this in mind, the best way to set the cursor size
;; and shape dynamically is to set the corresponding cursor symbols to functions.
(defun +evil--cursor-color (state)
  "Return the cursor color for state as a string."
  (cond ((bound-and-true-p telephone-line-mode)
         (face-attribute (intern (format "telephone-line-evil-%s" state)) :background nil t))
        ((facep (intern (format "spaceline-evil-%s" state)))
         (face-attribute (intern (format "spaceline-evil-%s" state)) :background nil t))))

(defun +evil-normal-state-cursor ()
  "Set cursor for normal state."
  (evil-set-cursor (list t (+evil--cursor-color 'normal))))

(defun +evil-insert-state-cursor ()
  "Set cursor for insert state."
  (evil-set-cursor (list '(bar . 2) (+evil--cursor-color 'insert))))

(defun +evil-visual-state-cursor ()
  "Set cursor for visual state."
  (evil-set-cursor (list t (+evil--cursor-color 'visual))))

(defun +evil-motion-state-cursor ()
  "Set cursor for motion state."
  (evil-set-cursor (list t (+evil--cursor-color 'motion))))

(defun +evil-replace-state-cursor ()
  "Set cursor for replace state."
  (evil-set-cursor (list t (+evil--cursor-color 'replace))))

(defun +evil-operator-state-cursor ()
  "Set cursor for operator state."
  (evil-set-cursor (list '(hbar . 9) (+evil--cursor-color 'operator))))

(defun +evil-emacs-state-cursor ()
  "Set cursor for emacs state."
  (evil-set-cursor (list t (+evil--cursor-color 'emacs))))

(opt! evil-normal-state-cursor   #'+evil-normal-state-cursor)
(opt! evil-insert-state-cursor   #'+evil-insert-state-cursor)
(opt! evil-visual-state-cursor   #'+evil-visual-state-cursor)
(opt! evil-motion-state-cursor   #'+evil-motion-state-cursor)
(opt! evil-replace-state-cursor  #'+evil-replace-state-cursor)
(opt! evil-operator-state-cursor #'+evil-operator-state-cursor)
(opt! evil-emacs-state-cursor    #'+evil-emacs-state-cursor)

Anyway I wanted to ask your thoughts on how I could go dynamical evil cursor changes which respect the current them. Specifically in regards to the modus themes I was going to ask which faces you recommended for the evil states. But while writing this post I noticed on the modus-operandi changelog that faces for telephone-line and spaceline were dropped. I am thinking of looking for the commit where the support wasnt dropped yet and adding the values of the faces--would you recommend this? Amusingly I asked chatgpt to generate possible values based on the list of modus-operandi faces and it came up with these colors:

(custom-theme-set-faces
 'modus-operandi
 ;; Evil state faces
 '(spaceline-evil-normal ((t (:background "#005bbb" :foreground "#ffffff"))))
 '(spaceline-evil-insert ((t (:background "#007400" :foreground "#ffffff"))))
 '(spaceline-evil-visual ((t (:background "#aa2200" :foreground "#ffffff"))))
 '(spaceline-evil-replace ((t (:background "#aa2200" :foreground "#ffffff"))))
 '(spaceline-evil-motion ((t (:background "#783c00" :foreground "#ffffff"))))
 '(spaceline-evil-operator ((t (:background "#783c00" :foreground "#ffffff"))))
 '(spaceline-evil-emacs ((t (:background "#4f0090" :foreground "#ffffff")))))
protesilaos commented 4 days ago

From: Luis @.***> Date: Sat, 23 Nov 2024 03:38:33 -0800

[... 58 lines elided]

Anyway I wanted to ask your thoughts on how I could go dynamical evil cursor changes which respect the current them. Specifically in regards to the modus themes I was going to ask which faces you recommended for the evil states. But while writing this post I noticed on the modus-operandi changelog that faces for telephone-line and spaceline were dropped. I am thinking of looking for the commit where the support wasnt dropped yet and adding the values of the faces--would you recommend this? Amusingly I asked chatgpt to generate possible values based on the list of modus-operandi faces and it came up with these colors:

(custom-theme-set-faces
 'modus-operandi
 ;; Evil state faces
 '(spaceline-evil-normal ((t (:background "#005bbb" :foreground "#ffffff"))))
 '(spaceline-evil-insert ((t (:background "#007400" :foreground "#ffffff"))))
 '(spaceline-evil-visual ((t (:background "#aa2200" :foreground "#ffffff"))))
 '(spaceline-evil-replace ((t (:background "#aa2200" :foreground "#ffffff"))))
 '(spaceline-evil-motion ((t (:background "#783c00" :foreground "#ffffff"))))
 '(spaceline-evil-operator ((t (:background "#783c00" :foreground "#ffffff"))))
 '(spaceline-evil-emacs ((t (:background "#4f0090" :foreground "#ffffff")))))

Here is the idea:

(defun my-custom-modus-faces (&rest _)
  (modus-themes-with-colors
    (custom-set-faces
     ;; Evil state faces
     `(spaceline-evil-normal ((t (:background ,blue :foreground ,bg-main))))
     `(spaceline-evil-insert ((t (:background ,green :foreground ,bg-main))))
     `(spaceline-evil-visual ((t (:background ,magenta :foreground ,bg-main))))
     `(spaceline-evil-replace ((t (:background ,red :foreground ,bg-main))))
     `(spaceline-evil-motion ((t (:background ,yellow :foreground ,bg-main))))
     `(spaceline-evil-operator ((t (:background ,yellow :foreground ,bg-main))))
     `(spaceline-evil-emacs ((t (:background ,magenta-cooler :foreground ,bg-main)))))))

(add-hook 'enable-theme-functions #'my-custom-modus-faces)

You can learn more about the names of the colours by using the command 'modus-themes-list-colors' (or 'modus-themes-list-colors-current').

Let me know if this works for you.

-- Protesilaos Stavrou https://protesilaos.com

Luis-Henriquez-Perez commented 2 days ago

Thank you for your response. The code you provided does work for the modus themes. That being said, I will need to tweak it so that it preferably applies only to the target theme. As it stands the code modifies the user theme and so the will faces persist for even any non-modus-themes I enable. When I enable a different theme such as habamax or doom-one-light I see that the modus colors are still applied which is not what I want. I usually prefer calling custom-theme-set-faces with a specific theme for that reason. In any case, the code you provide gives me the basic blueprint for how I will go about getting dynamically colored cursors. I just have to write it in such a way that when a modus theme is enabled customizes that themes particular face but does not affect other themes.

I will note that I consider that you have answered my question satisfactorily and what I post below is just for clarification of behind thoughts and not something I expect you to help with because it is very opinionated.

The following is a recently created excerpt from my configuration influenced by alphapapa's customize-theme-faces function. It is something I wrote when I was learning about faces while trying to solve this problem.

;;;; make setting faces actually work
;; Surprisingly, the function `custom-theme-set-faces' and `custom-set-faces' do
;; not by default actually change any faces.  For that to happen the variable
;; `custom--inhibit-theme-enable' needs to be nil.  Furthermore, because I
;; disable existing themes before enabling new ones even after customizing a
;; theme the customization does not persist.  This function addresses both of
;; these issues ensuring that as expected the faces are set immediately if the
;; theme is loaded and that these changes persist even after theme change.
(defvar oo-custom-faces-alist nil
  "An alist of faces to be applied.
Each element is of the form (theme . faces).  THEME is the customized theme and
FACES is the list of customized faces for THEME.")

(defun oo-custom-set-faces (theme &rest faces)
  "Customize THEME with FACES.
Advise `enable-theme' with a function that customizes FACES when
THEME is enabled.  If THEME is already enabled, also applies
faces immediately."
  (declare (indent defun))
  (when (and after-init-time
             (or (equal theme 'user) (member theme custom-enabled-themes)))
    (let ((custom--inhibit-theme-enable nil))
      (apply #'custom-theme-set-faces theme faces)))
  (setf (alist-get theme oo-custom-faces-alist)
        (cl-union faces (alist-get theme oo-custom-faces-alist) :key #'car)))

(defun oo-apply-custom-faces (current)
  "Apply any faces that need to be applied from `oo-custom-faces-alist'."
  (info! "Current theme -> %s" current)
  (for! ((theme . faces) oo-custom-faces-alist)
    (when (or (equal theme 'user) (member theme custom-enabled-themes))
      (info! "Applying faces for %s..." theme)
      (let ((custom--inhibit-theme-enable nil))
        (apply #'custom-theme-set-faces theme faces)))))

(add-hook 'enable-theme-functions #'oo-apply-custom-faces)

This is what the oo-custom-theme-alist looks like for me.

((modus-vivendi
  (spaceline-evil-normal
   ((t
     (:background "#b6a0ff" :foreground "#000000"))))
  (spaceline-evil-insert
   ((t
     (:background "#44bc44" :foreground "#000000"))))
  (spaceline-evil-visual
   ((t
     (:background "#feacd0" :foreground "#000000"))))
  (spaceline-evil-replace
   ((t
     (:background "#ff5f59" :foreground "#000000"))))
  (spaceline-evil-motion
   ((t
     (:background "#d0bc00" :foreground "#000000"))))
  (spaceline-evil-operator
   ((t
     (:background "#d0bc00" :foreground "#000000"))))
  (spaceline-evil-emacs
   ((t
     (:background "#2fafff" :foreground "#000000")))))
 (modus-operandi
  (spaceline-evil-normal
   ((t
     (:background "#531ab6" :foreground "#ffffff"))))
  (spaceline-evil-insert
   ((t
     (:background "#006800" :foreground "#ffffff"))))
  (spaceline-evil-visual
   ((t
     (:background "#721045" :foreground "#ffffff"))))
  (spaceline-evil-replace
   ((t
     (:background "#a60000" :foreground "#ffffff"))))
  (spaceline-evil-motion
   ((t
     (:background "#6f5500" :foreground "#ffffff"))))
  (spaceline-evil-operator
   ((t
     (:background "#6f5500" :foreground "#ffffff"))))
  (spaceline-evil-emacs
   ((t
     (:background "#0031a9" :foreground "#ffffff"))))))

After I get working code for this I will update this post.

Luis-Henriquez-Perez commented 2 days ago

Right now I am using this variant. It checks to see if the current theme is a modus them and whether I've already applied the custom faces. It also removes itself from enabled-theme-functions if all the different theme faces have been applied.

(defun oo-apply-custom-faces-for-modus-themes-h (theme)
  "Add custom faces for `modus-themes'.
This hook is meant to be added to `enabled-theme-functions'."
  (when (bound-and-true-p modus-themes-items)
    (when (and (not (assoc theme oo-custom-faces-alist)) (member theme modus-themes-items))
      (info! "Specifying custom faces for theme `%s'" theme)
      (modus-themes-with-colors
        (oo-custom-set-faces
          theme
          `(spaceline-evil-normal   ((t (:background ,magenta-cooler :foreground ,bg-main))))
          `(spaceline-evil-insert   ((t (:background ,green          :foreground ,bg-main))))
          `(spaceline-evil-visual   ((t (:background ,magenta        :foreground ,bg-main))))
          `(spaceline-evil-replace  ((t (:background ,red            :foreground ,bg-main))))
          `(spaceline-evil-motion   ((t (:background ,yellow         :foreground ,bg-main))))
          `(spaceline-evil-operator ((t (:background ,yellow         :foreground ,bg-main))))
          `(spaceline-evil-emacs    ((t (:background ,blue           :foreground ,bg-main)))))))
    (unless (-difference modus-themes-items (mapcar #'car oo-custom-faces-alist))
      (info! "Done setting specific modus-themes faces.")
      (remove-hook 'enable-theme-functions #'oo-apply-custom-faces-for-modus-themes-h))))

(add-hook 'enable-theme-functions #'oo-apply-custom-faces-for-modus-themes-h)