The compiler now casts from a type parameter through object, so the helpers no longer need their parameter declared as a Control. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8) Co-Authored-By: eca-agent <git@eca.dev>
176 lines
8.8 KiB
Text
176 lines
8.8 KiB
Text
;; 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/canvas.bjo — views of another message type, and canvases.
|
|
;;
|
|
;; bjo run tests/canvas.bjo
|
|
;;
|
|
;; No window is opened. Pointer events are made as Avalonia makes them, and
|
|
;; raised on the canvas and on a shape on it.
|
|
|
|
(import (std prelude))
|
|
(import (std simpletest))
|
|
(import (bjogui view))
|
|
(import (bjogui app))
|
|
|
|
(import/class
|
|
(Object (: System.Object))
|
|
(ClrString (: System.String))
|
|
(Control (: Avalonia.Controls.Control))
|
|
(AvaloniaObject (: Avalonia.AvaloniaObject))
|
|
(Layoutable (: Avalonia.Layout.Layoutable))
|
|
(Visual (: Avalonia.Visual))
|
|
(Interactive (: Avalonia.Interactivity.Interactive))
|
|
(RoutedEvent (: Avalonia.Interactivity.RoutedEvent))
|
|
(RoutedEventArgs (: Avalonia.Interactivity.RoutedEventArgs (-> RoutedEvent RoutedEventArgs)))
|
|
(TextBox (: Avalonia.Controls.TextBox))
|
|
(Shape (: Avalonia.Controls.Shapes.Shape))
|
|
(LineShape (: Avalonia.Controls.Shapes.Line))
|
|
(PathShape (: Avalonia.Controls.Shapes.Path))
|
|
(IBrush (: Avalonia.Media.IBrush))
|
|
(Geometry (: Avalonia.Media.Geometry))
|
|
(Point (: Avalonia.Point (-> double double Point)))
|
|
(Pointer (: Avalonia.Input.Pointer (-> int PointerType bool Pointer)))
|
|
(PointerType (: Avalonia.Input.PointerType))
|
|
(IPointer (: Avalonia.Input.IPointer))
|
|
(PointProps (: Avalonia.Input.PointerPointProperties (-> PointProps)))
|
|
(KeyModifiers (: Avalonia.Input.KeyModifiers))
|
|
(MouseButton (: Avalonia.Input.MouseButton))
|
|
(Pressed (: Avalonia.Input.PointerPressedEventArgs
|
|
(-> Object IPointer Visual Point ulong PointProps KeyModifiers int Pressed)))
|
|
(Moved (: Avalonia.Input.PointerEventArgs
|
|
(-> RoutedEvent Object IPointer Visual Point ulong PointProps KeyModifiers Moved)))
|
|
(Released (: Avalonia.Input.PointerReleasedEventArgs
|
|
(-> Object IPointer Visual Point ulong PointProps KeyModifiers MouseButton Released))))
|
|
|
|
(import/extern
|
|
(click-event (: Avalonia.Controls.Button.ClickEvent #:get))
|
|
(moved-event (: Avalonia.Input.InputElement.PointerMovedEvent #:get))
|
|
(raise-event (: Avalonia.Interactivity.Interactive.RaiseEvent (-> Interactive RoutedEventArgs void)))
|
|
(box-text! (: Avalonia.Controls.TextBox.Text (-> TextBox string void) #:set))
|
|
(left-of* (: Avalonia.Controls.Canvas.GetLeft (-> AvaloniaObject double)))
|
|
(top-of* (: Avalonia.Controls.Canvas.GetTop (-> AvaloniaObject double)))
|
|
(fill-of (: Avalonia.Controls.Shapes.Shape.Fill (-> Shape IBrush) #:get))
|
|
(stroke-width-of (: Avalonia.Controls.Shapes.Shape.StrokeThickness (-> Shape double) #:get))
|
|
(line-end (: Avalonia.Controls.Shapes.Line.EndPoint (-> LineShape Point) #:get))
|
|
(path-data (: Avalonia.Controls.Shapes.Path.Data (-> PathShape Geometry) #:get))
|
|
(point-x (: Avalonia.Point.X (-> Point double) #:get))
|
|
(width-of (: Avalonia.Layout.Layoutable.Width (-> Layoutable double) #:get)))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; A component: a counter with a name, which knows nothing of who uses it
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(type (: CounterMsg (Union Inc (: Named string))))
|
|
(type (: Counter (Record (: count int) (: name string))))
|
|
|
|
(: (-> CounterMsg Counter Counter))
|
|
(defun (counter-update msg c)
|
|
(match msg
|
|
(Inc (record-set c (count (+ (.count c) 1))))
|
|
((Named s) (record-set c (name s)))))
|
|
|
|
(: (-> Counter (View CounterMsg)))
|
|
(defun (counter-view c)
|
|
(row (button "+" Inc) (text-input (.name c) Named) (text #"${(.count c)}")))
|
|
|
|
;; Two of them, each a case of the larger message.
|
|
(type (: PairMsg (Union (: Left CounterMsg) (: Right CounterMsg))))
|
|
(type (: Pair (Record (: left Counter) (: right Counter))))
|
|
|
|
(: (-> PairMsg Pair Pair))
|
|
(defun (pair-update msg p)
|
|
(match msg
|
|
((Left m) (record-set p (left (counter-update m (.left p)))))
|
|
((Right m) (record-set p (right (counter-update m (.right p)))))))
|
|
|
|
(: (-> Pair (View PairMsg)))
|
|
(defun (pair-view p)
|
|
(column (view-map Left (counter-view (.left p))) (view-map Right (counter-view (.right p)))))
|
|
|
|
;; And the whole app: the pair, mapped once more, and a canvas.
|
|
(type (: Msg (Union (: OfPair PairMsg) (: Down double double) (: Drag double double)
|
|
(: Up double double))))
|
|
(type (: Model (Record (: pair Pair) (: log (List string)))))
|
|
|
|
(: (-> Msg Model (Tuple Model (List (Event Msg)))))
|
|
(defun (gui-update msg m)
|
|
(defun (log s) (record-set m (log (cons s (.log m)))))
|
|
(next (match msg
|
|
((OfPair pm) (record-set m (pair (pair-update pm (.pair m)))))
|
|
((Down x y) (log #"down ${x} ${y}"))
|
|
((Drag x y) (log #"drag ${x} ${y}"))
|
|
((Up x y) (log #"up ${x} ${y}")))))
|
|
|
|
(: (-> Model (View Msg)))
|
|
(defun (view m)
|
|
(column
|
|
(view-map OfPair (pair-view (.pair m))) ; 0
|
|
(on-release
|
|
(on-move
|
|
(on-press
|
|
(canvas 200.0 100.0 #:background "white" ; 1
|
|
(rect 10.0 20.0 30.0 40.0 #:fill "red" #:stroke "black" #:radius 4.0) ; 1 0
|
|
(ellipse 50.0 10.0 20.0 20.0 #:fill "blue") ; 1 1
|
|
(line 0.0 0.0 80.0 60.0 #:stroke "green" #:stroke-width 3.0) ; 1 2
|
|
(path "M 100 10 L 140 10 L 120 40 Z" #:fill "orange") ; 1 3
|
|
(at-point 150.0 60.0 (text "hi"))) ; 1 4
|
|
Down)
|
|
Drag)
|
|
Up)))
|
|
|
|
(defun (main)
|
|
(def app (start-app (Model (pair (Pair (left (Counter (count 0) (name "")))
|
|
(right (Counter (count 0) (name "")))))
|
|
(log '()))
|
|
gui-update view (fun (c) unit)))
|
|
(defun (ctl path) (app-control-at app path))
|
|
(defun (click path)
|
|
(raise-event (cast Interactive (ctl path)) (RoutedEventArgs. (cast RoutedEvent click-event))))
|
|
(defun (both) (.pair (app-model app)))
|
|
(defun (left-of c) (left-of* (cast AvaloniaObject c)))
|
|
(defun (top-of c) (top-of* (cast AvaloniaObject c)))
|
|
|
|
;; --- view-map ---------------------------------------------------------------------
|
|
(click '(0 1 0))
|
|
(expect "a click in a mapped view, mapped twice" (.count (.right (both))) 1)
|
|
(expect "and not the other one" (.count (.left (both))) 0)
|
|
(box-text! (cast TextBox (ctl '(0 0 1))) "Ada")
|
|
(expect "a mapped function message" (.name (.left (both))) "Ada")
|
|
(click '(0 0 0))
|
|
(click '(0 0 0))
|
|
(expect "the view shows the component's model" (.count (.left (both))) 2)
|
|
|
|
;; --- shapes -----------------------------------------------------------------------
|
|
(def r (ctl '(1 0)))
|
|
(expect "a rectangle's place" (list (left-of r) (top-of r)) '(10.0 20.0))
|
|
(expect "its size" (width-of (cast Layoutable r)) 30.0)
|
|
(expect "its fill" (.ToString (cast Object (fill-of (cast Shape r)))) "Red")
|
|
(expect "a line's end" (point-x (line-end (cast LineShape (ctl '(1 2))))) 80.0)
|
|
(expect "its width" (stroke-width-of (cast Shape (ctl '(1 2)))) 3.0)
|
|
(expect "a path's geometry" (eq? (cast Object (path-data (cast PathShape (ctl '(1 3))))) (cast Object 0)) #f)
|
|
(expect "a view at a point" (left-of (ctl '(1 4))) 150.0)
|
|
|
|
;; --- pointer ----------------------------------------------------------------------
|
|
(def canvas-control (ctl '(1)))
|
|
(def pointer (cast IPointer (Pointer. 1 PointerType.Mouse #t)))
|
|
(defun (at x y) (Point. x y))
|
|
(raise-event (cast Interactive canvas-control)
|
|
(cast RoutedEventArgs (Pressed. (cast Object canvas-control) pointer (cast Visual canvas-control)
|
|
(at 30.0 40.0) 0UL (PointProps.) KeyModifiers.None 1)))
|
|
(expect "a press on the canvas" (.log (app-model app)) '("down 30 40"))
|
|
;; On a shape, the event comes from the shape, and the place is still the
|
|
;; canvas's.
|
|
(raise-event (cast Interactive r)
|
|
(cast RoutedEventArgs (Moved. (cast RoutedEvent moved-event) (cast Object r) pointer
|
|
(cast Visual canvas-control) (at 35.0 45.0) 1UL (PointProps.)
|
|
KeyModifiers.None)))
|
|
(expect "a move over a shape" (list-ref (.log (app-model app)) 0) "drag 35 45")
|
|
(raise-event (cast Interactive canvas-control)
|
|
(cast RoutedEventArgs (Released. (cast Object canvas-control) pointer (cast Visual canvas-control)
|
|
(at 36.0 46.0) 2UL (PointProps.) KeyModifiers.None
|
|
MouseButton.Left)))
|
|
(expect "a release" (list-ref (.log (app-model app)) 0) "up 36 46")
|
|
|
|
(println "bjogui canvas ok"))
|