v0.14.0: Mouse improvements - selection tracking and link clicking

This commit is contained in:
Hermes
2026-05-11 22:41:34 +00:00
parent ddd3950e49
commit edd5a7b8d1
6 changed files with 128 additions and 3 deletions

View File

@@ -19,7 +19,8 @@
(backend-write b (format nil "~C[2J~C[H" #\Esc #\Esc)))) (backend-write b (format nil "~C[2J~C[H" #\Esc #\Esc))))
(defgeneric draw-text (backend x y string fg bg &key (defgeneric draw-text (backend x y string fg bg &key
bold italic underline reverse dim blink)) bold italic underline reverse dim blink
&allow-other-keys))
(defgeneric draw-border (backend x y width height (defgeneric draw-border (backend x y width height
&key style fg bg title title-align)) &key style fg bg title title-align))

View File

@@ -127,7 +127,8 @@ See =docs/plans/2026-05-11-rendering-pipeline.md= for full implementation plan.
#:make-framebuffer #:fb-framebuffer #:make-framebuffer #:fb-framebuffer
#:framebuffer-width #:framebuffer-height #:framebuffer-width #:framebuffer-height
#:diff-framebuffers #:flush-framebuffer #:diff-framebuffers #:flush-framebuffer
#:with-scissor)) #:with-scissor
#:extract-text #:fb-cell-link-url))
#+END_SRC #+END_SRC
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp #+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp

View File

@@ -102,3 +102,35 @@ module adds:
(setf cl-tty.mouse::*selection* (make-selection :text "hello")) (setf cl-tty.mouse::*selection* (make-selection :text "hello"))
(is (equal "hello" (get-selection)))) (is (equal "hello" (get-selection))))
#+END_SRC #+END_SRC
** Selection tracking
#+BEGIN_SRC lisp :tangle ../tests/mouse-tests.lisp
(def-test start-selection-initializes-state ()
(start-selection 5 10)
(is-true (selection-active-p))
(is (equal '(5 . 10) cl-tty.mouse::*selection-start*))
(is (equal '(5 . 10) cl-tty.mouse::*selection-end*))
(setf cl-tty.mouse::*selection-active* nil
cl-tty.mouse::*selection-start* nil
cl-tty.mouse::*selection-end* nil))
(def-test update-selection-moves-end ()
(start-selection 0 0)
(update-selection 3 7)
(is (equal '(3 . 7) cl-tty.mouse::*selection-end*))
(setf cl-tty.mouse::*selection-active* nil
cl-tty.mouse::*selection-start* nil
cl-tty.mouse::*selection-end* nil))
(def-test finalize-selection-extracts-text ()
(let* ((fb-be (cl-tty.rendering:make-framebuffer-backend))
(fb (cl-tty.rendering:fb-framebuffer fb-be)))
(cl-tty.backend:draw-text fb-be 0 0 "hello" nil nil)
(cl-tty.backend:draw-text fb-be 0 1 "world" nil nil)
(start-selection 0 0)
(update-selection 4 1)
(let ((text (finalize-selection fb)))
(is (equal "hello
world" text)))))
#+END_SRC

View File

@@ -7,7 +7,8 @@
#:make-framebuffer #:fb-framebuffer #:make-framebuffer #:fb-framebuffer
#:framebuffer-width #:framebuffer-height #:framebuffer-width #:framebuffer-height
#:diff-framebuffers #:flush-framebuffer #:diff-framebuffers #:flush-framebuffer
#:with-scissor)) #:with-scissor
#:extract-text #:fb-cell-link-url))
(in-package :cl-tty.rendering) (in-package :cl-tty.rendering)

View File

@@ -64,3 +64,64 @@
(draw-text fb 0 0 "X" :red nil) (draw-text fb 0 0 "X" :red nil)
(let ((changed (flush-framebuffer (make-framebuffer 80 24) (fb-framebuffer fb) real-be))) (let ((changed (flush-framebuffer (make-framebuffer 80 24) (fb-framebuffer fb) real-be)))
(is (>= changed 1))))) (is (>= changed 1)))))
;; ── Frame inspection ──────────────────────────────────────────
(test fb-cell-link-url-returns-nil-for-blank-cell
(let ((fb (make-framebuffer 10 10)))
(is (null (fb-cell-link-url fb 5 5)))))
(test fb-cell-link-url-finds-link-url
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "click" nil nil :link-url "https://example.com")
(is (equal "https://example.com" (fb-cell-link-url (fb-framebuffer fb) 0 0)))
(is (null (fb-cell-link-url (fb-framebuffer fb) 5 5)))))
(test fb-cell-link-url-out-of-bounds-returns-nil
(let ((fb (make-framebuffer 5 5)))
(is (null (fb-cell-link-url fb 10 10)))))
(test extract-text-single-row
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "hello" nil nil)
(let ((cells (fb-framebuffer fb)))
(is (equal "hello" (extract-text cells 0 0 4 0))))))
(test extract-text-multi-row
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "abc" nil nil)
(draw-text fb 0 1 "def" nil nil)
(let* ((cells (fb-framebuffer fb))
(text (extract-text cells 0 0 2 1)))
(is (equal "abc
def" text)))))
;; --- Frame inspection -------------------------------------------------
(test fb-cell-link-url-returns-nil-for-blank-cell
(let ((fb (make-framebuffer 10 10)))
(is (null (fb-cell-link-url fb 5 5)))))
(test fb-cell-link-url-finds-link-url
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "click" nil nil :link-url "https://example.com")
(is (equal "https://example.com" (fb-cell-link-url (fb-framebuffer fb) 0 0)))
(is (null (fb-cell-link-url (fb-framebuffer fb) 5 5)))))
(test fb-cell-link-url-out-of-bounds-returns-nil
(let ((fb (make-framebuffer 5 5)))
(is (null (fb-cell-link-url fb 10 10)))))
(test extract-text-single-row
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "hello" nil nil)
(let ((cells (fb-framebuffer fb)))
(is (equal "hello" (extract-text cells 0 0 4 0))))))
(test extract-text-multi-row
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "abc" nil nil)
(draw-text fb 0 1 "def" nil nil)
(let* ((cells (fb-framebuffer fb))
(text (extract-text cells 0 0 2 1)))
(is (equal "abc
def" text)))))

View File

@@ -15,3 +15,32 @@
(def-test selection-set-and-get () (def-test selection-set-and-get ()
(setf cl-tty.mouse::*selection* (make-selection :text "hello")) (setf cl-tty.mouse::*selection* (make-selection :text "hello"))
(is (equal "hello" (get-selection)))) (is (equal "hello" (get-selection))))
;; --- Selection tracking -------------------------------------------------
(def-test start-selection-initializes-state ()
(start-selection 5 10)
(is-true (selection-active-p))
(is (equal '(5 . 10) cl-tty.mouse::*selection-start*))
(is (equal '(5 . 10) cl-tty.mouse::*selection-end*))
(setf cl-tty.mouse::*selection-active* nil
cl-tty.mouse::*selection-start* nil
cl-tty.mouse::*selection-end* nil))
(def-test update-selection-moves-end ()
(start-selection 0 0)
(update-selection 3 7)
(is (equal '(3 . 7) cl-tty.mouse::*selection-end*))
(setf cl-tty.mouse::*selection-active* nil
cl-tty.mouse::*selection-start* nil
cl-tty.mouse::*selection-end* nil))
(def-test finalize-selection-extracts-text ()
(let* ((fb-be (cl-tty.rendering:make-framebuffer-backend))
(fb (cl-tty.rendering:fb-framebuffer fb-be)))
(cl-tty.backend:draw-text fb-be 0 0 "hello" nil nil)
(cl-tty.backend:draw-text fb-be 0 1 "world" nil nil)
(start-selection 0 0)
(update-selection 4 1)
(let ((text (finalize-selection fb)))
(is (equal "hello
world" text)))))