run-app's #:windows gives the windows a model has. The app opens a window when it is first in the list, patches its view and title while it stays, and closes it when it is gone. A click on its close button sends its on-close message, and the model closes it. A #:modal window is a dialog over the main window, so a dialog of the program's own is a window whose state is in the model. Windows wait for the main window to be shown, because a dialog cannot belong to a hidden one, and the program ends when the main window closes. Key shortcuts and commands such as focus reach the other windows too, and #:screenshot saves a picture of each one beside the main window's. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8) Co-Authored-By: eca-agent <git@eca.dev>
108 lines
4.4 KiB
Text
108 lines
4.4 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/examples/contacts.bjo — a dialog of the program's own, and a
|
|
;; second window.
|
|
;;
|
|
;; bjo run examples/contacts.bjo
|
|
;;
|
|
;; "Edit" opens a dialog with the contact's name and address. What is typed
|
|
;; there is a draft in the model, so the dialog is a view like any other;
|
|
;; "Save" puts it back, and "Cancel", Escape or the close button drop it.
|
|
;; "Details" opens a window that stays open beside the main one, and follows
|
|
;; the contact chosen in the list.
|
|
;;
|
|
;; The windows are the model's: `windows` gives those it has, and the app
|
|
;; opens, changes and closes them to match.
|
|
|
|
(import (std prelude))
|
|
(import (bjogui view))
|
|
(import (bjogui app))
|
|
|
|
(type (: Contact (Record (: name string) (: address string))))
|
|
|
|
;; The contact being edited, and which one it is.
|
|
(type (: Draft (Record (: index int) (: contact Contact))))
|
|
|
|
(type (: Msg (Union (: Edit int) (: DraftName string) (: DraftAddress string) Save Cancel
|
|
(: Choose int) ToggleDetails)))
|
|
|
|
(type (: Model (Record (: contacts (Vec Contact)) (: draft (Option Draft)) (: chosen int)
|
|
(: details bool))))
|
|
|
|
(: gui-update (-> Msg Model (Tuple Model (List (Event Msg)))))
|
|
(defun (gui-update msg m)
|
|
(defun (edit-draft f)
|
|
(match (.draft m)
|
|
((Some d) (record-set m (draft (Some (record-set d (contact (f (.contact d))))))))
|
|
(None m)))
|
|
(match msg
|
|
((Edit i)
|
|
(next (record-set m (draft (Some (Draft (index i) (contact (vec-ref (.contacts m) i))))))
|
|
(focus "draft-name")))
|
|
((DraftName s) (next (edit-draft (fun (c) (record-set c (name s))))))
|
|
((DraftAddress s) (next (edit-draft (fun (c) (record-set c (address s))))))
|
|
(Save
|
|
(next (match (.draft m)
|
|
((Some d) (record-set m (contacts (vec-set (.contacts m) (.index d) (.contact d)))
|
|
(draft None)))
|
|
(None m))))
|
|
(Cancel (next (record-set m (draft None))))
|
|
((Choose i) (next (record-set m (chosen i))))
|
|
(ToggleDetails (next (record-set m (details (not (.details m))))))))
|
|
|
|
(: view (-> Model (View Msg)))
|
|
(defun (view m)
|
|
(column #:spacing 8.0 #:margin 16.0
|
|
(list-box (vec-map (fun (c) (text (.name c) #:margin 4.0)) (.contacts m)) (.chosen m) Choose
|
|
#:height 150.0)
|
|
(row #:spacing 8.0
|
|
(button "Edit" (Edit (.chosen m)) #:enabled (>= (.chosen m) 0) #:width 80.0)
|
|
(button (if (.details m) "Hide details" "Details") ToggleDetails))))
|
|
|
|
(: (-> Draft (View Msg)))
|
|
(defun (draft-view d)
|
|
(def c (.contact d))
|
|
(grid #:columns "Auto,250" #:rows "Auto,Auto,Auto" #:spacing 8.0 #:margin 16.0
|
|
(at 0 0 (text "Name"))
|
|
(at 0 1 (keyed "draft-name" (text-input (.name c) DraftName)))
|
|
(at 1 0 (text "Address"))
|
|
(at 1 1 (text-input (.address c) DraftAddress))
|
|
(at 2 1 (row #:spacing 8.0
|
|
(button "Save" Save #:width 80.0)
|
|
(button "Cancel" Cancel #:width 80.0)))))
|
|
|
|
(: windows (-> Model (List (WindowView Msg))))
|
|
(defun (windows m)
|
|
(def dialog
|
|
(match (.draft m)
|
|
((Some d) (list (window "edit" #"Edit ${(.name (vec-ref (.contacts m) (.index d)))}" Cancel
|
|
(draft-view d) #:modal #t)))
|
|
(None '())))
|
|
(def details
|
|
(if (and (.details m) (>= (.chosen m) 0))
|
|
(let ((c (vec-ref (.contacts m) (.chosen m))))
|
|
(list (window "details" "Details" ToggleDetails
|
|
(column #:spacing 4.0 #:margin 16.0
|
|
(text (.name c) #:size 18.0)
|
|
(text (.address c)))
|
|
#:width 260.0 #:height 110.0)))
|
|
'()))
|
|
(list-append dialog details))
|
|
|
|
;; Escape and Enter are the dialog's while it is open.
|
|
(: keys (-> Model (List (Key Msg))))
|
|
(defun (keys m)
|
|
(match (.draft m)
|
|
((Some _) (list (key "Escape" Cancel) (key "Enter" Save)))
|
|
(None '())))
|
|
|
|
(defun (main)
|
|
(run-app (Model (contacts [(Contact (name "Ada Lovelace") (address "12 St James's Square, London"))
|
|
(Contact (name "Alan Turing") (address "Hollymeade, Wilmslow"))
|
|
(Contact (name "Grace Hopper") (address "Arlington, Virginia"))])
|
|
(draft None) (chosen 0) (details #f))
|
|
gui-update view
|
|
#:windows windows #:keys keys
|
|
#:title "Contacts" #:width 360.0 #:height 260.0))
|