Skip to content

Instantly share code, notes, and snippets.

@joinr
Created January 8, 2019 19:32
Show Gist options
  • Save joinr/039e2b182e5cdba308770cadf73ce7cb to your computer and use it in GitHub Desktop.
Save joinr/039e2b182e5cdba308770cadf73ce7cb to your computer and use it in GitHub Desktop.
an extend example of the fn-fx form example, this time with a timer and a label that updates the model (reflected in the UI) programatically.
(ns getting-started.reactlike
(:require [fn-fx.fx-dom :as dom]
[fn-fx.diff :refer [component defui render should-update?]]
[fn-fx.controls :as ui]))
(def firebrick
(ui/color :red 0.69 :green 0.13 :blue 0.13))
;; The main login window component, notice the authed? parameter, this defines a function
;; we can use to construct these ui components, named "login-form"
(defui LoginWindow
(render [this {:keys [authed? label]}]
(ui/grid-pane
:alignment :center
:hgap 10
:vgap 10
:padding (ui/insets
:bottom 25
:left 25
:right 25
:top 25)
:children [(ui/text
:text "Welcome"
:font (ui/font
:family "Tahoma"
:weight :normal
:size 20)
:grid-pane/column-index 0
:grid-pane/row-index 0
:grid-pane/column-span 2
:grid-pane/row-span 1)
(ui/label
:text "User:"
:grid-pane/column-index 0
:grid-pane/row-index 1)
(ui/text-field
:id :user-name-field
:grid-pane/column-index 1
:grid-pane/row-index 1)
(ui/label :text "Password:"
:grid-pane/column-index 0
:grid-pane/row-index 2)
(ui/password-field
:id :password-field
:grid-pane/column-index 1
:grid-pane/row-index 2)
(ui/h-box
:spacing 10
:alignment :bottom-right
:children [(ui/button :text "Sign in"
:on-action {:event :auth
:fn-fx/include {:user-name-field #{:text}
:password-field #{:text}}})]
:grid-pane/column-index 1
:grid-pane/row-index 4)
(ui/text
:text (if authed? "Sign in was pressed" "")
:fill firebrick
:grid-pane/column-index 1
:grid-pane/row-index 6)
(ui/text
:text (or label "No label present!")
:fill firebrick
:grid-pane/column-index 1
:grid-pane/row-index 7)])))
;; Wrap our login form in a stage/scene, and create a "stage" function
(defui Stage
(render [this args]
(ui/stage
:title "JavaFX Welcome"
:shown true
:scene (ui/scene
:root (login-window args)))))
(def app-state (atom nil))
(defn reset-state! []
(do (reset! app-state {:authed? false})
app-state))
(defn set-label! [x]
(swap! app-state assoc :label x))
(defn -main []
(let [;; Data State holds the business logic of our app
;; now it points to the global app-state....
data-state (reset-state!)
;; handler-fn handles events from the ui and updates the data state
handler-fn (fn [{:keys [event] :as all-data}]
(println "UI Event" event all-data)
(case event
:auth (swap! data-state assoc :authed? true)
(println "Unknown UI event" event all-data)))
;; ui-state holds the most recent state of the ui
ui-state (agent (dom/app (stage @data-state) handler-fn))]
;; Every time the data-state changes, queue up an update of the UI
;; Transitively, we can push changes to the DOM by munging with
;; the global app state now.
(add-watch data-state :ui (fn [_ _ _ _]
(send ui-state
(fn [old-ui]
(dom/update-app old-ui (stage @data-state))))))))
(comment
(-main)
;;Let's see what happens when we change the time....
(def res (future
(while true
(do (set-label! (str (System/currentTimeMillis) " ms"))
(Thread/sleep 16)))))
;;we're now propogating changes to our model just modifying the
;;atom from another thread. You can do this a number of ways,
;;uncluding core.async. I used futures...
;;to stop the counter (manually), cancel the future.
;;we'd probably use channels or something else for this.
#_(future-cancel res))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment