cljfx

https://github.com/cljfx/cljfx
defa 2021-01-05T12:45:47.229300Z

I’m trying to figure out a good workflow to develop a UI with cljfx in IntelliJ IDEA with the CrusiveIDE plugin… is there a way to update/reload the UI when the corresponding source file is saved or when a component gets compiled? I understand that I can call (renderer) to “reload” the UI after changing the code but it would be nice if that could happen automatically.

vlaaad 2021-01-05T12:48:24.230500Z

I also use (swap! *state identity) to touch state atoms, that triggers a normal update (e.g. not a full re-create)

defa 2021-01-05T12:57:35.231800Z

Thanks, @vlaaad, I’ll try that it looks like I have something set up the wrong way…

defa 2021-01-05T13:07:45.234300Z

I do something like this:

(defonce *state
  (atom {:app-title "Proto App"}))

(defn root [{:keys [app-title]}]
  {:fx/type :stage
   :showing true
   :title   app-title
   ;...
   })

(defonce renderer
  (fx/create-renderer
    :middleware (fx/wrap-map-desc assoc :fx/type root)))

(fx/on-fx-thread
  (fx/mount-renderer *state renderer))
And updating *state with (swap! *state assoc-in [:app-title] "Foo") changes the title. But when changing and compiling root those changes will not take effect on the next (swap! *state …). While tying, I think I know what’s wrong…

defa 2021-01-05T13:13:58.235Z

… the renderer is not aware of the “new” root:thinking_face:

vlaaad 2021-01-05T13:14:29.235600Z

do you use (fx/wrap-map-desc #'root) or (fx/wrap-map-desc root) ?

vlaaad 2021-01-05T13:14:44.235800Z

former should work

defa 2021-01-05T13:20:36.236200Z

When defining the renderer like this:

(def renderer
  (fx/create-renderer
    :middleware (fx/wrap-map-desc assoc :fx/type #'root)))
I get:
java.lang.IllegalArgumentException: No implementation of method: :create of protocol: #'cljfx.lifecycle/Lifecycle found for class: clojure.lang.Var
	at clojure.core$_cache_protocol_fn.invokeStatic(core_deftype.clj:583)
	at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:575)
	at cljfx.lifecycle$eval2423$fn__2445$G__2410__2454.invoke(lifecycle.clj:17)
	at cljfx.lifecycle$create_dynamic_component.invokeStatic(lifecycle.clj:32)
	at cljfx.lifecycle$create_dynamic_component.invoke(lifecycle.clj:29)
	at cljfx.lifecycle$reify__2495.create(lifecycle.clj:39)
	at cljfx.lifecycle$fn__2499.invokeStatic(lifecycle.clj:58)
	at cljfx.lifecycle$fn__2499.invoke(lifecycle.clj:56)
	at cljfx.lifecycle$eval2423$fn__2445$G__2410__2454.invoke(lifecycle.clj:17)
	at cljfx.lifecycle$wrap_map_desc$fn__2713.invoke(lifecycle.clj:446)
	at cljfx.lifecycle$eval2423$fn__2445$G__2410__2454.invoke(lifecycle.clj:17)
	at cljfx.renderer$render_component.invokeStatic(renderer.clj:57)
	at cljfx.renderer$render_component.invoke(renderer.clj:47)
	at cljfx.renderer$create$fn__3047.invoke(renderer.clj:77)
	at cljfx.renderer$perform_render$fn__2998.invoke(renderer.clj:23)
	at cljfx.renderer$perform_render.invokeStatic(renderer.clj:22)
	at cljfx.renderer$perform_render.invoke(renderer.clj:14)
	at cljfx.renderer$request_render$fn__3024$fn__3028.invoke(renderer.clj:44)
	at cljfx.renderer$request_render$fn__3024.invoke(renderer.clj:44)
	at clojure.lang.AFn.run(AFn.java:22)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)

vlaaad 2021-01-05T13:23:47.238600Z

ah, no, cljfx doesn’t accept vars as fx/type by default, I meant (fx/wrap-map-desc #'root) which calls map fn outside of cljfx lifecycle. The thing is, you need to have clojure code that has var dereferencing at call site for var reloading to work, and (fx/wrap-map-desc assoc :fx/type root) does dereferencing only on call to fx/wrap-map-desc

vlaaad 2021-01-05T13:24:13.239200Z

in your case (fx/wrap-map-desc #(assoc % :fx/type root)) should also work

defa 2021-01-05T13:25:22.239800Z

Okay thanks. I’ll try that and then I’ll try to understand you explanaiton 😉