bjogui/tests/commands.bjo

126 lines
5.7 KiB
Text
Raw Normal View History

;; 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"))