v0.8.0: TUI simplification — process-key-event, with-frame, inline reader
Replace queue-based key dispatch with process-key-event (inline in reader, zero latency between keypress and render). Add with-frame to cl-tty.backend (error-safe begin-sync/end-sync wrapper). Use with-frame in redraw instead of manual begin-sync/end-sync. Add initial render before main loop (UI appears before first read-event). Remove position-cursor (replaced by inline block cursor in view-input). Remove input-string/input-insert-char/input-delete-char wrappers. Remove :input-buffer/:cursor-pos from state (managed by text-input widget). passepartout script: set *debugger-hook* nil and failure-behaviour :warn before quickload to survive compile warnings; remove cache-clear line.
This commit is contained in:
@@ -758,6 +758,55 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
|
||||
|
||||
** Connection
|
||||
#+BEGIN_SRC lisp :tangle /home/user/.local/share/passepartout/lisp/channel-tui-main.lisp
|
||||
;; Process a key-event: route through dialog, keymap, navigation, or text-input.
|
||||
(defun process-key-event (event)
|
||||
(let* ((k (cl-tty.input:key-event-key event)))
|
||||
(cond
|
||||
((st :dialog-stack)
|
||||
(let* ((dlg (car (st :dialog-stack)))
|
||||
(sel (cl-tty.dialog:dialog-content dlg)))
|
||||
(cond
|
||||
((eq k :escape)
|
||||
(pop (st :dialog-stack))
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((member k '(:up :down))
|
||||
(if (eq k :up)
|
||||
(cl-tty.dialog:select-prev sel)
|
||||
(cl-tty.dialog:select-next sel))
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((eq k :enter)
|
||||
(let* ((filtered (cl-tty.dialog:select-filtered-options sel))
|
||||
(idx (cl-tty.dialog:select-selected-index sel))
|
||||
(item (when (< idx (length filtered))
|
||||
(third (nth idx filtered)))))
|
||||
(when item
|
||||
(let ((cb (cl-tty.dialog:select-on-select sel)))
|
||||
(when cb (funcall cb item))))
|
||||
(pop (st :dialog-stack))
|
||||
(setf (st :dirty) (list t t nil))))
|
||||
((let ((ch (code-char (cl-tty.input:key-event-code event))))
|
||||
(and ch (graphic-char-p ch))
|
||||
(setf (cl-tty.dialog:select-filter sel)
|
||||
(concatenate 'string
|
||||
(or (cl-tty.dialog:select-filter sel) "")
|
||||
(string ch)))))
|
||||
((eq k :backspace)
|
||||
(let* ((f (cl-tty.dialog:select-filter sel))
|
||||
(len (length (or f ""))))
|
||||
(when (> len 0)
|
||||
(setf (cl-tty.dialog:select-filter sel)
|
||||
(subseq f 0 (1- len)))))))))
|
||||
((cl-tty.input:dispatch-key-event event)
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((member k '(:enter :tab :escape :up :down))
|
||||
(on-key k))
|
||||
(t (handler-case
|
||||
(progn
|
||||
(cl-tty.input:handle-text-input (st :text-input) event)
|
||||
(setf (st :dirty) (list nil nil t)))
|
||||
(error (c)
|
||||
(add-msg :system (format nil "* Input error: ~a *" c))))))))
|
||||
|
||||
(defun connect-daemon (&optional (host "127.0.0.1") (start-port 9105) (end-port 9115))
|
||||
"Try to connect to daemon once across START-PORT to END-PORT.
|
||||
Returns T on success, nil on failure. Does NOT wait or retry."
|
||||
@@ -919,8 +968,11 @@ Returns T on success, nil on failure. Does NOT wait or retry."
|
||||
(loop while (and (st :running) (not (st :connected)))
|
||||
do (connect-daemon)
|
||||
(unless (st :connected) (sleep 5))))
|
||||
:name "daemon-auto-connect"))
|
||||
(loop while (st :running) do
|
||||
:name "daemon-auto-connect"))
|
||||
;; Initial render before first read-event (which may block)
|
||||
(unless (st :dialog-stack)
|
||||
(redraw be w h))
|
||||
(loop while (st :running) do
|
||||
(dolist (ev (drain-queue))
|
||||
(cond
|
||||
((eq (getf ev :type) :daemon)
|
||||
@@ -982,25 +1034,8 @@ Returns T on success, nil on failure. Does NOT wait or retry."
|
||||
(let ((new-size resize-data))
|
||||
(setq w (car new-size) h (cdr new-size))
|
||||
(setf (st :dirty) (list t t t))))
|
||||
((cl-tty.input:key-event-p ev)
|
||||
(let* ((k (cl-tty.input:key-event-key ev))
|
||||
(ctrl (cl-tty.input:key-event-ctrl ev))
|
||||
(code (cl-tty.input:key-event-code ev))
|
||||
(ch (cond
|
||||
;; Ctrl+letter → :CTRL-X keyword (compatible with case dispatch)
|
||||
(ctrl (let ((c (char (symbol-name k) 0)))
|
||||
(intern (string-upcase (format nil "CTRL-~a" c)) :keyword)))
|
||||
;; PageUp/PageDown → :ppage/:npage
|
||||
((eq k :page-up) :ppage)
|
||||
((eq k :page-down) :npage)
|
||||
;; Single-char keyword → printable character
|
||||
((and (keywordp k) (= (length (symbol-name k)) 1))
|
||||
(code-char code))
|
||||
;; Everything else → pass keyword through
|
||||
(t k))))
|
||||
(queue-event
|
||||
(list :type :key
|
||||
:payload (list :code (or code 0) :ch ch)))))))
|
||||
((cl-tty.input:key-event-p ev)
|
||||
(process-key-event ev))))
|
||||
(error (c)
|
||||
(add-msg :system (format nil "* Reader error: ~a *" c))))
|
||||
;; Guard w and h before render (resize or other code may have set them to nil)
|
||||
@@ -1062,12 +1097,9 @@ Returns T on success, nil on failure. Does NOT wait or retry."
|
||||
(cl-tty.backend:draw-text be 0 (- h 3)
|
||||
(format nil "> ~a" (or filter ""))
|
||||
(theme-color :input-prompt) bg-p))
|
||||
(cl-tty.backend:end-sync be))
|
||||
(sleep 0.1)
|
||||
;; Show terminal cursor at input position every frame
|
||||
(unless (st :dialog-stack)
|
||||
(passepartout.channel-tui:position-cursor be w h))))
|
||||
(progn (disconnect-daemon)))))
|
||||
(cl-tty.backend:end-sync be))
|
||||
(sleep 0.1)))
|
||||
(progn (disconnect-daemon)))))
|
||||
#+END_SRC
|
||||
|
||||
* Test Suite
|
||||
|
||||
Reference in New Issue
Block a user