Compare commits

...

10 Commits

View File

@ -1,8 +1,8 @@
;;; notibox.el --- PosFrame-based notification UI -*- lexical-binding: t; -*- ;;; notibox.el --- PosFrame-based notification UI -*- lexical-binding: t; -*-
;; Copyright (C) 2023 mitch ;; Copyright (C) 2023 mir
;; Author: mitch <mitch@mitchmarq42.xyz> ;; Author: mir <mirmarq428@gmail.com>
;; Keywords:frames,convenience,help ;; Keywords:frames,convenience,help
;; Package-Requires: ((posframe) (alert)) ;; Package-Requires: ((posframe) (alert))
@ -31,7 +31,10 @@
(require 'posframe) (require 'posframe)
(require 'alert) (require 'alert)
(require 'shr) ;for `shr-string-pixel-width' (if (not (fboundp #'string-pixel-width))
(progn
(require 'shr) ;for `shr-string-pixel-width'
(defalias #'string-pixel-width #'shr-string-pixel-width)))
(defvar notibox-width 40) ; characters (defvar notibox-width 40) ; characters
(defvar notibox-height 4) ; characters (defvar notibox-height 4) ; characters
@ -55,7 +58,7 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down."
(let* ( (let* (
(stackdepth (or stackdepth 0)) (stackdepth (or stackdepth 0))
(parent-width (frame-pixel-width)) (parent-width (frame-pixel-width))
(child-width (* notibox-width (shr-string-pixel-width " "))) (child-width (* notibox-width (string-pixel-width " ")))
(parent-height (frame-pixel-height)) (parent-height (frame-pixel-height))
(child-height (* notibox-height (line-pixel-height))) (child-height (* notibox-height (line-pixel-height)))
(padded-height (+ child-height notibox-padding)) (padded-height (+ child-height notibox-padding))
@ -74,17 +77,18 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down."
) )
(cons x-justify y-coord) (cons x-justify y-coord)
)) ))
;; (notibox--get-position) ;; (notibox--get-position 3)
(defun notibox--prepare-buffer (title body) (defun notibox--prepare-buffer (title body)
"Populate the `*notibox*' buffer with TITLE and BODY properly formatted." "Populate the `*notibox*' buffer with TITLE and BODY properly formatted."
(with-current-buffer (get-buffer-create "*notibox*") (with-current-buffer (get-buffer-create "*notibox*")
(erase-buffer) (let ((inhibit-read-only t))
(insert (format "%s\n%s\n%s" ;; (buttonize title #'view-echo-area-messages) (erase-buffer)
title (insert (format "%s\n%s\n%s" ;; (buttonize title #'view-echo-area-messages)
(propertize (make-string notibox-width ?─) title
'face `((:foreground ,notibox-border-color))) (propertize (make-string notibox-width ?─)
body)))) 'face `((:foreground ,notibox-border-color)))
body)))))
;; (notibox--prepare-buffer "test" "this better work gadangit") ;; (notibox--prepare-buffer "test" "this better work gadangit")
(defvar notibox-current-posframes nil) (defvar notibox-current-posframes nil)
@ -114,28 +118,89 @@ If STACKDEPTH is non-nil and nonzero, return a position that far down."
(notibox--show :timeout (unless timeout alert-fade-time) (notibox--show :timeout (unless timeout alert-fade-time)
:depth (or depth (- (length notibox-current-posframes) 1))))) :depth (or depth (- (length notibox-current-posframes) 1)))))
(defun notibox--resolve-frame (object)
"Return the /frame reference/ signified by OBJECT, whatever it may be."
(cond
((framep object) object) ;this is the easy one
((windowp object) (window-frame object))
((bufferp object) (window-frame (get-window-buffer object)))
((stringp object)
(window-frame (get-buffer-window (get-buffer object))))
((symbolp object)
(window-frame (get-buffer-window (get-buffer (format "%s" object)))))
))
;; (notibox--resolve-frame "*notibox*")
(defun notibox--hide (frame) (defun notibox--hide (frame)
"Stop showing FRAME." "Stop showing FRAME."
(posframe-hide (window-buffer (frame-selected-window frame)))) (if (frame-live-p frame)
(posframe-hide (window-buffer (frame-selected-window frame)))))
(defun notibox-delete (frame) (defun notibox-delete (frame)
"Delete the notibox FRAME. "Delete the notibox FRAME.
If FRAME is the root Emacs window, or some other symbol, hide all notiboxes." If FRAME is the root Emacs window, or some other symbol, hide all notiboxes."
(if (and (framep frame) (not (frame-parent frame))) (let ((frame (notibox--resolve-frame frame)))
(delete-frame frame) (if frame
(notibox--hide (car notibox-current-posframes))) (notibox--hide frame)
(notibox--hide (car notibox-current-posframes))
))
(pop notibox-current-posframes)) (pop notibox-current-posframes))
;; (frame-parent (selected-frame)) ;=> nil ;; (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))))
)
;; (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" ": ")
;; (notibox--parse-message "evil-forward-char: End of line")
;; (notibox--parse-message "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*"))
(defun notibox--tail-echoarea () (defun notibox--tail-echoarea ()
"Show `current-message' in the notibox. If that does not exist, probably hide it." "Show `current-message' in the notibox. If that does not exist, probably hide it."
(if (current-message) (if (current-message)
(notibox-alert `( (let* ((notibox-border-color "#0faa0f") ; hacky, can we set it elsewhere?
:title ,(format "%s" (current-buffer)) (parsed-msg
:message ,(current-message))) (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
:depth 0)))
(if notibox-current-posframes (if notibox-current-posframes
(notibox-delete 'current)))) (notibox-delete (car notibox-current-posframes))))
)
(defun notibox/setup-timer () (defun notibox/setup-timer ()
"Start running notibox." "Start running notibox."
@ -147,7 +212,7 @@ If FRAME is the root Emacs window, or some other symbol, hide all notiboxes."
"Show a sample notibox to prove we can." "Show a sample notibox to prove we can."
(interactive) (interactive)
(notibox-alert '(:title "five" :message "six"))) (notibox-alert '(:title "five" :message "six")))
;; (notibox-alert '(:title "一" :message "二" :timeout nil :depth 1)) ;; (notibox-alert '(:title "一" :message "二" :timeout 5 :depth 0))
(provide 'notibox) (provide 'notibox)
;;; notibox.el ends here ;;; notibox.el ends here