From fcaaec0ac58d6410255110ac819351896255df72 Mon Sep 17 00:00:00 2001 From: Miranda Marquez Date: Fri, 15 Nov 2024 12:50:56 -0900 Subject: [PATCH] Move everything around to organize the file better (?) --- notibox.el | 223 +++++++++++++++++++++++++---------------------------- 1 file changed, 104 insertions(+), 119 deletions(-) diff --git a/notibox.el b/notibox.el index 6ae3a64..72e672a 100644 --- a/notibox.el +++ b/notibox.el @@ -27,8 +27,11 @@ ;; echo area. It was designed to be used with configurations that hide the echo ;; area by splitting it into a `minibuffer-frame'. +;; it even shows multiple notifications in a stack! + ;;; Code: +;; imports (require 'posframe) (require 'alert) (require 'dash) @@ -37,9 +40,15 @@ (require 'shr) ;for `shr-string-pixel-width' (defalias #'string-pixel-width #'shr-string-pixel-width))) +;; variables / customizations (defvar notibox-width 40) ; characters (defvar notibox-height 4) ; characters -(defvar notibox-border-color "#808080") +(defvar notibox-border-color "#808080") ; just a global default +(defvar notibox-current-posframes nil) +(defvar last-message nil + "Previous value of `current-message', stored for notibox.") +(defvar mir/framove-update-interval 0.05 + "How many seconds to wait before updating incremental frame positions.") (defgroup notibox nil "The notification box.") (defcustom notibox-corner 'bottomright @@ -52,6 +61,8 @@ (defvar notibox-padding 30) (defvar alert-fade-time 5); seconds, also provided by `alert' package (defvar notibox--refresh-delay 0.1) ; seconds. Probably don't change this one. + +;; functions - static frame ---------------------------------------------------- (defun notibox--get-position (&optional stackdepth) "Return the starting coordinate at which to place the next notibox, as cons. @@ -59,20 +70,20 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down." (let* ( ;; need to not overlap the gtk scrollbar (scrollbar-width (or (if (eq x-toolkit-scroll-bars 'gtk) - 30) ; educated guess... + 30) ; educated guess... 0)) ;; (stackdepth (or stackdepth 0)) - (stackdepth 0) ; we do not care actually + (stackdepth 0) ; we do not care actually (parent-width (frame-pixel-width)) (child-width (* notibox-width (string-pixel-width " "))) (parent-height (frame-pixel-height)) (child-height (* notibox-height (line-pixel-height))) (padded-height (+ child-height notibox-padding)) (x-justify (pcase notibox-corner - ((or 'topleft 'bottomleft) notibox-padding) - ((or 'topright 'bottomright) - (- (- parent-width scrollbar-width) - (+ child-width notibox-padding))))) + ((or 'topleft 'bottomleft) notibox-padding) + ((or 'topright 'bottomright) + (- (- parent-width scrollbar-width) + (+ child-width notibox-padding))))) (y-justify (pcase notibox-corner ((or 'topleft 'topright) notibox-padding) ((or 'bottomleft 'bottomright) @@ -82,27 +93,7 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down." ((or 'bottomleft 'bottomright) #'-))) (y-coord (funcall y-direction y-justify (* stackdepth padded-height))) ) - (cons x-justify y-coord) - )) -;; (notibox--get-position 3) - -(defun notibox--prepare-buffer (title body &optional buffer) - "Return the notibox buffer containing TITLE and BODY, properly formatted" - (let ((buf (get-buffer-create (format " *notibox-- %s: %s *" title body)))) - (with-current-buffer buf - (let ((inhibit-read-only t)) - (erase-buffer) - (insert (format "%s\n%s\n%s" ;; (buttonize title #'view-echo-area-messages) - title - (propertize (make-string notibox-width ?─) - 'face `((:foreground ,notibox-border-color))) - body)))) - buf)) -;; (notibox--prepare-buffer "test" "this better work gadahgit" (or nil "*notibox-2*")) -;; (get-buffer-create "*notibox-2*") -;; wait, we need to be actually basing the things on the buffers huh - -(defvar notibox-current-posframes nil) + (cons x-justify y-coord))) (cl-defun notibox--show (&key timeout &key depth &key buf) "Show the notibox currently prepared, with optional TIMEOUT, at DEPTH, in BUF." (let ((this-notibox @@ -126,7 +117,6 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down." ;; this-notibox) ) nil) - (defun notibox-alert (info) "Show a notibox corresponding with INFO. Can* be used as a backend for `alert'." (let* ((message (plist-get info :message)) @@ -137,7 +127,6 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down." (notibox--show :timeout (unless timeout alert-fade-time) ;; :depth (or depth (- (length notibox-current-posframes) 1)) :buf (notibox--prepare-buffer title message)))) - (defun notibox--resolve-frame (object) "Return the /frame reference/ signified by OBJECT, whatever it may be." (cond @@ -167,98 +156,20 @@ If FRAME is the root Emacs window, or some other symbol, hide all notiboxes." )) (pop notibox-current-posframes)) -;; (frame-parent (selected-frame)) ;=> nil -;; (frame-live-p (car notibox-current-posframes)) -;; (notibox-delete (car notibox-current-posframes)) - -(defun notibox--parse-message (msg) - "Given string MSG, return a cons of source and contents. -If the source is not obvious, use `current-buffer'." - (let* (source - contents - (splooted (split-string msg ": "))) - (pcase (length splooted) - ;; problem: the echo area shows only cdr, whereas messages buffer shows the source fn we want.. - (1 (progn - (setq source - (format "%s" - (current-buffer))) - (setq contents - (car splooted)))) - (2 (progn - (setq source (car splooted)) - (setq contents (cadr splooted)))) - (3 (progn - (setq source (car splooted)) - (setq contents (format "%s: %s" (cadr splooted) (caddr splooted))))) - ) - ;; (message "[debug] message: %s" msg) - (cons source contents))) -;; (split-string "evil-forward-character: End of line" ": ") -;; (length '("evil-forward-character" "end of line")) -;; (split-string "this is a valid message" ": ") -;; (message "evil-forward-char: End of line") -;; (message "%s" 'this\:\ is\ a\ \"badly\:\ formed\ message\") -(defun buflast (buffer) - "Return the last line of BUFFER as a string, without linebreaks." - (with-current-buffer buffer - (save-excursion - (goto-char (- (point-max) 1)) ; there is a \n at the end of every buffer - (buffer-substring-no-properties (point-at-bol) (point-max))))) -;; (buflast (get-buffer "*Messages*")) - -(defvar last-message nil - "Previous value of `current-message', stored for notibox.") -(defun notibox--tail-echoarea () - "Show `current-message' in the notibox. If that does not exist, probably hide it." - (if (current-message) - (unless (string= last-message (current-message)) - (let* ((notibox-border-color "#0faa0f") ; hacky, can we set it elsewhere? - (parsed-msg - (notibox--parse-message - ;; (current-message) - (buflast (get-buffer "*Messages*")) ; workaround for abbreviated stuff - )) - (title (car parsed-msg)) - (contents (cdr parsed-msg))) - (notibox-alert `( :title ,title - :message ,contents - ))) - (setq last-message (current-message))) - ;; (if notibox-current-posframes - ;; (notibox-delete (car notibox-current-posframes))) - ) - ) - (defun notibox/setup-timer () "Start running notibox." (interactive) (run-with-timer notibox--refresh-delay notibox--refresh-delay #'notibox--tail-echoarea)) - -;; (notibox-delete 'anything) (defun notibox-test-alert () "Show a sample notibox to prove we can." (interactive) (notibox-alert `(:title ,(random 8096) :message "test of notibox"))) -;; (notibox-test-alert) -;; (notibox-alert '(:title "一" :message "二" :timeout 5)) -;; (notibox-alert '(:title "三" :message "四" :timeout 5)) -;; wanna watch the variable. how do we do that -(add-variable-watcher 'notibox-current-posframes - (lambda (symbol newval operation where) - "Display the variable in *watched* buffer." - (with-current-buffer (get-buffer-create "*watched*") - (goto-char (point-max)) - (insert - (format "\n%s:\n was: %s\n now: %s" - symbol - (eval symbol) - newval))) - )) -(--map (remove-variable-watcher 'notibox-current-posframes it) - (get-variable-watchers 'notibox-current-posframes)) +(defun notibox-test-message () + "Send a message and see if it gets picked up by notibox." + (message "test message %s: %s is a number" (random 40) (random 500))) -;; begin frame moving experiments. thought we already did this... + +;; functions - frame moving ---------------------------------------------------- (defun mir/move-frame (frame pos) "Move FRAME to POS, a cons of (top . left)." (if (frame-live-p frame) @@ -267,9 +178,6 @@ If the source is not obvious, use `current-buffer'." (top . ,(car pos)) (left . ,(cdr pos)) )))) -(defvar mir/framove-update-interval 0.05 - "How many seconds to wait before updating incremental frame positions.") - (defun mir/framove-gradual (frame newpos duration) "Move FRAME to NEWPOS ( a cons of top and left) over DURATION. @@ -329,9 +237,6 @@ intermediate positions are calculated via `mir/framove-update-interval'." old-x ) 0.125)))) ;; (notibox-test-alert) -(defun notibox-test-message () - "Send a message and see if it gets picked up by notibox." - (message "test message %s: %s is a number" (random 40) (random 500))) ;; (notibox-test-message) (defun mir/push-notibox (frame) @@ -341,5 +246,85 @@ intermediate positions are calculated via `mir/framove-update-interval'." (push frame notibox-current-posframes) ) +;; functions - message parsing ------------------------------------------------- +(defun notibox--prepare-buffer (title body &optional buffer) + "Return the notibox buffer containing TITLE and BODY, properly formatted" + (let ((buf (get-buffer-create (format " *notibox-- %s: %s *" title body)))) + (with-current-buffer buf + (let ((inhibit-read-only t)) + (erase-buffer) + (insert (format "%s\n%s\n%s" ;; (buttonize title #'view-echo-area-messages) + title + (propertize (make-string notibox-width ?─) + 'face `((:foreground ,notibox-border-color))) + body)))) + buf)) +(defun notibox--parse-message (msg) + "Given string MSG, return a cons of source and contents. +If the source is not obvious, use `current-buffer'." + (let* (source + contents + (splooted (split-string msg ": "))) + (pcase (length splooted) + ;; problem: the echo area shows only cdr, whereas messages buffer shows the source fn we want.. + (1 (progn + (setq source + (format "%s" + (current-buffer))) + (setq contents + (car splooted)))) + (2 (progn + (setq source (car splooted)) + (setq contents (cadr splooted)))) + (3 (progn + (setq source (car splooted)) + (setq contents (format "%s: %s" (cadr splooted) (caddr splooted))))) + ) + (cons source contents))) +(defun buflast (buffer) + "Return the last line of BUFFER as a string, without linebreaks." + (with-current-buffer buffer + (save-excursion + (goto-char (- (point-max) 1)) ; there is a \n at the end of every buffer + (buffer-substring-no-properties (point-at-bol) (point-max))))) +(defun notibox--tail-echoarea () + "Show `current-message' in the notibox. If that does not exist, probably hide it." + (if (current-message) + (unless (string= last-message (current-message)) + (let* ((notibox-border-color "#0faa0f") ; hacky, can we set it elsewhere? + (parsed-msg + (notibox--parse-message + ;; (current-message) + (buflast (get-buffer "*Messages*")) ; workaround for abbreviated stuff + )) + (title (car parsed-msg)) + (contents (cdr parsed-msg))) + (notibox-alert `( :title ,title + :message ,contents + ))) + (setq last-message (current-message))) + ;; (if notibox-current-posframes + ;; (notibox-delete (car notibox-current-posframes))) + ) + ) + + + + +;; (add-variable-watcher 'notibox-current-posframes +;; (lambda (symbol newval operation where) +;; "Display the variable in *watched* buffer." +;; (with-current-buffer (get-buffer-create "*watched*") +;; (goto-char (point-max)) +;; (insert +;; (format "\n%s:\n was: %s\n now: %s" +;; symbol +;; (eval symbol) +;; newval))) +;; )) +;; (--map (remove-variable-watcher 'notibox-current-posframes it) +;; (get-variable-watchers 'notibox-current-posframes)) + + (provide 'notibox) ;;; notibox.el ends here