Giter Club home page Giter Club logo

fn-fx's Introduction

Build Status JavaFX build on Clojars OpenJFX build on Clojars

Assistance / contributions welcome! Please see the contribution guide for more details.

fn(fx)

This library provides a functional, declarative wrapper around JavaFX / OpenJFX. The goals are to provide a "Virtual DOM" interface over the OOP mutability JavaFX / OpenJFX embraces.

Rationale

While the web has taken over many aspects of GUI programming that normally would have been implemented in JavaFX / OpenJFX, it's still important to recognize that a certain amount of complexity is involved in adopting a web based GUI. Programmers must now write in several other languages, setup web servers, and handle network data transfers, when all that was required was a GUI to some backend process. Sometimes a desktop UI really is the simplest option.

However, clojure developers have traditionally shied away from adopting technologies such as Swing and JavaFX / OpenJFX for fear of delving into the mess of mutability that is GUI programming.

This is the niche that fn(fx) attempts to fill: providing a functional interface over JavaFX / OpenJFX.

Basic Overview

fn(fx) requires that users express their UI via data, and calls to a function known as "ui". This function constructs a quasi-immutable datastructure that can easily be diffed against other components. We say "quasi-immutable", since some of the fields on the structure are mutated, but only once, from nil to a known value, never from a value to another value. This tree of components can then be handled by several functions:

  • (fn-fx.fx-dom/render component event-callback) - This function takes a virtual dom (component tree) and renders it, returning an opaque structure that can be used to later update the UI with a new virtual dom. event-callback is a function that will be handed events from the UI, more on that later.
  • (fn-fn.fx-dom/update-dom prev-state new-dom) - Given a value returned from a previous call to render or update-dom this function will diff new-dom against the dom used to create prev-state the resulting diff will be used to make minimal changes to the UI where required.

Event handling

Events are data, and are attached to components where EventHandler instances would normally be used. Thus creating a button with the property :on-action {:event :clicked!} would result in a button that sent {:event :clicked!} to the event-callback handed to the initial call to render.

User components

The defui macro generates a "user component" that is not a UI component, but a rendering function, and an optional differ function. The render method on this component is only invoked when the properties to the component change. defui is most often used to optimize re-rendering as whole sections of the UI can be ignored during rendering and diffing if the properties of the component haven't changed since the last render cycle.

Usage

Tested Versions

Before using fn-fx, please make sure you're using a JRE version that has been tested and is known to work. Here's the current test matrix:

JRE 1.8 (Oracle) JRE 1.8 (OpenJDK) JRE 11 (Oracle) JRE 11 (OpenJDK)
Clojure 1.7.0 1 1,2 1 1
Clojure 1.8.0 2
Clojure 1.9.0 2
Clojure 1.10.0 2

1 For now we've decided to only test back as far as Clojure 1.8.0. If anyone needs this tested on older versions of Clojure, PRs are welcome!

2 Currently, there is no easy way to obtain OpenJFX for OpenJDK 1.8, so it is not supported. If anyone has ideas on how to easily add this support, please comment on issue #71.

A Note on JavaFX vs OpenJFX

JavaFX was included in Oracle JRE versions 1.7u6 through 10, but has never been bundled in any version of OpenJDK, nor is it bundled in any edition of the JRE (Oracle or OpenJDK) from version 11 onward. In JRE 11 and up, these capabilities are instead provided by a separate library called OpenJFX that is not part of the default JRE installation.

Although we've attempted to hide this compatibility mess from the library user to the maximum extent possible, we were forced to provide multiple fn-fx artifacts in Clojars:

  1. fn-fx-javafx - for code that targets JREs that bundle JavaFX (i.e. Oracle JRE versions 1.7u6 through 10)
  2. fn-fx-openjfx## - for code that targets JREs that do not bundle JavaFX (i.e. OpenJDK and Oracle JRE versions 11 and up). Note that "##" is a specific number (currently only "11" is provided, yielding fn-fx-openjfx11)

Although these artifacts are code-identical, they have different upstream dependencies that are JRE-version-specific, so please make sure you select the correct artifact based on the version of the JRE that will be used at both development and runtime.

The regrettably tight coupling between OpenJFX and JRE versions makes it more challenging for the project to maintain both forward and backward compatibility across JRE editions and versions, but our intent is to maintain the broadest practical compatibility, at least until JRE 11+ is widely deployed. Whether this continues to be done via multiple artifacts, or some other mechanism is an open question. If you have suggestions / comments / preferences on this, please let us know!

Trying the Library Out

With lein-try

Kicking the tyres is a snap with the handy lein-try plugin:

$ lein try fn-fx/fn-fx-openjfx11 "0.5.0-SNAPSHOT"   # If you're running JRE 11 (Oracle or OpenJDK)
$ lein try fn-fx/fn-fx-javafx "0.5.0-SNAPSHOT"      # If you're running on an Oracle JRE, versions 1.7u6 to 10

With a Placeholder Project

Or, if you'd rather not use lein-try, you could create a new folder and put a project.clj file in it, something like this:

(defproject your-name/my-first-fn-fx-project "0.1.0-SNAPSHOT"
  :description      "My first fn(fx) project"
  :min-lein-version "2.8.1"
  :dependencies     [[org.clojure/clojure "1.9.0"]
                     ; Pick one, and only one, of the following dependencies:
                     [fn-fx/fn-fx-openjfx11 "0.5.0-SNAPSHOT"]    ; If you're running JRE 11 (Oracle or OpenJDK)
                     [fn-fx/fn-fx-javafx "0.5.0-SNAPSHOT"]       ; If you're running on an Oracle JRE, versions 1.7u6 to 10
])

Then start a REPL from that directory:

$ lein repl
nREPL server started on port 55554 on host 127.0.0.1 - nrepl://127.0.0.1:55554
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
OpenJDK 64-Bit Server VM 11.0.1+13
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> 

Using the Clojure Command Line Tools

Not yet implemented, though this is being tracked as issue #49. PRs welcome!

Making API Calls from the REPL

Once you're in a REPL you can make standard fn-fx API calls:

