Fork me on GitHub
#cljfx
<
2020-09-01
>
genekim05:09:48

(I just added @logbot to enable archiving in clojurians slack archive)

seancorfield06:09:47

@genekim The @zulip-mirror-bot archives it to Zulip and is free and searchable, just FYI (but having both bots is a good idea).

seancorfield06:09:13

See https://clojurians.zulipchat.com/#narrow/stream/180378-slack-archive/topic/cljfx for the searchable archive of this channel on Zulip back to February 25, 2019 (when I added the Zulip bot).

genekim06:09:08

@seancorfield Whoa… Thank you! Was despairing at the loss of all the messages here! Checking it out now! 🙂

seancorfield06:09:34

Zulip is also really nice for being entirely keyboard driven 🙂

genekim06:09:51

Freaking awesome, @seancorfield — never used Zulip until today. Look at all this history!!! And will do on the mirror bot!

seancorfield06:09:23

It's mostly how I keep track of everything happening in this Slack because it also shows the contents of threads, so I get to see stuff that wasn't even posted to the main channel. It's very useful to me as an Admin 🙂

genekim06:09:04

Uncanny. Reading about your depstar work on the hn.core app, found on the Zulip archive from this channel. 🙂

seancorfield06:09:44

Even tho' I'm one of the founding admins of this Slack, I would shed no tears if all 19,700 17,000 of us decamped to Zulip at some point 🙂

seancorfield06:09:57

(and I'm not an Admin of the Zulip space)

genekim06:09:04

Haha. Despite free Slack tier limits, I can’t even imagine the conditions that would lead to people fleeing Slack to another platform…. (Actually, I guess I can think of a few bad scenarios, and holy cow, those are scenarios I’d hope to avoid… 🙂

seancorfield06:09:48

It is somewhat amazing how much inertia a large community can have...

seancorfield06:09:18

If you find any channel that doesn't have the Zulip bot, please feel free to add it. Some of us Admins try to do that but it's hard to keep track of channels here at the rate people keep creating them!

vlaaad06:09:06
replied to a thread:

Great question! You uncovered an under-developed area of cljfx :) It's not that easy to do because what you want (e.g. scrolling to top) is not a state that can be expressed by cljfx with component description maps, but an event. Cljfx has support for events, but there is a problem: events you get are native JavaFX events. With map events, you can provide additional information to these events in the description, but there is no way to provide a reference to another node (e.g. a WebView instance). You can do it anyway with a hack: set WebView id so you can find it in the scene graph from any event. Example:

(fx/on-fx-thread
  (fx/create-component
    {:fx/type :stage
     :showing true
     :scene {:fx/type :scene
             :root {:fx/type :v-box
                    :children [{:fx/type :web-view
                                :url ""
                                :id "my-web-view"}
                               {:fx/type :button
                                :text "Scroll to top"
                                :on-action #(-> ^javafx.event.ActionEvent %
                                                ^javafx.scene.Node (.getTarget)
                                                (.getScene)
                                                ^javafx.scene.web.WebView (.lookup "#my-web-view")
                                                .getEngine
                                                (.executeScript "window.scrollTo(0,0)"))}]}}}))

genekim06:09:54

@vlaaad — thank you for this… I will give this a try tomorrow! Very exciting! (I’ve been studying the cljfx docs all day today between meetings, and the docs are superb. So many questions got answered.) Some questions that remain: 1. Can that code in the :on-action event be put into a function, and then called from a cljfx event? Are there any reasons not to do this? (I.e., imagine that button dispatches a cljx event, which would then execute that .executeScriptfunction). 2. Is there a way to store the results of the .executeScript, and send that to an cljfx event? (i.e., imagine an event that executes some WebView JS query as in #1, and then stores it in the app state, where other events could access/process it. ) Regarding #2, is the only way to pass values back is registering a Java object as a callback? (https://stackoverflow.com/questions/30390257/how-to-catch-return-value-from-javascript-in-javafx) Thanks again!

genekim07:09:17

…stand by… cleaning up/clarifying the question ^^^

😀 3
genekim07:09:56

Hopefully the question is clearer now…. 🙏

genekim07:09:19

@vlaaad #2 rewritten. Suppose I want to get the entire contents of the DOM. In Java, you apparently do, Document document = webEngine.getDocument(); I’m guessing that to do this in cljfx, we’d do the same trick: in the cljfx event, copy your technique (I.e., .lookup().getEngine().getDocumen , and then store the results in the state/context. Am I on target now?

genekim07:09:57

(Big ahas tonight: 1) you can use .lookup() to access the objects outside of cljfx; 2) the rendering of cljfx components are one way — they’re not meant to call functions which return values, to be stored in app state. Yes?)

vlaaad07:09:29

@genekim I’m not sure I understand your first question: the code in :on-action is already a function that is called from cljfx

genekim07:09:51

...just want to validate that moving that function into a “defn” and calling it from a cljfx event isn’t considered catastrophic, in poor taste, or immoral. :) 😂

vlaaad07:09:33

no-no, quite on the contrary!

vlaaad07:09:10

I’d say my example is in a poor taste — for the sake of simplicity — because putting anonymous function in a function component will create a new instance of that function on every re-render, causing unnecessary :on-action reassignments. For example:

(defn my-button [_]
  {:fx/type :button :on-action #(do-stuff %)})
Calling my-button will create a different instance of anonymous function. That’s why map event handlers are preferred: it’s easier to make maps “stable” in terms of equality

🎉 3
genekim07:09:33

Excellent! And I’m assuming use case #2 is similarly ok... When I get this working, i propose an example that does this get added? I spent an hour or two exploring how I might be able to do this in the extended lifecycle mechanisms you provide. Maybe even a sentence in the docs. I can propose some ideas in a PR?

vlaaad07:09:37

2. rendering cljfx components is one way, yes: cljfx description is a sort of a template, and you can reuse this template in the multiple times in the same description tree. That’s why my suggestion is a hack: if you have multiple web views, you’ll need to make sure they have different ids, so your event handlers can find both.

vlaaad07:09:25

I wouldn’t suggest taking component instances produced from the app state and putting them back in the state, this looks fragile — might cause infinite loops and will require tricky logic

vlaaad07:09:27

@genekim I would suggest you create an issue in cljfx repo, I’ll think how this kind of stuff can be done more idiomatically. I have a couple of ideas so far: 1. if your app has a single WebView, you can create it at the startup and put it into state straight away. Your cljfx component tree will need to take it from the state and put it in the right place in the tree. There is no built in lifecycle for that, but I had it done for cljfx projects a couple of times already, I think I’ll just add it to built in lifecycles. 2. it’s possible to define and pass references to nodes in the component tree with ext-let-refs/ext-get-ref, it should be possible to augment cljfx to make event handlers receive these nodes.

genekim15:09:37

Will do, today or tomorrow! Thanks for all the help, @vlaaad!!