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