1

I want to get this behavior: when the button is pressed with user the button's text and the label's text should are changed together. But the problem is button and label have the equal name of keys for the text properties. And I can't store equal keys in one hash-map.



;;I have two atoms keeps the state of button text and state of label text 
(def *button-text (atom 
                   {:text "click me"}))

(def *label-text (atom 
                   {:text "press the button"}))

;;I have the root function which should accepts arguments for button and label props.
But these props have equal names  - text - and I can't store two equal keys in one map.

{:fx/type root
 :text (:text *button-text)
 :text (:text *label-text)}

;;This will cause an error.

This is how I solved this problem. But is too much of the code and out of normal way.

(ns examp.core
  (:gen-class)
  (:require [cljfx.api :as fx])
  (:import [javafx.application Platform]))

(def *button-text (atom 
                   {:text "click me"}))

(def *label-text  (atom 
                   {:text "press the button"}))


(def renderer (fx/create-renderer))


(defn root [{:keys [one two]}]
  (let [button-text  (:text one)
        label-text (:text two)]
  {:fx/type :stage
   :showing true
   :title "Window"
   :width 250
   :height 150
   :scene {:fx/type :scene
           :root {:fx/type :v-box
                  :alignment :center
                  :spacing 10
                  :children [{:fx/type :label
                              :text label-text}
                             {:fx/type :button
                              :min-width 100
                              :min-height 50
                              :text button-text
                              :on-action (fn [_]
                                           (if (= button-text "click me")
                                             (do 
                                               (swap! *button-text assoc :text "clicked")
                                               (swap! *label-text assoc :text "button is pressed")
                                               (renderer
                                                 {:fx/type root
                                                  :one @*button-text
                                                  :two @*label-text}))
                                             (do 
                                               (swap! *button-text assoc :text "click me")
                                               (swap! *label-text assoc :text "presse the button")
                                               (renderer
                                                 {:fx/type root
                                                  :one @*button-text
                                                  :two @*label-text}))))}]}}}))
                                             



(defn -main [& args]
  (Platform/setImplicitExit true)
  (renderer {:fx/type root 
             :one @*button-text
             :two @*label-text}))








akond
  • 15,865
  • 4
  • 35
  • 55
user324885
  • 21
  • 2
  • 1
    Please add a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) and expected new text. Are you just toying or is there some specific problem you are trying to solve? – Martin Půda Jan 13 '23 at 14:06
  • I also would not usually bother with mutable state just for some button text.... – Jared Smith Jan 13 '23 at 14:38
  • @Martin Půda I just want to understand how cljfx works in every aspect. I know it easier to use direct java calls but I want to write in Clojure most of the time not Java, I know it easier to use direct java calls but I want to write in Clojure most of the time not Java. I edited the question and added full example. – user324885 Jan 13 '23 at 16:28
  • @Jared Smith this example is from Cljfx manual - to use atom for some mutable state of component – user324885 Jan 13 '23 at 16:31
  • Hmmm... I don't want to disagree with the library authors, and I would indeed use an `atom` for local component state BUT... I don't think you want arbitrary strings as your button text. You are probably going to have a limited number of strings for the text based on some state that could be an index into a map of text strings. YMMV – Jared Smith Jan 13 '23 at 17:45
  • Jared Smith thanks, bro. I agree with you. – user324885 Jan 16 '23 at 08:42

1 Answers1

1

Before I will show you my attempt, here is the link to the official Cljfx examples repository. These examples should be useful for you, as they show practices for managing app state, event handling and so on.

For this situation, I recommend studying e05_fn_fx_like_state.clj- this is also an example I based my code on:

(ns examp.core
  (:require [cljfx.api :as fx])
  (:gen-class))

(def *state
  (atom {:label-text  "Click the button."
         :button-text "Click me!"}))

(defn root [{:keys [label-text button-text]}]
  {:fx/type :stage
   :showing true
   :title   "Window"
   :width   250
   :height  150
   :scene   {:fx/type :scene
             :root    {:fx/type   :v-box
                       :alignment :center
                       :spacing   10
                       :children  [{:fx/type :label
                                    :text    label-text}
                                   {:fx/type    :button
                                    :text       button-text
                                    :min-width  100
                                    :min-height 50
                                    :on-action  {:key :button-action}}]}}})

(defn map-event-handler [event]
  (when (= (:key event) :button-action)
    (if (= (:button-text @*state) "Click me!")
      (reset! *state {:label-text  "The button was clicked!"
                      :button-text "Clicked!"})
      (reset! *state {:label-text  "Click the button."
                      :button-text "Click me!"}))))

(def renderer
  (fx/create-renderer
    :middleware (fx/wrap-map-desc root)
    :opts {:fx.opt/map-event-handler map-event-handler}))

(defn -main [& args]
  (fx/mount-renderer *state renderer))
Martin Půda
  • 7,353
  • 2
  • 6
  • 13