Fork me on GitHub
#cljfx
<
2020-10-01
>
ribelo08:10:03

Unfortunately, I did not find information in the examples or in the source code, so I will ask. Is it possible to fire several events at once? I mean something like :dispatch-n in re-frame

vlaaad08:10:56

@huxley yep, possible: effects are treated as a collection of tuples, it doesn’t have to be a map, e.g. [[:dispatch ...] [:dispatch ...]]

ribelo08:10:31

@vlaaad Can I find a code example somewhere?

vlaaad09:10:46

I don’t think there are code examples, just return a coll of tuples instead of a map in event handler 🙂

ribelo09:10:26

It took me a while, but I think I understand ; )

👍 3
ribelo09:10:09

I did something similar, but I thought that maybe there is a more obvious solution, for example something like that.

ribelo09:10:35

{:fx/type          :button
  :text            "button"
  :on-action       [{:event/type :evt-1}
                    {:event/type :evt-2}
                    {:event/type :evt-3}]}

vlaaad09:10:16

ah, no, there is nothing like that unfortunately, you can only have a single event in description

ribelo09:10:01

Okay, it's awesome anyway. 😉

vlaaad09:10:03

I have an issue to make event handlers more free-form: https://github.com/cljfx/cljfx/issues/48

vlaaad09:10:21

but I’m not working on it 🙂

vlaaad09:10:14

I’m not doing anything about it because it’s wasn’t that big of an issue in practice

ribelo09:10:29

Thanks for your help and your time

vlaaad09:10:44

you are welcome 🙂

danielsz12:10:17

As documented in https://github.com/cljfx/cljfx#aot-compilation-is-complicated, the classes from javafx.scene.control require JavaFX runtime to be running by accessing it in Control's static initializer. I've encountered a variation on this issue that manifests itself in a nREPL session. After initializing the toolkit, any code running on the Javafx Application Thread that references a control throws an error. Exception in thread "JavaFX Application Thread" java.lang.NoClassDefFoundError: Could not initialize class javafx.scene.control.Label The following code block runs fine because it doesn't reference a control.

(let [stage (Stage.)
      stack-pane (StackPane.)
      scene (Scene. stack-pane 640 480)]
      (.setScene stage scene)
      (.show stage))
An empty window appears. However, the following code block throws the aforementioned error because it references a control.
(let [stage (Stage.)
      label (Label. "Hello") 
      stack-pane (StackPane. (into-array Node [^Label label]))
      scene (Scene. stack-pane 640 480)]
      (.setScene stage scene)
      (.show stage))  
To be clear, this issue has nothing to do with cljfx. Most likely it is a problem with nREPL and its class loading. The problem doesn't appear with inf-clojure either. I'm asking it here in case this resonates with someone.

vlaaad12:10:04