user=> (require '[fn-fx.fx-dom :as dom])
nil
user=> (require '[fn-fx.controls :as ui])
nil
user=> ; Do awesome GUI stuff here!

API documentation is published here, but note that these are currently *cough* "limited" due to a lack of docstrings in the code. Issue #s 21, 26, 27, and 28 go into more detail on this, and any additional comments / feedback / PRs for documentation are welcome!

Running the Examples

Note that the examples are not deployed to Clojars, so to run those you'll need to clone the project locally, and run a REPL from within the cloned directory:

$ cd <directory where you like to put your GitHub clones>
$ git clone https://github.com/fn-fx/fn-fx.git
Cloning into 'fn-fx'...
remote: Enumerating objects: 47, done.
remote: Counting objects: 100% (47/47), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 550 (delta 23), reused 39 (delta 20), pack-reused 503
Receiving objects: 100% (550/550), 301.67 KiB | 4.02 MiB/s, done.
Resolving deltas: 100% (264/264), done.
$ cd fn-fx
$ lein repl
nREPL server started on port 52005 on host 127.0.0.1 - nrepl://127.0.0.1:52005
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
OpenJDK 64-Bit Server VM 11.0.1+13
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=>

Once you have a REPL up within the cloned directory, the examples may be run as follows:

  • 01 Hello world: (require '[getting-started.01-hello-word :as hello]) (hello/-main)
  • 02 Form: (require '[getting-started.02-form :as form]) (form/-main)
  • Shapes 3D: (require '[other-examples.shapes-3d :as shapes-3d]) (shapes-3d/-main)
  • Todo: (require '[other-examples.todo :as todo]) (todo/-main)
  • Menu Bar: (require '[other-examples.menubar :as menu]) (menu/-main)
  • WebView: (require '[other-examples.webview :as webview]) (webview/-main)

Each example will open a window that can be closed at any time (and reopened by calling -main again).

License

Copyright (c) 2016 Timothy Baldridge. All rights reserved. The use and distribution terms for this software are covered by the Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can be found in the file epl-v10.html at the root of this distribution. By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must not remove this notice, or any other, from this software.

fn-fx's People

Contributors

halgari avatar jimmyhmiller avatar nblumoe avatar paulrd avatar pmonks avatar roti avatar stathissideris avatar titonbarua avatar zcaudate avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fn-fx's Issues

Can you show two more examples of fn-fx in you video

Can you show two more examples of fn-fx in your video?

One would be the settings to run it as a Java Application in Cursive.
The other would be the settings to run it as a Clojure Application in Cursive.

I've been able to run the examples via the REPL but have had issues trying to get them to run those two ways above. Doc updates with screenshots of the settings would be greatly appreciated if you have the time.

Thanks for the cool library.

Support for WebView

Feature Request

fn-fx should support javafx.scene.web.WebView

Description of Problem:

With a WebView one needs to call .getEngine().load() or .getEngine().loadContent() to do something meaningfull. The problem is that the underlying WebEngine is not accessible via a property in JavaFX, but more importantly .load() and .loadContent() are actions that should happen on an event, and can't be called on each render cycle like the properties.

Potential Solutions:

Ideally the fn-fx's API should support this use case directly. As an alternative solution, fn-fx could have a standard way to access the underlying Java FX objects so that use cases like this are possible.

Endless changeloop for sliders

Support Question

I'm sure I'm doing something wrong, but I can't figure it out. The sliders I create get into an endless change loop if I make changes too fast. It seems like when the new value is put into the view, that triggers the handler again. How can I avoid this?

Here's what I do:

(defn- my-slider-value-changed-handler [my-app-state event-data]
  (swap! my-app-state assoc :my-slider-value (:fn-fx.listen/new event-data)))

(defui MySliderExample
  (render [this {:keys [my-slider-value] :as my-app-state}]
          [(ui/slider
            :value my-slider-value
            :listen/value {:event ::slider-value-changed})]))

FWIW, I tried rounding these values to lower the number of swaps on the atom, but that didn't have any effect.

Error when hotswapping CSS file name

I have a setup where the name of my .css file is stored in the data-state atom. I tried using the method suggested by this blog post on how to dynamically update the style on the repl. However, about 50% of the time when I set the css file to a new string (with (swap! data-state assoc-in [:ui :stylesheets] ["main.css"])) the below error occurs. Even if the error doesn't occur, the window does not update its style. At the very least, I imagine the error message should be improved.

#error {
 :cause Assert failed: TODO: Implement this
(= idx (count lst))
 :via
 [{:type java.lang.AssertionError
   :message Assert failed: TODO: Implement this
(= idx (count lst))
   :at [fn_fx.fx_dom.FXDom$fn__18087 invoke fx_dom.clj 33]}]
 :trace
 [[fn_fx.fx_dom.FXDom$fn__18087 invoke fx_dom.clj 33]
  [clojure.lang.AFn run AFn.java 22]
  [com.sun.javafx.application.PlatformImpl lambda$null$173 PlatformImpl.java 295]
  [java.security.AccessController doPrivileged AccessController.java -2]
  [com.sun.javafx.application.PlatformImpl lambda$runLater$174 PlatformImpl.java 294]
  [com.sun.glass.ui.InvokeLaterDispatcher$Future run InvokeLaterDispatcher.java 95]
  [com.sun.glass.ui.gtk.GtkApplication _runLoop GtkApplication.java -2]
  [com.sun.glass.ui.gtk.GtkApplication lambda$null$49 GtkApplication.java 139]
  [java.lang.Thread run Thread.java 745]]}
 "Elapsed time: 3.601076 msecs"

Got Exception when run (-main) of other-example.todo in repl

Exception in thread "JavaFX Application Thread" 
java.lang.ClassCastException: javafx.stage.Stage cannot be cast to javafx.scene.Node
	at fn_fx.fx_tree_search$find_child_by_id.invokeStatic(fx_tree_search.clj:23)
	at fn_fx.fx_tree_search$find_child_by_id.invoke(fx_tree_search.clj:22)
	at fn_fx.fx_tree_search$find_nearest_by_id.invokeStatic(fx_tree_search.clj:37)
	at fn_fx.fx_tree_search$find_nearest_by_id.invoke(fx_tree_search.clj:34)
	at fn_fx.render_core$gather_event_data$fn__18684.invoke(render_core.clj:279)
	at clojure.lang.PersistentArrayMap.kvreduce(PersistentArrayMap.java:373)
	at clojure.core$fn__9354.invokeStatic(core.clj:6721)
	at clojure.core$fn__9354.invoke(core.clj:6706)
	at clojure.core.protocols$fn__9145$G__9140__9154.invoke(protocols.clj:174)
	at clojure.core$reduce_kv.invokeStatic(core.clj:6732)
	at clojure.core$reduce_kv.invoke(core.clj:6723)
	at fn_fx.render_core$gather_event_data.invokeStatic(render_core.clj:275)
	at fn_fx.render_core$gather_event_data.invoke(render_core.clj:269)
	at fn_fx.render_core$get_add_listener$fn$reify__18691.changed(render_core.clj:300)
	

example application exit automatically

I run examples/other_examples/todo.clj with lein run on mac os sierra, when the window lose focus, the program will exit automatically with no error output.

lein uberjar gives an error ClassNotFoundException

Bug Report

Steps to Reproduce:

  1. ...step 1 lein new app xxx
  2. ...step 2 add dependency [fn-fx/fn-fx-javafx "0.5.0-SNAPSHOT"] the copy fn-fx-master 01_hello_word.clj code to xxx.core
  3. ...step 3 "lein run" works ok, but "lein uberjar" doesn't

Expected Result:

get jar files

Actual Result:

java.lang.ClassNotFoundException: javafx.application.Platform, compiling:(util.clj:1:1)
Exception in thread "main" java.lang.ClassNotFoundException: javafx.application.Platform, compiling:(util.clj:1:1)

Environment:

Leiningen 2.9.1 on Java 11 Java HotSpot(TM) 64-Bit Server VM

Additional Context:

nil cause for error

Been getting some errors when compiling my code with a singularly unhelpful stacktrace:

#error {
 :cause nil
 :via
 [{:type java.lang.NullPointerException
   :message nil
   :at [fn_fx.render_core$get_getter invokeStatic render_core.clj 320]}]
 :trace
 [[fn_fx.render_core$get_getter invokeStatic render_core.clj 320]
  [fn_fx.render_core$get_getter invoke render_core.clj 313]
  [fn_fx.render_core$get_property invokeStatic render_core.clj 104]
  [fn_fx.render_core$get_property invoke render_core.clj 103]
  [fn_fx.fx_dom.FXDom$fn__18478 invoke fx_dom.clj 32]
  [clojure.lang.AFn run AFn.java 22]
  [com.sun.javafx.application.PlatformImpl lambda$null$173 PlatformImpl.java 295]
  [java.security.AccessController doPrivileged AccessController.java -2]
  [com.sun.javafx.application.PlatformImpl lambda$runLater$174 PlatformImpl.java 294]
  [com.sun.glass.ui.InvokeLaterDispatcher$Future run InvokeLaterDispatcher.java 95]
  [com.sun.glass.ui.gtk.GtkApplication _runLoop GtkApplication.java -2]
  [com.sun.glass.ui.gtk.GtkApplication lambda$null$49 GtkApplication.java 139]
  [java.lang.Thread run Thread.java 745
]]}

The errors are most likely my fault, but a better error message (so I could find out what my mistake was), would be great.

problem with clojars package?

seems like a problem with clojars package?

my project.clj:

(defproject func "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [fn-fx "0.1.0-SNAPSHOT"]]
  :main func.core)

my core.clj:

(ns func.core
  (:require [fn-fx.fx-dom :as dom]            
            [fn-fx.diff :refer [component defui render should-update?]]            
            [fn-fx.render :refer [ui]]))


(defn -main []  
  (let [u (ui :stage            
            :title "Hello World!"            
            :shown true            
            :scene (ui :scene                     
                     :width 300                     
                     :height 250                     
                     :root (ui :stack-pane                             
                             :children [(ui :button                                          
                                          :text "Say 'Hello World'"                                          
                                          :on-action {:say "Hello World!"})])))        
        handler-fn (fn [evt]                     
                     (println "Received Event: " evt))]    
    (dom/app u handler-fn)))

the error message:

java.io.FileNotFoundException: Could not locate fn_fx/fx_dom__init.class or fn_fx/fx_dom.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name., compiling:(core.clj:1:1)

Auto generate and deploy API docs from master

As a prelude to addressing issues #21, #26, #27, and #28, it would be great to autogenerate API docs from the master branch, and publish them on GitHub pages.

Even though those API docs will likely be *cough* "rough" to begin with, by airing our dirty laundry we create pressure on ourselves to fix the docstrings in the codebase.

Bug with KeyFrame constructors

The constructors of KeyFrame are not being initialized correctly and we just get the default/empty constructor. It should actually have multiple constructor values (:time and :on-finished) https://docs.oracle.com/javase/8/javafx/api/javafx/animation/KeyFrame.html

I've narrowed down the issue a bit. Comparing something that works

 (fn-fx.util.reflect-utils/get-value-ctors javafx.scene.image.Image) 

output:

({:method #object[java.lang.reflect.Method 0x8876855 "public static javafx.scene.image.Image javafx.scene.image.Image.impl_fromPlatformImage(java.lang.Object)"], :is-ctor? false, :method-name impl_fromPlatformImage, :prop-names (image), :prop-types [java.lang.Object], :prop-names-kw [:image], :prop-names-sym [image]} {:method #object[java.lang.reflect.Constructor 0x49507ef5 "public javafx.scene.image.Image(java.io.InputStream,double,double,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (is requestedWidth requestedHeight preserveRatio smooth), :prop-types [java.io.InputStream double double boolean boolean], :prop-names-kw [:is :requested-width :requested-height :preserve-ratio :smooth], :prop-names-sym [is requested-width requested-height preserve-ratio smooth]} {:method #object[java.lang.reflect.Constructor 0x70df4b07 "public javafx.scene.image.Image(java.io.InputStream)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (is), :prop-types [java.io.InputStream], :prop-names-kw [:is], :prop-names-sym [is]} {:method #object[java.lang.reflect.Constructor 0x18cdfc45 "public javafx.scene.image.Image(java.lang.String)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url), :prop-types [java.lang.String], :prop-names-kw [:url], :prop-names-sym [url]} {:method #object[java.lang.reflect.Constructor 0x2b3bfac9 "public javafx.scene.image.Image(java.lang.String,double,double,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url requestedWidth requestedHeight preserveRatio smooth), :prop-types [java.lang.String double double boolean boolean], :prop-names-kw [:url :requested-width :requested-height :preserve-ratio :smooth], :prop-names-sym [url requested-width requested-height preserve-ratio smooth]} {:method #object[java.lang.reflect.Constructor 0x61568e8d "public javafx.scene.image.Image(java.lang.String,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url backgroundLoading), :prop-types [java.lang.String boolean], :prop-names-kw [:url :background-loading], :prop-names-sym [url background-loading]} {:method #object[java.lang.reflect.Constructor 0x6bdb66d3 "public javafx.scene.image.Image(java.lang.String,double,double,boolean,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url requestedWidth requestedHeight preserveRatio smooth backgroundLoading), :prop-types [java.lang.String double double boolean boolean boolean], :prop-names-kw [:url :requested-width :requested-height :preserve-ratio :smooth :background-loading], :prop-names-sym [url requested-width requested-height preserve-ratio smooth background-loading]})

To KeyFrame which is broken

(fn-fx.util.reflect-utils/get-value-ctors javafx.animation.KeyFrame) 

output:

({:method #object[java.lang.reflect.Constructor 0x28f769b2 "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.event.EventHandler,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration javafx.event.EventHandler [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x64f9ddf1 "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x5002e3ee "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x35c9f65a "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.event.EventHandler,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String javafx.event.EventHandler [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x26e0a3a8 "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.event.EventHandler,java.util.Collection)"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String javafx.event.EventHandler java.util.Collection], :prop-names-kw [], :prop-names-sym []})

It's really hard to see with GitHub's formatting, but there a semi-colon ";" that shows up near the beginning of the KeyFrame output It's very bizarre and makes most of the output effectively commented out.

Running:

(:method (first (fn-fx.util.reflect-utils/get-ctors javafx.animation.KeyFrame)))

Does give us something where we can see the constructor arguments are there

#object[java.lang.reflect.Constructor 0x67dc18da "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.event.EventHandler,javafx.animation.KeyValue[])"]

Past that, I'm not sure. Something is not quite parsing in the values correctly. I haven't been able to figure out the rest of the internals of get-value-ctors b/c the code is a bit beyond me. Any help would be greatly appreciated

Using stylesheets

How can I add my to my scene? In Java it's done using a method, so I'm not sure how to translate that to fn-fx. Do you have an example?

Calling methods

Would it be possible to have an example of calling a method on a ui object? As an example this does not work:

(def transition (ui/parallel-transition a-rect a-translate))
(.play transition)

How to get pixelwriter from controls/canvas

I want to get the instance of PixelWriter because I want to draw images.
So , I need this function ...

PixelWriter writer = canvas.getGraphicsContext2D().getPixelWriter()

But I don't know how to do it in this api.
I think ...

(defui Image 
   (render [this]
        (controls/canvas
           (**something, but I cannot find solution**))))

So, can you give me some example?

(ui/text :fill (ui/color ...)) does not set fill property to said color

Bug Report

Steps to Reproduce:

  1. Create a (dom/app with a ui/text element and set the :fill to a ui/color as in the 02_form.clj example application.
  2. Look at the text color via your eyes or examining the properties.

Expected Result:

Color should have changed ie firebrick.

Actual Result:

Text remains black.

Environment:

master branch, ubuntu 19.04, javafx11

supporting openjfx

Openjfx is a months out and there are some significant changes:

  • Builders have been deprecated since JFX8 and are completely removed in OpenFX
  • There's a module system for JDK10+ that need some workarounds

are there plans for supporting openjfx?

Non-deterministic unit test failure

When running the tests locally on JDK 8 (i.e. with bundled JavaFX), I periodically see the following error, and a similar error has shown up on TravisCI too.

I don't seem to have the same issue on JDK 11 / OpenJFX 11, though it has occurred on TravisCI with that version combo.

I haven't discovered a pattern to its occurrence - often just rerunning the tests will succeed (both locally and on TravisCI). This is especially puzzling on TravisCI, since the container the tests run in there is thrown away between every single test run, significantly reducing the possibility that some kind of state is being preserved between successive runs of the tests.

$ lein do clean, version, test, jar
Leiningen 2.8.1 on Java 1.8.0_192 Java HotSpot(TM) 64-Bit Server VM

lein test fn-fx.diff-test

lein test fn-fx.fx-dom-test

lein test :only fn-fx.fx-dom-test/static-member-tests

ERROR in (static-member-tests) (fx_dom_test.clj:10)
Uncaught exception, not in assertion.
expected: nil
  actual: java.lang.AssertionError: Assert failed: comp
 at fn_fx.fx_dom_test$get_prop.invokeStatic (fx_dom_test.clj:10)
    fn_fx.fx_dom_test$get_prop.invoke (fx_dom_test.clj:10)
    fn_fx.fx_dom_test$fn__4061.invokeStatic (fx_dom_test.clj:116)
    fn_fx.fx_dom_test/fn (fx_dom_test.clj:99)
    clojure.test$test_var$fn__9209.invoke (test.clj:716)
    clojure.test$test_var.invokeStatic (test.clj:716)
    clojure.test$test_var.invoke (test.clj:707)
    clojure.test$test_vars$fn__9235$fn__9240.invoke (test.clj:734)
    clojure.test$default_fixture.invokeStatic (test.clj:686)
    clojure.test$default_fixture.invoke (test.clj:682)
    clojure.test$test_vars$fn__9235.invoke (test.clj:734)
    clojure.test$default_fixture.invokeStatic (test.clj:686)
    clojure.test$default_fixture.invoke (test.clj:682)
    clojure.test$test_vars.invokeStatic (test.clj:730)
    clojure.test$test_all_vars.invokeStatic (test.clj:736)
    clojure.test$test_ns.invokeStatic (test.clj:757)
    clojure.test$test_ns.invoke (test.clj:742)
    user$eval233$fn__294.invoke (form-init3321944168700773224.clj:1)
    clojure.lang.AFn.applyToHelper (AFn.java:156)
    clojure.lang.AFn.applyTo (AFn.java:144)
    clojure.core$apply.invokeStatic (core.clj:659)
    clojure.core$apply.invoke (core.clj:652)
    leiningen.core.injected$compose_hooks$fn__163.doInvoke (form-init3321944168700773224.clj:1)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:657)
    clojure.core$apply.invoke (core.clj:652)
    leiningen.core.injected$run_hooks.invokeStatic (form-init3321944168700773224.clj:1)
    leiningen.core.injected$run_hooks.invoke (form-init3321944168700773224.clj:1)
    leiningen.core.injected$prepare_for_hooks$fn__168$fn__169.doInvoke (form-init3321944168700773224.clj:1)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.lang.AFunction$1.doInvoke (AFunction.java:29)
    clojure.lang.RestFn.invoke (RestFn.java:408)
    clojure.core$map$fn__5587.invoke (core.clj:2747)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.Cons.next (Cons.java:39)
    clojure.lang.RT.boundedLength (RT.java:1785)
    clojure.lang.RestFn.applyTo (RestFn.java:130)
    clojure.core$apply.invokeStatic (core.clj:659)
    clojure.test$run_tests.invokeStatic (test.clj:767)
    clojure.test$run_tests.doInvoke (test.clj:767)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.core$apply.invokeStatic (core.clj:657)
    clojure.core$apply.invoke (core.clj:652)
    user$eval233$fn__306$fn__339.invoke (form-init3321944168700773224.clj:1)
    user$eval233$fn__306$fn__307.invoke (form-init3321944168700773224.clj:1)
    user$eval233$fn__306.invoke (form-init3321944168700773224.clj:1)
    user$eval233.invokeStatic (form-init3321944168700773224.clj:1)
    user$eval233.invoke (form-init3321944168700773224.clj:1)
    clojure.lang.Compiler.eval (Compiler.java:7062)
    clojure.lang.Compiler.eval (Compiler.java:7052)
    clojure.lang.Compiler.load (Compiler.java:7514)
    clojure.lang.Compiler.loadFile (Compiler.java:7452)
    clojure.main$load_script.invokeStatic (main.clj:278)
    clojure.main$init_opt.invokeStatic (main.clj:280)
    clojure.main$init_opt.invoke (main.clj:280)
    clojure.main$initialize.invokeStatic (main.clj:311)
    clojure.main$null_opt.invokeStatic (main.clj:345)
    clojure.main$null_opt.invoke (main.clj:342)
    clojure.main$main.invokeStatic (main.clj:424)
    clojure.main$main.doInvoke (main.clj:387)
    clojure.lang.RestFn.applyTo (RestFn.java:137)
    clojure.lang.Var.applyTo (Var.java:702)
    clojure.main.main (main.java:37)

lein test :only fn-fx.fx-dom-test/test-basic-component-properties

FAIL in (test-basic-component-properties) (fx_dom_test.clj:23)
expected: (= (get-prop (clojure.core/deref root) :title) "Hello")
  actual: (not (= nil "Hello"))

Ran 9 tests containing 59 assertions.
1 failures, 1 errors.
Tests failed.

Doesn't seem to work with Ubuntu out of the box

Bug Report

Steps to Reproduce:

On Ubuntu Cuttlefish (18.10)
Install leiningen from the repository
Install openjdk-8-jre from the repository
Try to run a fn-fx project and it will not find JavaFX

Expected Result:

Installing leiningen installs openjdk-8-jre-headless. But after installing openjdk-8-jre it should include JavaFX as I understand it.

Actual Result:

What I get instead is a crash:

Exception in thread "main" java.lang.NoClassDefFoundError: javafx/scene/Group, compiling:(asparapiss/svg2jfx.clj:16:3)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
	at clojure.lang.Compiler.analyze(Compiler.java:6669)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
	at clojure.lang.Compiler.analyze(Compiler.java:6669)
	at clojure.lang.Compiler.analyze(Compiler.java:6625)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5380)
[etc]

Additional Context:

I've also tried to install the openjfx package (which I think is for Java 11) as well as the openjdk-8-jdk

So I'm not sure if this is a bug or I'm missing something obvious here (like some additional package). I've had this project running before on a Debian system (using version 0.4.0), but unfortunately I don't remember how I set up then (but I remember it not being straightforward). I've lost that setup and had to switch to an Ubuntu system. When trying to get this old project running I ran into this crash. My project.clj has a

              [fn-fx/fn-fx-javafx "0.5.0-SNAPSHOT"]

I tried the old halgari 0.4.0 as well and have the same issue
And my namespace starts with a

If you point me to another fn-fx project to test I can do that as well

UI not updating even though the model differs

Bug Report

Steps to Reproduce:

  1. Checkout https://github.com/Azzurite/fn-fx-update-bug/
  2. Run -main
  3. Click on "Goto Settings"

Expected Result:

Expect "MainPane content!" to change to "Settings content!"

Actual Result:

"MainPane content!" stays displayed

Environment:

Windows 10 x64, JDK 1.8.0_191 (also does not work under OpenJDK 11.0.1 with the other dependency)

Additional Context:

Happened in a more complex program of mine. I can get the change to work by wrapping one of the GridPanes with a StackPane, apparently that manages to "trick" the diff algorithm into thinking something actually changed.

You can see that in the example repository in the workaround branch.

I managed to trace the bug to somewhere in the diff-component fn in fn-fx.diff, but didn't manage to understand that fn.

Add documentation or example for combo-box

Howdy, I was trying to use the ui/combo-box macro, and I had a hell of a time since there was no documentation. I was able to figure out how to add list items by adding the :items ["my item"] thing, but I believe that that is something that should be documented, or at the very least, given an example.

I would be happy to help contribute to documentation if it would be useful.

fn-fx doesn't work on OpenJDK 1.8

Steps to Reproduce:

  1. Install OpenJDK 1.8
  2. Run the tests (lein do version, test)

Expected Result:

Tests run, and pass (taking note of issue #45).

Actual Result:

$ lein do version, test
(Warning: profile :openjfx1.8 not found.)
Leiningen 2.8.1 on Java 1.8.0_192 OpenJDK 64-Bit Server VM
(Warning: profile :openjfx1.8 not found.)
Exception in thread "main" java.lang.ClassNotFoundException: javafx.application.Platform, compiling:(fn_fx/util.clj:1:1)
	at clojure.lang.Compiler.load(Compiler.java:7526)
	at clojure.lang.RT.loadResourceScript(RT.java:379)
	at clojure.lang.RT.loadResourceScript(RT.java:370)
	at clojure.lang.RT.load(RT.java:460)
	at clojure.lang.RT.load(RT.java:426)
	at clojure.core$load$fn__6548.invoke(core.clj:6046)
	at clojure.core$load.invokeStatic(core.clj:6045)
	at clojure.core$load.doInvoke(core.clj:6029)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5848)
	at clojure.core$load_one.invoke(core.clj:5843)
	at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
	at clojure.core$load_lib.invokeStatic(core.clj:5887)
	at clojure.core$load_lib.doInvoke(core.clj:5868)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$load_libs.invokeStatic(core.clj:5925)
	at clojure.core$load_libs.doInvoke(core.clj:5909)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$require.invokeStatic(core.clj:5947)
	at clojure.core$require.doInvoke(core.clj:5947)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at fn_fx.diff$eval367$loading__6434__auto____368.invoke(diff.clj:1)
	at fn_fx.diff$eval367.invokeStatic(diff.clj:1)
	at fn_fx.diff$eval367.invoke(diff.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.RT.loadResourceScript(RT.java:379)
	at clojure.lang.RT.loadResourceScript(RT.java:370)
	at clojure.lang.RT.load(RT.java:460)
	at clojure.lang.RT.load(RT.java:426)
	at clojure.core$load$fn__6548.invoke(core.clj:6046)
	at clojure.core$load.invokeStatic(core.clj:6045)
	at clojure.core$load.doInvoke(core.clj:6029)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5848)
	at clojure.core$load_one.invoke(core.clj:5843)
	at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
	at clojure.core$load_lib.invokeStatic(core.clj:5887)
	at clojure.core$load_lib.doInvoke(core.clj:5868)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$load_libs.invokeStatic(core.clj:5925)
	at clojure.core$load_libs.doInvoke(core.clj:5909)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$require.invokeStatic(core.clj:5947)
	at clojure.core$require.doInvoke(core.clj:5947)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at fn_fx.diff_test$eval361$loading__6434__auto____362.invoke(diff_test.clj:1)
	at fn_fx.diff_test$eval361.invokeStatic(diff_test.clj:1)
	at fn_fx.diff_test$eval361.invoke(diff_test.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.RT.loadResourceScript(RT.java:379)
	at clojure.lang.RT.loadResourceScript(RT.java:370)
	at clojure.lang.RT.load(RT.java:460)
	at clojure.lang.RT.load(RT.java:426)
	at clojure.core$load$fn__6548.invoke(core.clj:6046)
	at clojure.core$load.invokeStatic(core.clj:6045)
	at clojure.core$load.doInvoke(core.clj:6029)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5848)
	at clojure.core$load_one.invoke(core.clj:5843)
	at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
	at clojure.core$load_lib.invokeStatic(core.clj:5887)
	at clojure.core$load_lib.doInvoke(core.clj:5868)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$load_libs.invokeStatic(core.clj:5925)
	at clojure.core$load_libs.doInvoke(core.clj:5909)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$require.invokeStatic(core.clj:5947)
	at clojure.core$require.doInvoke(core.clj:5947)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$apply.invoke(core.clj:652)
	at user$eval233.invokeStatic(form-init3481129458187765184.clj:1)
	at user$eval233.invoke(form-init3481129458187765184.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7052)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.Compiler.loadFile(Compiler.java:7452)
	at clojure.main$load_script.invokeStatic(main.clj:278)
	at clojure.main$init_opt.invokeStatic(main.clj:280)
	at clojure.main$init_opt.invoke(main.clj:280)
	at clojure.main$initialize.invokeStatic(main.clj:311)
	at clojure.main$null_opt.invokeStatic(main.clj:345)
	at clojure.main$null_opt.invoke(main.clj:342)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Caused by: java.lang.ClassNotFoundException: javafx.application.Platform
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at clojure.lang.DynamicClassLoader.findClass(DynamicClassLoader.java:69)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at clojure.lang.DynamicClassLoader.loadClass(DynamicClassLoader.java:77)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at clojure.lang.RT.classForName(RT.java:2204)
	at clojure.lang.RT.classForNameNonLoading(RT.java:2217)
	at fn_fx.util$eval427$loading__6434__auto____428.invoke(util.clj:1)
	at fn_fx.util$eval427.invokeStatic(util.clj:1)
	at fn_fx.util$eval427.invoke(util.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	... 97 more
Tests failed.

How do you get the selected value from a choice-box

(defui Choice
  (render [this _]
          (ui/choice-box
           :on-action {:event :choose-env :this this  }
           :items ["development"
                   "staging"
                   "production"] )))

In the "choose-env" handle, I have no access to what item was chosen. I tried to pass "this" as an argument and call (.getSelectionModel this) in the event handler, but there's no such method. How are you supposed to get the chosen item ?

bind functionality

Support Question

Hello there, first of all, thank you for this wonderful library.
I've been using it instead of regular javafx for the fast few while and I really enjoy using it. However I have encountered a few roadblocks.
What would be the correct way to emulate the infamous "fit-to-parent" AnchorPane functionality? My so far unsuccessful approach was something like this:

(controls/anchor-pane
  :children 
    [(controls/some-control 
      :anchor-pane/top-anchor 0.0
      :anchor-pane/bottom-anchor 0.0
      :anchor-pane/left-anchor 0.0
      :anchor-pane/right-anchor 0.0)])

But for some controls (ImageView cough) I know, this just won't cut it in the long run. So are there major hurdles for the nested-properties branch to get merged? I looked at the source and am looking forward to the :bind/property syntax with [:id :prop] values. I don't really get the complete picture of what is depending on what right now, but with some pointers I would like to look into this and help if possible/necessary.

table-cell with button

Button inside cll

How can I use a button inside cell?
I tried with Java examples but that thing about extend is too dificult

Not being able to compile fn-fx

Maybe there is something wrong with my system by I'm not being able to use 0.2.0 or 0.3.0-SNAPSHOT.

Current system:

Leiningen 2.7.1 on Java 1.8.0_11 Java HotSpot(TM) 64-Bit Server VM

The first problem I was facing was compiling fn-fx.util.reflect-utils
Trying to def all-javafx-types it complains in the first line that it can't initialize javafx.stage.Screen in current thread, only in event thread.

To avoid initializing that class I changed

#(Class/forName %)

to

#(Class/forName % false (.getContextClassLoader (Thread/currentThread)))

so it doesn't try to initialize the class now, just loads it.

Now that part works but I get

Error compiling fn_fx/controls.clj at (27:3)
Caused by java.lang.ClassNotFoundException
   javafx.util.converter.LocalDateTimeStringConverter

Any ideas of what I'm doing wrong?

Standardise how examples are run

Currently the examples are run in 2 different ways. It would be great to standardise on a single approach, to make it easier for newcomers to the library.

  • 01 Hello world: (require '[getting-started.01-hello-word]) (note: (-main) is automatically invoked)
  • 02 Form: (require '[getting-started.02-form :as form]) (form/-main)
  • Shapes 3d: (require '[other-examples.shapes-3d :as shapes-3d]) (shapes-3d/-main)
  • Todo: (require '[other-examples.todo :as todo]) (todo/-main)

Trouble editing TabPane.selectionModel.selectedIndex / Nested Modify

Overview

Hey Tim!

Great library. Thanks a million for providing it.

Here's a question for you and the community:

I'm having an issue adding a listener to TabPane.selectionModelProperty.selectedIndexProperty.

I've never had to go two layers deep in fn-fx before, so I may be missing something fairly obvious.

Details

Here's what I'm attempting to do in Java:

tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
    @Override
    public void changed(ObservableValue<? extends Number> ov, Number oldValue, Number newValue) {
        // do something...
    }
});

1 - Dream Client Code

Here's my dream client code in Clojure with fn-fx:

(ui/tab-pane
 :selection-model {:selected-index/listener {:event :tab-changed}}
 :tabs tabs))

Error Running Dream Client Code

The error I encountered when writing the above code:

Can't convert {:selected-index/listener {:event :tab-changed}} of type class clojure.lang.PersistentArrayMap to class javafx.scene.control.SingleSelectionModel

^ Seems that my attempt at a nested modification [:selection-model :selected-index/listener] failed.

2 - Failure to Instantiate SingleSelectionModel

I attempted to instantiate a SingleSelectionModel, but it looks like this violates JavaFX's instantiation policy:

(ui/tab-pane
 :selection-model (SingleSelectionModel.)
 :tabs tabs))

Recap

I suspect that there may be something simple for doing a nested modify.

Is there a good workaround or perhaps an insight that I am missing? Many thanks!

Using mouse running other-examples.shapes-3d triggers assert

Using mouse running other-examples.shapes-3d triggers assert:

Can't convert 0 of type class java.lang.Long to int

Having this new function in render-core fixes the problem:

(defmethod convert-value
  [java.lang.Long Integer/TYPE]
  [value _]
  (int value)) 

using alternate hiccup syntax for defining dom

@halgari, @pmonks, @roti: I've been staring at the fn-fx.diff/defui code for the longest time and I can't quite figure out what it is doing.

I started on a simplified non-react wrapper for javafx that enables property bindings instead of a virtual dom. It's okay and found that I like the syntax but the app does becomes harder to control the bigger it gets.

So I'm looking deeper into fn-fx. However, I built the javafx wrapper to compile hiccup syntax and I quite like it:

(defn application [state]
  (let [output-img (derived-output state)]
    (fx/ui [:stage {:title (fx/get state [:window :title] str)
                    :width (fx/set state [:window :width])}
            [:scene
             [:border-pane
              {:bottom  [:label "hello"]
               :center  [:border-pane
                                {:center  [:anchor-pane
                                                  [:image-view
                                                   {:fit-width (fx/get state [:window :width] #(- % 300))
                                                    :preserve-ratio true
                                                    :image (fx/get output-img [])}]])}]
               :right  (cp/control-pane state)}]]]))

What I'm looking to do is to modify fn-fx slightly to get this type of syntax and I'm wondering how best to go about it.

Add documentation for ui/file-chooser

Howdy, I am trying to use the ui/file-chooser macro, but I don't really know what parameters to give it since there is no documentation. Can you provide an example?

Exception on Reflection while running examples

Apparently needs some updates. Checked the deps and fiddled with lein a bit, but can't even get the examples to run.

fn-fx$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
fn-fx$ clj examples/getting_started/01_hello_word.clj 
Exception in thread "main" java.lang.ClassNotFoundException: org.reflections.Reflections, compiling:(fn_fx/util/reflect_utils.clj:1:1)
	at clojure.lang.Compiler.load(Compiler.java:7526)
	at clojure.lang.RT.loadResourceScript(RT.java:379)
	at clojure.lang.RT.loadResourceScript(RT.java:370)
	at clojure.lang.RT.load(RT.java:460)
	at clojure.lang.RT.load(RT.java:426)
	at clojure.core$load$fn__6548.invoke(core.clj:6046)
	at clojure.core$load.invokeStatic(core.clj:6045)
	at clojure.core$load.doInvoke(core.clj:6029)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5848)
	at clojure.core$load_one.invoke(core.clj:5843)
	at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
	at clojure.core$load_lib.invokeStatic(core.clj:5887)
	at clojure.core$load_lib.doInvoke(core.clj:5868)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$load_libs.invokeStatic(core.clj:5925)
	at clojure.core$load_libs.doInvoke(core.clj:5909)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$require.invokeStatic(core.clj:5947)
	at clojure.core$require.doInvoke(core.clj:5947)
	at clojure.lang.RestFn.invoke(RestFn.java:482)
	at fn_fx.render_core$eval159$loading__6434__auto____160.invoke(render_core.clj:1)
	at fn_fx.render_core$eval159.invokeStatic(render_core.clj:1)
	at fn_fx.render_core$eval159.invoke(render_core.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.RT.loadResourceScript(RT.java:379)
	at clojure.lang.RT.loadResourceScript(RT.java:370)
	at clojure.lang.RT.load(RT.java:460)
	at clojure.lang.RT.load(RT.java:426)
	at clojure.core$load$fn__6548.invoke(core.clj:6046)
	at clojure.core$load.invokeStatic(core.clj:6045)
	at clojure.core$load.doInvoke(core.clj:6029)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5848)
	at clojure.core$load_one.invoke(core.clj:5843)
	at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
	at clojure.core$load_lib.invokeStatic(core.clj:5887)
	at clojure.core$load_lib.doInvoke(core.clj:5868)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$load_libs.invokeStatic(core.clj:5925)
	at clojure.core$load_libs.doInvoke(core.clj:5909)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$require.invokeStatic(core.clj:5947)
	at clojure.core$require.doInvoke(core.clj:5947)
	at clojure.lang.RestFn.invoke(RestFn.java:436)
	at fn_fx.fx_dom$eval153$loading__6434__auto____154.invoke(fx_dom.clj:1)
	at fn_fx.fx_dom$eval153.invokeStatic(fx_dom.clj:1)
	at fn_fx.fx_dom$eval153.invoke(fx_dom.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.RT.loadResourceScript(RT.java:379)
	at clojure.lang.RT.loadResourceScript(RT.java:370)
	at clojure.lang.RT.load(RT.java:460)
	at clojure.lang.RT.load(RT.java:426)
	at clojure.core$load$fn__6548.invoke(core.clj:6046)
	at clojure.core$load.invokeStatic(core.clj:6045)
	at clojure.core$load.doInvoke(core.clj:6029)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5848)
	at clojure.core$load_one.invoke(core.clj:5843)
	at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
	at clojure.core$load_lib.invokeStatic(core.clj:5887)
	at clojure.core$load_lib.doInvoke(core.clj:5868)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$load_libs.invokeStatic(core.clj:5925)
	at clojure.core$load_libs.doInvoke(core.clj:5909)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:659)
	at clojure.core$require.invokeStatic(core.clj:5947)
	at clojure.core$require.doInvoke(core.clj:5947)
	at clojure.lang.RestFn.invoke(RestFn.java:436)
	at getting_started.01_hello_word$eval147$loading__6434__auto____148.invoke(01_hello_word.clj:1)
	at getting_started.01_hello_word$eval147.invokeStatic(01_hello_word.clj:1)
	at getting_started.01_hello_word$eval147.invoke(01_hello_word.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	at clojure.lang.Compiler.loadFile(Compiler.java:7452)
	at clojure.main$load_script.invokeStatic(main.clj:278)
	at clojure.main$script_opt.invokeStatic(main.clj:338)
	at clojure.main$script_opt.invoke(main.clj:333)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Caused by: java.lang.ClassNotFoundException: org.reflections.Reflections
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at clojure.lang.DynamicClassLoader.findClass(DynamicClassLoader.java:69)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at clojure.lang.DynamicClassLoader.loadClass(DynamicClassLoader.java:77)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at clojure.lang.RT.classForName(RT.java:2204)
	at clojure.lang.RT.classForNameNonLoading(RT.java:2217)
	at fn_fx.util.reflect_utils$eval629$loading__6434__auto____630.invoke(reflect_utils.clj:1)
	at fn_fx.util.reflect_utils$eval629.invokeStatic(reflect_utils.clj:1)
	at fn_fx.util.reflect_utils$eval629.invoke(reflect_utils.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7062)
	at clojure.lang.Compiler.eval(Compiler.java:7051)
	at clojure.lang.Compiler.load(Compiler.java:7514)
	... 93 more

CompilerException org.reflections.ReflectionsException: Couldn't find subtypes of Object.

CompilerException org.reflections.ReflectionsException: Couldn't find subtypes of Object.

Steps to Reproduce:

  1. Start an nREPL
  2. Navigate to exampes/getting_started/01_hello_world.clj
  3. Load the entire file

Expected Result:

Example should be running.

Actual Result:

Window opens, after that the following stacktrace:

Syntax error compiling at (reflect_utils.clj:36:32).
Couldn't find subtypes of Object. Make sure SubTypesScanner initialized to include Object class - new SubTypesScanner(false)
Oct 30, 2018 8:58:17 PM clojure.tools.logging$eval570$fn__573 invoke
SEVERE: Unhandled REPL handler exception processing message {:op stacktrace, :pprint-fn clojure.pprint/pprint, :print-length 50, :print-level 50, :session 133612c4-ddf3-449d-ba10-65f15bb837d9, :id 13}
java.lang.NullPointerException
at clojure.string$replace_first.invokeStatic(string.clj:165)
at clojure.string$replace_first.invoke(string.clj:138)
at cider.nrepl.middleware.stacktrace$relative_path.invokeStatic(stacktrace.clj:204)
at cider.nrepl.middleware.stacktrace$relative_path.invoke(stacktrace.clj:198)
at cider.nrepl.middleware.stacktrace$extract_location.invokeStatic(stacktrace.clj:219)
at cider.nrepl.middleware.stacktrace$extract_location.invoke(stacktrace.clj:206)
at clojure.core$comp$fn__5707.invoke(core.clj:2561)
at clojure.core$map$fn__5766.invoke(core.clj:2747)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:51)
at clojure.lang.RT.seq(RT.java:529)
at clojure.core$seq__5302.invokeStatic(core.clj:137)
at clojure.core$seq__5302.invoke(core.clj:137)
at cider.nrepl.middleware.stacktrace$handle_stacktrace.invokeStatic(stacktrace.clj:312)
at cider.nrepl.middleware.stacktrace$handle_stacktrace.invoke(stacktrace.clj:309)
at clojure.lang.Var.invoke(Var.java:388)
at cider.nrepl$wrap_stacktrace$fn__6591.invoke(nrepl.clj:436)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_undef$fn__6623.invoke(nrepl.clj:495)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at refactor_nrepl.middleware$wrap_refactor$fn__6169.invoke(middleware.clj:214)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_classpath$fn__6475.invoke(nrepl.clj:166)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_apropos$fn__6467.invoke(nrepl.clj:158)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_complete$fn__6483.invoke(nrepl.clj:172)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_version$fn__6631.invoke(nrepl.clj:503)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_slurp$fn__6459.invoke(nrepl.clj:150)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_trace$fn__6607.invoke(nrepl.clj:466)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_ns$fn__6543.invoke(nrepl.clj:308)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_info$fn__6517.invoke(nrepl.clj:236)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_profile$fn__6559.invoke(nrepl.clj:345)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_resource$fn__6575.invoke(nrepl.clj:408)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_spec$fn__6583.invoke(nrepl.clj:420)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__1160.invoke(interruptible_eval.clj:247)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_inspect$fn__6527.invoke(nrepl.clj:255)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_debug$fn__6493.invoke(nrepl.clj:192)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_enlighten$fn__6501.invoke(nrepl.clj:218)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at clojure.tools.nrepl.middleware.session$add_stdin$fn__1243.invoke(session.clj:238)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at clojure.tools.nrepl.middleware.load_file$wrap_load_file$fn__1286.invoke(load_file.clj:79)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_content_type$fn__6451.invoke(nrepl.clj:139)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_pprint$fn__6441.invoke(nrepl.clj:119)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_refresh$fn__6567.invoke(nrepl.clj:382)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_tracker$fn__6615.invoke(nrepl.clj:484)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at clojure.tools.nrepl.middleware.pr_values$pr_values$fn__1076.invoke(pr_values.clj:22)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_test$fn__6599.invoke(nrepl.clj:446)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl.middleware.pprint$handle_pprint_fn.invokeStatic(pprint.clj:61)
at cider.nrepl.middleware.pprint$handle_pprint_fn.invoke(pprint.clj:56)
at clojure.lang.Var.invoke(Var.java:388)
at cider.nrepl$wrap_pprint_fn$fn__6431.invoke(nrepl.clj:100)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at cider.nrepl$wrap_out$fn__6551.invoke(nrepl.clj:336)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at clojure.tools.nrepl.middleware.session$session$fn__1228.invoke(session.clj:192)
at clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__877.invoke(middleware.clj:22)
at clojure.tools.nrepl.server$handle_STAR_.invokeStatic(server.clj:19)
at clojure.tools.nrepl.server$handle_STAR_.invoke(server.clj:16)
at clojure.tools.nrepl.server$handle$fn__1301.invoke(server.clj:28)
at clojure.core$binding_conveyor_fn$fn__5654.invoke(core.clj:2022)
at clojure.lang.AFn.call(AFn.java:18)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:844)

Environment:

  • Hash 6edd7a5 of the project.
  • macOS High Sierra
  • java 10 2018-03-20
    Java(TM) SE Runtime Environment 18.3 (build 10+46)
    Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)
  • Clojure 1.9.0 (also went with a bare bone project with 1.10.0-beta2 and example 2 from getting started, same result)

Better Documentation

Right now if you call (doc fn-fx.diff/render) in a repl, you don't get any result. Many of fn-fx's functions lack usable documentation and it makes it more frustrating to program on the repl.

I understand that many of the items under fn-fx.controls can't easily have documentation, but it would be nice if other, more central functions did.

README example outdated

The example in the README is not working anymore (e.g. ui is not in fn-fx.render anymore).
I suggest to either just refer to the examples folder or replace it with a simple working example in the README itself.

Need a way to define `:cell-value-factory` for `table-column` component

As far as I know, currently there is no idiomatic way to define :cell-value-property for table-column components. You have to jump through hoops to create an object which implement the java Callback interface. Currently I am using a custom function, thanks to this blogpost. Here's the code I am using:

(defn cell-value-factory [f]
  (reify javafx.util.Callback
    (call [this entity]
      (javafx.beans.property.ReadOnlyObjectWrapper. (f (.getValue entity))))))

(defui MyTable
  (render [this _]
      (ui/table-view
        :items [{:name "foo" :age 10}
                {:name "bar" :age 56}]
        :columns [(ui/table-column
                  :text "Name"
                  :cell-value-factory (cell-value-factory #(:name %)))
                  (ui/table-column
                   :text "Age"
                   :cell-value-factory (cell-value-factory #(:age %)))])))

While this works; ideally (IMO), the object creation should be handled by the table-column component itself and :cell-value-factory should accept plain clojure functions. Like this:

(defui MyTable
  (render [this _]
      (ui/table-view
        :items [{:name "foo" :age 10}
                {:name "bar" :age 56}]
        :columns [(ui/table-column
                  :text "Name"
                  :cell-value-factory #(:name %))
                  (ui/table-column
                   :text "Age"
                   :cell-value-factory #(:age %))])))

Transfer to fn-fx org should now be possible

@halgari I've deleted the fork (fn-fx/fn-fx-openjfx) that was preventing the transfer of this repo to the fn-fx org - the transfer should be possible now.

Apologies for raising a new issue for this, but as part of deleting that fork I also deleted the issue where we had previously been discussing this. 😊

Special handling of :shown property prevents on-showing/on-shown events being triggered.

The special handling of the :shown property:

(let [^Method show-method (->> (.getDeclaredMethods Window)

on a Window/Stage immediately causes the show method to be called, potentially before any on-showing/on-shown event handler properties have been set on the component. In my opinion, there should be some sort of lifecycle callback done when the FX component has been fully instantiated where doing things like call show() should be done. I'm still trying to grok this codebase, so I'm not clear where this callback would be inserted.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.