Commands for focus, scrolling and the clipboard, and pictures from bytes
focus, scroll-to-end, scroll-to-start and scroll-into-view find their control by the key of its view, and run once the update's controls are laid out. copy-text puts text on the clipboard, and paste-text sends what it holds. A command that sends no message ends its fiber when its work is done, so none is left waiting until the app stops. image-data shows a picture from the bytes of a file, read again only when it is given another array. start-headless! now adds the Fluent theme, as run-app does, so controls are templated and laid out as in a window. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8) Co-Authored-By: eca-agent <git@eca.dev>
This commit is contained in:
parent
52ebd0ace8
commit
708256d0b3
4 changed files with 342 additions and 11 deletions
125
tests/commands.bjo
Normal file
125
tests/commands.bjo
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
;; bjogui/tests/commands.bjo — commands that act on the controls and the
|
||||
;; clipboard, and pictures from memory.
|
||||
;;
|
||||
;; bjo run tests/commands.bjo
|
||||
;;
|
||||
;; Avalonia runs with no screen, and the app is shown in a window of it, so
|
||||
;; that a control can take the keyboard, a scroll view is laid out, and there
|
||||
;; is a clipboard.
|
||||
|
||||
(import (std prelude))
|
||||
(import (std simpletest))
|
||||
(import (std interop))
|
||||
(import (bjogui view))
|
||||
(import (bjogui app))
|
||||
|
||||
(import/class
|
||||
(Object (: System.Object))
|
||||
(Control (: Avalonia.Controls.Control))
|
||||
(Window (: Avalonia.Controls.Window (-> Window)))
|
||||
(ContentControl (: Avalonia.Controls.ContentControl))
|
||||
(Layoutable (: Avalonia.Layout.Layoutable))
|
||||
(InputElement (: Avalonia.Input.InputElement))
|
||||
(ScrollViewer (: Avalonia.Controls.ScrollViewer))
|
||||
(Image (: Avalonia.Controls.Image))
|
||||
(IImage (: Avalonia.Media.IImage))
|
||||
(Vector (: Avalonia.Vector)))
|
||||
|
||||
(import/extern
|
||||
(content! (: Avalonia.Controls.ContentControl.Content (-> ContentControl Object void) #:set))
|
||||
(show! (: Avalonia.Controls.Window.Show (-> Window void)))
|
||||
(width! (: Avalonia.Layout.Layoutable.Width (-> Layoutable double void) #:set))
|
||||
(height! (: Avalonia.Layout.Layoutable.Height (-> Layoutable double void) #:set))
|
||||
(update-layout! (: Avalonia.Layout.Layoutable.UpdateLayout (-> Layoutable void)))
|
||||
(focused? (: Avalonia.Input.InputElement.IsFocused (-> InputElement bool) #:get))
|
||||
(offset-of (: Avalonia.Controls.ScrollViewer.Offset (-> ScrollViewer Vector) #:get))
|
||||
(vector-y (: Avalonia.Vector.Y (-> Vector double) #:get))
|
||||
(source-of (: Avalonia.Controls.Image.Source (-> Image IImage) #:get))
|
||||
(sleep-ms (: System.Threading.Thread.Sleep (-> int void) #:blocking)))
|
||||
|
||||
(type (: Msg (Union FocusName FocusOther End Start Into (: Copy string) Paste (: Pasted (Option string)))))
|
||||
(type (: Model (Record (: messages int) (: pasted (Option string)))))
|
||||
|
||||
(: (-> Msg Model (Tuple Model (List (Event Msg)))))
|
||||
(defun (gui-update msg m0)
|
||||
(def m (record-set m0 (messages (+ (.messages m0) 1))))
|
||||
(match msg
|
||||
(FocusName (next m (focus "name")))
|
||||
(FocusOther (next m (focus "other")))
|
||||
(End (next m (scroll-to-end "log")))
|
||||
(Start (next m (scroll-to-start "log")))
|
||||
(Into (next m (scroll-into-view "line 25")))
|
||||
((Copy s) (next m (copy-text s)))
|
||||
(Paste (next m (paste-text Pasted)))
|
||||
((Pasted s) (next (record-set m (pasted s))))))
|
||||
|
||||
(def picture (file-read-bytes "docs/mixer.png"))
|
||||
|
||||
(: (-> Model (View Msg)))
|
||||
(defun (view m)
|
||||
(column
|
||||
(keyed "name" (text-input "" (fun (s) Start))) ; 0
|
||||
(keyed "other" (text-input "" (fun (s) Start))) ; 1
|
||||
(keyed "log" (scroll (apply column (vec->array (vec-map (fun (i) (keyed #"line ${i}" (text #"line ${i}")))
|
||||
(->vec (range 0 40)))))
|
||||
#:height 60.0)) ; 2
|
||||
(image-data picture #:width 46.0 #:height 30.0) ; 3
|
||||
(image-data (: (make-array 3 #:fill (cast byte 7)) (Array byte))))) ; 4
|
||||
|
||||
(defun (main)
|
||||
(start-headless!)
|
||||
(def w (Window.))
|
||||
(width! (cast Layoutable w) 300.0)
|
||||
(height! (cast Layoutable w) 400.0)
|
||||
(def app (start-app (Model (messages 0) (pasted None)) gui-update view
|
||||
(fun (c) (content! (cast ContentControl w) (cast Object c)))))
|
||||
(show! w)
|
||||
(defun (ctl path) (app-control-at app path))
|
||||
(defun (settle) (app-run-pending! app) (update-layout! (cast Layoutable w)))
|
||||
(: (-> (-> bool) bool))
|
||||
(defun (wait-for done?)
|
||||
(let go ((tries 0))
|
||||
(settle)
|
||||
(cond ((done?) #t)
|
||||
((> tries 200) #f)
|
||||
(else (sleep-ms 10) (go (+ tries 1))))))
|
||||
(defun (log-offset) (vector-y (offset-of (cast ScrollViewer (ctl '(2))))))
|
||||
(settle)
|
||||
|
||||
;; --- focus ------------------------------------------------------------------------
|
||||
(app-dispatch! app FocusName)
|
||||
(expect "focus gives the keyboard to the keyed control"
|
||||
(wait-for (fun () (focused? (cast InputElement (ctl '(0)))))) #t)
|
||||
(app-dispatch! app FocusOther)
|
||||
(expect "and to another" (wait-for (fun () (focused? (cast InputElement (ctl '(1)))))) #t)
|
||||
(expect "which the first has no more" (focused? (cast InputElement (ctl '(0)))) #f)
|
||||
|
||||
;; --- scrolling --------------------------------------------------------------------
|
||||
(expect "the log starts at the top" (log-offset) 0.0)
|
||||
(app-dispatch! app End)
|
||||
(expect "scroll-to-end" (wait-for (fun () (> (log-offset) 500.0))) #t)
|
||||
(app-dispatch! app Start)
|
||||
(expect "scroll-to-start" (wait-for (fun () (= (log-offset) 0.0))) #t)
|
||||
(app-dispatch! app Into)
|
||||
(expect "scroll-into-view" (wait-for (fun () (> (log-offset) 300.0))) #t)
|
||||
|
||||
;; --- the clipboard -----------------------------------------------------------------
|
||||
(app-dispatch! app (Copy "copied"))
|
||||
(app-dispatch! app Paste)
|
||||
(expect "a copy is pasted" (wait-for (fun () (= (.pasted (app-model app)) (Some "copied")))) #t)
|
||||
|
||||
;; --- commands that send nothing --------------------------------------------------
|
||||
(sleep-ms 50)
|
||||
(settle)
|
||||
(expect "send nothing: only the messages dispatched, and Pasted" (.messages (app-model app)) 8)
|
||||
|
||||
;; --- pictures from memory ---------------------------------------------------------
|
||||
(expect "a picture from bytes" (eq? (cast Object (source-of (cast Image (ctl '(3))))) (clr-default)) #f)
|
||||
(expect "bytes that are no picture show nothing"
|
||||
(eq? (cast Object (source-of (cast Image (ctl '(4))))) (clr-default)) #t)
|
||||
|
||||
(println "bjogui commands ok"))
|
||||
Loading…
Add table
Add a link
Reference in a new issue