java 11? call (javafx.application.Platform/startup #()) before importing classes from javafx.scene.control package

danielsz12:10:22

Yes, of course. I do that.

vlaaad12:10:59

just to be clear — you do it before import, not before creating instance?

vlaaad12:10:35

unfortunately, I can’t help with nrepl as I don’t use it

danielsz12:10:06

The import statement are in the ns declaration, the platform initialization is a top-level definition.

vlaaad12:10:26

top-level definition before ns call?

danielsz12:10:03

No, it's a function that runs on the nREPL, so it runs after the import.

vlaaad12:10:22

hm, I would guess this is the problem — JavaFX runtime has to be started before javafx.scene.control.* classes are imported

danielsz12:10:37

I tried at the REPL, initialized the JavaFX runtime, then imported the control at the REPL, still the same error.

vlaaad12:10:12

hmm, actually just running (import 'javafx.scene.control.Label) does not fail in a freshly-started repl with JavaFX on the classpath

vlaaad12:10:27

can you share the whole stacktrace?

danielsz12:10:54

Exception in thread "JavaFX Application Thread" java.lang.NoClassDefFoundError: Could not initialize class javafx.scene.control.Label
        at caonima.repl$foo$f__15007__auto____15021.invoke(repl.clj:35)
        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)
        at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
        at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
        at java.base/java.lang.Thread.run(Thread.java:832)

vlaaad12:10:54

hmm, and that’s it? no causes? no extra output in stderr?

danielsz12:10:12

No. It's definitely a nREPL specific problem, just asking if anyone has seen this before.

danielsz12:10:38

nREPL is modifying the class loading mechanism.

danielsz12:10:29

And to be clear, I'm not using cljfx neither. It's just plain interop.

vlaaad12:10:49

yeah, I see 🙂

scythx12:10:18

Hello, i want to create menu-item to use file chooser, so i modified the root-view of https://github.com/cljfx/cljfx/blob/master/examples/e33_file_chooser.clj But for the menu-item it gave me

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: No matching field found: getScene for class javafx.scene.control.MenuItem
(defn root-view [{:keys [image-path content]}]
  {:fx/type :stage
   :title "Textual file viewer"
   :showing true
   :width 800
   :height 600
   :scene {:fx/type :scene
           :root {:fx/type :v-box
                  :children [{:fx/type :menu-bar
                              :menus [{:fx/type :menu
                                       :text "File"
                                       :items [{:fx/type :menu-item
                                                :text "Open image ..."
                                                :on-action {::event ::open-image}}]}]}
                             ;; The button works but not for the menu above
                             {:fx/type :h-box
                              :spacing 15
                              :alignment :center-left
                              :children [{:fx/type :button
                                          :text "Open file..."
                                          :on-action {::event ::open-image}}
                                         {:fx/type :label
                                          :text (str image-path)}]}]}}})

scythx12:10:35

Am i missing something?

vlaaad12:10:09

day of cljfx issues 😄

parrot 12
vlaaad12:10:05

@danielsz just in case — the problems with JavaFX initialization and controls I had had to do with this code in Control static initializer: https://github.com/openjdk/jfx/blob/d10f948ee7380ac73bc4e2d5bff1caba50fe00a8/modules/javafx.controls/src/main/java/javafx/scene/control/Control.java#L98-L100

danielsz12:10:26

Yes, that is indeed why the JavaFx runtime needs to be running when compiling those classes. You've explained it nicely in the README. I thank you for that. Have you noticed what happens https://github.com/openjdk/jfx/blob/d10f948ee7380ac73bc4e2d5bff1caba50fe00a8/modules/javafx.controls/src/main/java/javafx/scene/control/Control.java#L119 in that same class? It's a class loading hack. nREPL does its own class loading hacks and I suspect they don't see eye to eye.

vlaaad12:10:38

oh right, it’s mentioned in the readme 😄

vlaaad12:10:56

haven’t seen that, definitely looks like a classloader hack 🙂

danielsz12:10:09

Yup, all bets are off at that point. 😕

danielsz12:10:33

nREPL is guilty of the same folly.

danielsz12:10:45

Here's the interesting thing. A "hello world" program with cljfx works at a nREPL.

danielsz12:10:24

Including a Label control.

danielsz12:10:16

My super basic interop throws this class not found error. And the maddening thing is: not all the time.

danielsz12:10:45

Sometimes it all works.

vlaaad12:10:47

¯\(ツ)

vlaaad12:10:12

cljfx FTW 😄

danielsz12:10:15

Thanks for your patience,.

danielsz12:10:56

I'll take this opportunity to congratulate you on the work. I've been reading the source code. Very elegant. Kudos!

vlaaad12:10:17

thanks for the compliment! ^_^

vlaaad12:10:20

perhaps setting default user agent stylesheet might help with nrepl ¯\(ツ)

vlaaad12:10:55

@raefaldhiamartya can you share the whole stacktrace?

vlaaad12:10:24

do you call .getScene somewhere?

scythx12:10:47

yes getscene only in that code, here's all i have

scythx12:10:47

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: No matching field found: getScene for class javafx.scene.control.MenuItem
	at clojure.lang.Reflector.getInstanceField(Reflector.java:397)
	at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:440)
	at clojure.io.github.raefaldhia.image_processing.core$eval17490$fn__17492.invoke(core.clj:13)
	at clojure.lang.MultiFn.invoke(MultiFn.java:229)
	at cljfx.event_handler$wrap_co_effects$fn__13793.invoke(event_handler.clj:10)
	at cljfx.event_handler$wrap_effects$dispatch_sync_BANG___13806.invoke(event_handler.clj:27)
	at cljfx.event_handler$wrap_effects$dispatch_sync_BANG___13806.invoke(event_handler.clj:25)
	at cljfx.lifecycle$make_handler_fn$fn__13457.invoke(lifecycle.clj:128)
	at cljfx.coerce$event_handler$reify__13022.handle(coerce.clj:135)
	at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
	at javafx.event.Event.fireEvent(Event.java:198)
	at javafx.scene.control.MenuItem.fire(MenuItem.java:459)
	at com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1380)
	at com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.lambda$createChildren$12(ContextMenuContent.java:1333)
	at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
	at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
	at javafx.event.Event.fireEvent(Event.java:198)
	at javafx.scene.Scene$MouseHandler.process(Scene.java:3890)
	at javafx.scene.Scene.processMouseEvent(Scene.java:1885)
	at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2618)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
	at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
	at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
	at com.sun.glass.ui.View.notifyMouse(View.java:942)
	at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
	at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
	at java.base/java.lang.Thread.run(Thread.java:834)

vlaaad12:10:10

Looks like MenuItem does not have .getScene method that is called on an event target by the handler

vlaaad12:10:16

and you need to pass a parent window to .showOpenDialog

vlaaad12:10:38

Either you should find another way to get a hold on the window from the event, or you should use (javafx.stage.Window/getWindows) to get a window from there

thanks 3
scythx12:10:09

ahhh alright then, thank you!