cljfx

Ken Huang 2026-01-30T08:18:29.018869Z

Hey guys! I work on Linux and have an minor but a bit annoying issue regarding the interactive development workflow. All the UI got updated after I evaluated (renderer), but the app window would become a small one, I had to maximize it or drag the window to get larger. Is it possible to get rid of this? thanks.

vlaaad 2026-01-30T09:03:20.960779Z

Hi Ken! BTW, I don’t use renderer nowadays, ext-watcher replaced it for me. Typically, I trigger a refresh by changing the state atom..

Ken Huang 2026-01-30T09:07:00.149699Z

Interesting, the way you describe sounds better than renderer, as I have to trigger it manually.

Ken Huang 2026-01-30T09:07:10.500079Z

Thanks Vlad!

πŸ‘ 1
Ken Huang 2026-01-30T09:09:20.212859Z

But ... it's rarely mentioned in the docs. Here is what AI gives me:

;; Example from cljfx documentation (simplified)
(require '[cljfx.api :as fx])

(def *state (atom {:current-value 0}))

(defn my-component [_]
  {:fx/type fx/ext-watcher
   :ref *state
   ::fx/type :label
   :text (str "Current state value: " (:current-value @*state))})

;; When *state is updated elsewhere (e.g., (swap! *state update :current-value inc)),
;; the label text will automatically update.
It seems this component would be my root, right?

vlaaad 2026-01-30T09:09:53.108459Z

ai fails you here unfortunately

Ken Huang 2026-01-30T09:11:27.486939Z

fine 😞 I'm also searching cljfx's code base, trying to figure out something

vlaaad 2026-01-30T09:12:42.439049Z

(defn view [{:keys [value]}]
  {:fx/type :label
   ;:alignment :center
   :text (str value)})

(def state (atom 0))

#_(swap! state inc)

(fx/instance
  @(fx/on-fx-thread
     (fx/create-component
       {:fx/type :stage
        :showing true
        :width 500
        :height 600
        :scene {:fx/type :scene
                :root {:fx/type fx/ext-watcher
                       :ref state
                       :desc {:fx/type #'view}}}}
       {:fx.opt/type->lifecycle (some-fn fx/keyword->lifecycle #(when (ifn? %) fx.lifecycle/dynamic-fn->dynamic))})))

vlaaad 2026-01-30T09:13:37.080939Z

try this example, then uncomment :alignment :center, then, to refresh the view, do (swap! state inc)

πŸ‘ 1
Ken Huang 2026-01-30T09:25:21.523609Z

Thanks! I'm trying to adapt the code to my existing app, and see if I can make it.

Ken Huang 2026-01-30T09:44:50.949349Z

OMG, it works!

πŸ”₯ 1
Ken Huang 2026-01-30T09:45:42.195879Z

And it doesn't have the original annoying window problem

πŸ’― 1
Ken Huang 2026-01-30T09:46:01.842019Z

Huge thanks @vlaaad

vlaaad 2026-01-30T09:46:09.985839Z

my pleasure!

Ken Huang 2026-01-30T09:54:50.506929Z

I'm wondering, maybe it's better to replace the "interactive dev example" with this approach? It's data driven and we don't have to execute (renderer) manually every now and then.

Ken Huang 2026-01-30T09:57:20.345129Z

PS, does it work to apply ext-watcher to the :stage component? It will be perfect as all the ui elements will be under the control of this watcher.

vlaaad 2026-01-30T09:57:43.711679Z

yeah, you can use it as a root

Ken Huang 2026-01-30T10:03:12.903519Z

NICE! just tweaked my code a bit that way.

Ken Huang 2026-03-01T05:07:19.699629Z

@vlaaad Hi! Is it possible to use ext-many with ext-watcher? I had no luck making it work, it threw an exception: java.lang.IllegalArgumentException: Key must be integer Here is my minimal example:

;;; aka multiple stages
(ns e10-multiple-windows
  (:require [cljfx.api :as fx])
  (:import [javafx.stage Screen]))

(defn window [{:keys [x y width height]}]
  {:fx/type :stage
   :always-on-top true
   :showing true
   :x x
   :y y
   :width width
   :height height
   :scene {:fx/type :scene
           :root {:fx/type :v-box
                  :alignment :center
                  :children [{:fx/type :label
                              :text (str "Window at [" x ", " y "] "
                                         "with size " width "x" height)}]}}})

(def width 300)

(def height 100)

(def bottom
  (-> (Screen/getPrimary)
      .getBounds
      .getHeight
      (- height)))

(def right
  (-> (Screen/getPrimary)
      .getBounds
      .getWidth
      (- width)))

(def *state (atom {:x 300 :y 300
                   :width width :height height}))

(defn window-dispatcher [{state :value}]
  #_                                    ; works
  (window state)
  #_                                    ; works
  (merge {:fx/type #'window}
         state)
  ;; #_
  {:fx/type fx/ext-many
   :desc [{:fx/type window
           :x 0
           :y 0
           :width width
           :height height}
          {:fx/type window
           :x 0
           :y bottom
           :width width
           :height height}
          {:fx/type window
           :x right
           :y 0
           :width width
           :height height}
          {:fx/type window
           :x right
           :y bottom
           :width width
           :height height}]})

(fx/on-fx-thread
  (fx/create-component
   {:fx/type fx/ext-watcher
    :ref *state
    :desc #_{:fx/type #'window-dispatcher}
      {:fx/type fx/ext-many
   :desc [{:fx/type window
           :x 0
           :y 0
           :width width
           :height height}
          {:fx/type window
           :x 0
           :y bottom
           :width width
           :height height}
          {:fx/type window
           :x right
           :y 0
           :width width
           :height height}
          {:fx/type window
           :x right
           :y bottom
           :width width
           :height height}]}}
   ;; #_
   {:fx.opt/type->lifecycle (some-fn fx/keyword->lifecycle
                                     ;; fx/fn->lifecycle
                                     #(when (ifn? %)
                                        cljfx.lifecycle/dynamic-fn->dynamic))}))

Ken Huang 2026-03-01T05:10:50.060999Z

I need a fullscreen transparent window, but it wasn't transparent in fullscreen mode. And I figured that having :style :transparent to :stage can work around it, but I also wanted a :decorated one. So I thought maybe having two :stage windows could fix it.

Ken Huang 2026-02-01T04:45:59.968219Z

Hey @vlaaad, I created a pr for updating the interactive dev example at https://github.com/cljfx/cljfx/pull/193 , and a short demo video as well: https://www.youtube.com/watch?v=efnNg4AHKtU What do you think?

Ken Huang 2026-03-03T11:48:51.240329Z

Hi Vlad, I've fixed the above issue by removing #' and replacing cljfx.lifecycle/dynamic-fn->dynamic with fx/fn->lifecycle

Zubair Ahmed 2026-01-30T00:58:56.440979Z

hey, was working on a project and couldn't find anything for javafx to use fontawesome 7 icons, so i made a minimal lib for cljfx https://clojars.org/org.clojars.zubad/clj-font-awesome

πŸ”₯ 2