fn-fx / fn-fx

A Functional API around JavaFX / OpenJFX.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for WebView

roti opened this issue · comments

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.

I've been working on a sketch of using WebView. I've got a webview component with a URL text field above it that works as far loading URL's. If you give your webview component an ID, you can get a handle to it from your root component. I keep a reference to the fx-dom root in an agent in my namespace, then using that I can get a handle to the webview, then the engine to load a URL. Feels really hacky, but it works.

I feel like we need another dom node lifecycle callback similar to React's componentDidMount, where after the FX component has been instantiated, you pass it to this optional callback where you can then do things like .getEngine().load() etc..

I was trying to use the on-shown callback on my top-level stage to make the browser load the :current-url in my app state if it wasn't already loaded, but I bumped into another bug that prevents the on-shown event from being triggered (which I'll report in another issue). This is caused by the special handling of the :shown 'property' causing the show() method to be called before the other properties have been set. The special-case handling of the :shown would be better done in a callback done after the FX component has been created.

Some hacky, but working webview code:

(def initial-state {:current-url nil})
(defonce app-state (atom initial-state))
(defonce app-ui-state (agent nil))

(defn load-url [url]
  (log/infof "load-url: %s" url)
  (let [engine (.getEngine (.lookup (.getScene @(:root @app-ui-state)) "#web-browser"))]
    (run-later
    (.load engine url))))
;; The handler responding to changes in the URL text field:
(defmethod handle-event :load-url
  [state {:keys [fn-fx/includes]}]
  (let [new-url (get-in includes [:url-field :text])]
    (load-url new-url)
    (assoc-in state [:current-url] new-url)))

I've added the full webview example in a pull request #67 . This shows it's possible to get a simple example going, but I think @roti's point stands that it would be nice to have a handle to the underlying FX nodes from the DOM nodes.