From b2324e5d778b9f6b55ac2df9c841c50facf0f4b6 Mon Sep 17 00:00:00 2001 From: TEC Date: Wed, 6 Mar 2024 22:27:44 +0800 Subject: [PATCH] Introduce analogue clock time icon With the SVG library (bundled as part of Emacs), we can generate an analogue clock for the current time to-the-minute, instead of using the nearest-hour nerdicon. This relies on a graphical display, and so a new variable is introduced, `doom-modeline-time-analogue-clock' and the behaviour is gated on (display-graphic-p). There are a number of variables that affect the style of the displayed clock. For now, I've chosen to be conservative and just expose `doom-modeline-time-clock-minute-resolution' as a custom variable, leaving the rest as constants. We may well want to revisit this in the future. I've personally been running this variant of the time segment for over a year now, and as a result am pretty confident there are no behaviour or performance issues with this implementation. This implementation is originally sourced from https://github.com/tecosaur/emacs-config/blob/a3c3b73a00f4e034ffeae11f20b00d1eb948a6e6/config.org#time. --- README.md | 4 ++ doom-modeline-core.el | 14 +++++++ doom-modeline-segments.el | 85 ++++++++++++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index cd10e984..13ceadbf 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,10 @@ Run `M-x customize-group RET doom-modeline RET` or set the variables. ;; It respects option `doom-modeline-icon' and option `doom-modeline-time-icon'. (setq doom-modeline-time-live-icon t) +;; Whether to use an analogue clock svg as the live time icon. +;; It respects options `doom-modeline-icon', `doom-modeline-time-icon', and `doom-modeline-time-live-icon'. +(setq doom-modeline-time-analogue-clock t) + ;; Whether to use unicode as a fallback (instead of ASCII) when not using icons. (setq doom-modeline-unicode-fallback nil) diff --git a/doom-modeline-core.el b/doom-modeline-core.el index 4ce0406c..a7b51bc4 100644 --- a/doom-modeline-core.el +++ b/doom-modeline-core.el @@ -233,6 +233,20 @@ It respects option `doom-modeline-icon' and option `doom-modeline-time-icon'." :type 'boolean :group 'doom-modeline) +(defcustom doom-modeline-time-analogue-clock t + "Whether to draw an analogue clock SVG as the live time icon. + +It respects options `doom-modeline-icon', `doom-modeline-time-icon', and +`doom-modeline-time-live-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time-clock-minute-resolution 1 + "The clock will be updated every this many minutes, truncated. +See `doom-modeline-time-analogue-clock'." + :type 'number + :group 'doom-modeline) + (defcustom doom-modeline-unicode-fallback nil "Whether to use unicode as a fallback (instead of ASCII) when not using icons." :type 'boolean diff --git a/doom-modeline-segments.el b/doom-modeline-segments.el index 1fcf9978..c9faa468 100644 --- a/doom-modeline-segments.el +++ b/doom-modeline-segments.el @@ -3080,28 +3080,73 @@ mouse-3: Restart preview" ;; Display time ;; +(defconst doom-modeline--clock-hour-hand-ratio 0.45 + "Length of the hour hand as a proportion of the radius.") + +(defconst doom-modeline--clock-minute-hand-ratio 0.7 + "Length of the minute hand as a proportion of the radius.") + +(defconst doom-modeline--clock-inverse-size 4.8 + "The size of the clock, as an inverse proportion to the mode line height.") + +(defvar doom-modeline--clock-cache nil + "The last result of `doom-modeline--generate-clock'.") + +(defun doom-modeline--generate-clock () + "Return a string containing the current time as an analogue clock svg. +When the svg library is not availible, return nil." + (cdr + (or (and (equal (truncate (float-time) + (* doom-modeline-time-clock-minute-resolution 60)) + doom-modeline--clock-cache)) + (and (require 'svg nil t) + (setq doom-modeline--clock-cache + (cons (truncate (float-time) + (* doom-modeline-time-clock-minute-resolution 60)) + (with-temp-buffer + (svg-insert-image + (micro-clock-svg + (string-to-number (format-time-string "%-I")) ; hour + (* (truncate (string-to-number (format-time-string "%-M")) + doom-modeline-time-clock-minute-resolution) + doom-modeline-time-clock-minute-resolution) ; minute + (/ doom-modeline-height doom-modeline--clock-inverse-size) ; radius + "currentColor")) + (propertize + " " + 'display + (append (get-text-property 0 'display (buffer-string)) + '(:ascent center)) + 'face 'doom-modeline-time + 'help-echo (lambda (_window _object _pos) + (format-time-string "%c")))))))))) + (defun doom-modeline-time-icon () "Displays the time icon." - (doom-modeline-icon - 'mdicon - (if doom-modeline-time-live-icon - (pcase (% (caddr (decode-time)) 12) - (0 "nf-md-clock_time_twelve_outline") - (1 "nf-md-clock_time_one_outline") - (2 "nf-md-clock_time_two_outline") - (3 "nf-md-clock_time_three_outline") - (4 "nf-md-clock_time_four_outline") - (5 "nf-md-clock_time_five_outline") - (6 "nf-md-clock_time_six_outline") - (7 "nf-md-clock_time_seven_outline") - (8 "nf-md-clock_time_eight_outline") - (9 "nf-md-clock_time_nine_outline") - (10 "nf-md-clock_time_ten_outline") - (11 "nf-md-clock_time_eleven_outline")) - "nf-md-clock_outline") - "⏰" - "" - :face '(:inherit doom-modeline-time :weight normal))) + (or (and doom-modeline-time-live-icon + doom-modeline-time-analogue-clock + (display-graphic-p) + (doom-modeline--generate-clock)) + (doom-modeline-icon + 'mdicon + (if doom-modeline-time-live-icon + (pcase (% (caddr (decode-time)) 12) + (0 "nf-md-clock_time_twelve_outline") + (1 "nf-md-clock_time_one_outline") + (2 "nf-md-clock_time_two_outline") + (3 "nf-md-clock_time_three_outline") + (4 "nf-md-clock_time_four_outline") + (5 "nf-md-clock_time_five_outline") + (6 "nf-md-clock_time_six_outline") + (7 "nf-md-clock_time_seven_outline") + (8 "nf-md-clock_time_eight_outline") + (9 "nf-md-clock_time_nine_outline") + (10 "nf-md-clock_time_ten_outline") + (11 "nf-md-clock_time_eleven_outline")) + "nf-md-clock_outline") + "⏰" + "" + :face '(:inherit doom-modeline-time :weight normal)))) (doom-modeline-def-segment time (when (and doom-modeline-time