Fork me on GitHub
#cljfx
<
2021-01-05
>
defa12:01:47

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.

vlaaad12:01:24

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

defa12:01:35

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

defa13:01:45

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…

defa13:01:58

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

vlaaad13:01:29

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

vlaaad13:01:44

former should work

defa13:01:36

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)

vlaaad13:01:47

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

vlaaad13:01:13

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

defa13:01:22

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