2

I've been trying to get http://docs.oracle.com/javafx/2/get_started/fxml_tutorial.htm running in clojure.

I discovered that by omitting the @FXML annotation in the java version and making things public, from:

public class FXMLExampleController {
    @FXML private Text actiontarget; 
    @FXML protected void handleSubmitButtonAction(ActionEvent event) {
        actiontarget.setText("Sign in button pressed");
    }
} 

to:

public class FXMLExampleController {
    public Text actiontarget;  
    public void handleSubmitButtonAction(ActionEvent event) {
        actiontarget.setText("Sign in button pressed");
    }
}

...that it basically still works when I click the button, and fxml is able to get to the controller's public Text actiontarget, whose access is usually enabled only via the @FXML annotation.

So I'm trying to make my clojure-based controller class have public mutable fields, but in the last several hours of hunting through :gen-class and deftypes, I can't find a way to make it work. I was able to access final (default) deftype fields from a java test code, but the only online discussion I've seen says you can't have public and mutable fields, and to try :gen-class. Well, I can't find that in gen-class either, and all the gen-class examples I've been able to find use the class fields only from within clojure; I'm not sure how to define the :state in a :gen-class such that it is accessible from java, and I don't know how to make those mutable and public.

My next thing is to try clojure annotations, and then using the fx:script field rather than fx:controller to define a clojure callback... but I want to make sure it's doable/not-doable with deftype or gen-class first.

So can someone tell me if it's possible to make a java-accessible class with public mutable fields in clojure?

Thanks.

Sonicsmooth
  • 2,673
  • 2
  • 22
  • 35

2 Answers2

3

No, you cannot define public mutable fields in Clojure. That applies to both deftype and gen-class.

I suppose you could try and find out whether JavaFX would be happy to call a getter and, if so, define some getFoo methods in Clojure instead.

Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
  • Thanks, that's the feeling I was getting. It occurred to me maybe there is some other way to get fxml to talk to java objects via properties, setters, getters, etc., but I haven't found that way yet. – Sonicsmooth May 20 '13 at 00:38
  • You can just add the same @FXML annotation now (it was probably not possible at the time of writing) – Rick77 Aug 27 '16 at 19:48
1

Quite late as an answer, but if I found this question other people might as well... :)

I have stumbled in the same very problem of the OP (that is, I'm trying to duplicate in clojure the FXML example in the javafx tutorial) and this is my solution:

1) Create an interface with all methods required in my FXML for event-driven interaction (just one in my case, which mirrors the method defined in the controller in the mid of the example):

(definterface MyFXMLControllerMethods                                                                                                                 
  (^void handleSubmitButtonAction (^javafx.event.ActionEvent event )))

2) then use deftype to instantiate a new java type implementing the interface and, that's the nifty thing, defining a private mutable field with the @FXML annotation:

(deftype MyFXMLController [^{:volatile-mutable true javafx.fxml.FXML true} actiontarget]
    MyFXMLControllerMethods
        (^void handleSubmitButtonAction [this ^javafx.event.ActionEvent event]
            (.setText actiontarget "Sign in button pressed!"))
    Initializable
        (^void initialize [this ^java.net.URL location ^java.util.ResourceBundle resources]
          (assert (not= actiontarget nil)))) ; this test passes! =)

which, as the OP correctly pointed out, is the key to allow the FXML loader to access the private fields of the controller through introspection.

What's really nice about this approach, is that it doesn't require static compilation so, besides the Application class, everything else can be dynamic.

Other solutions might involve initializing each field manually with lookup (works, I tried) or using introspection (didn't try that).

Hope this helps

Rick77
  • 3,121
  • 25
  • 43