Comments (12)
Hmm, you could compose key subscriptions with function subscriptions, like in this example:
(defn user-sub [ctx user-id]
(get (fx/sub ctx :users) user-id)
(defn user-name-sub [ctx user-id]
(:name (fx/sub ctx user-sub user-id)))
from cljfx.
So the idea is to use more granular subscription functions to build a subscription dependency graph from the whole app state map. Don't forget to use core.cache
, or context cache will eat all your memory!
from cljfx.
Yeah, so this did occur to me... but does this have the intended effect ? Wouldn't any change in any of the :users
trigger an update on everywhere you are subscribed to a :name
?
Or is the idea that it would trigger an update, but once you hit
(get (fx/sub ctx :users) user-id)
What you got back would be identical and the update would terminate prematurely and return a cached value ?
from cljfx.
Exactly!
Going from {:users {0 {:name "geokon"}}}
to {:users {0 {:name "geokon"} 1 {:name "vlaaad"}}}
will require recomputation of (fx/sub ctx user-sub 0)
subscriptions, but since that will return the same value as before, it won't require recomputation of (fx/sub ctx user-name-sub 0)
. It is described in Subscriptions and Contexts readme section :)
from cljfx.
Alright! I was just worried I misunderstood. I guess the term "subscription" is a bit of a misnomer b/c an update of the state/context is not triggering the subscribers in any sense - and if I understand correctly, all the subscriptions are rechecked on each GUI tree regeneration.
In my mental model I thought it was an actual subscription in that each node's subscriptions are recursively traversed to get a dependency list and only affected branches of the trees are regenerated when values are updated (that would be very tricky to implement haha)
from cljfx.
In my mental model I thought it was an actual subscription in that each node's subscriptions are recursively traversed to get a dependency list and only affected branches of the trees are regenerated when values are updated (that would be very tricky to implement haha)
It is like that! For every subscription function, the context tracks its dependencies and then uses these dependencies to determine if it should call subscription function again or use cached result if results for dependencies didn't change. It was super tricky to implement!
from cljfx.
haha, yes it does sounds really tricky, but it's elegant the way it works! :)
The "issue" is that at the end of the day every subscription needs to be get checked to get that cache hit (like in the example you gave - you get the user, see it's the same user - return the cached name) so that you can redraw the whole GUI tree
What I had misunderstood was a bit different haha. Imagining you only are subscribing to keys (subscription functions would be and added complication on top), you could have every key in the context/state has an associate list of subscribed nodes. When the key is modified then the associated nodes are regenerated. The rest of the tree would just be left as-is and wouldn't need to be checked
But in way I actually like your solution more. The subscriptions and context are decoupled from the GUI tree. There are no assumptions about the data structure of the state. You can test it directly and it's much easier to reason about. The overhead is negligible. It's just my C++ brain that inverted the direction of control here :)
Thanks for walking me through it. I now get how to get this all plugged in (famous last words :)) . As always I really appreciate your help here. This is going to greatly clean up my code
from cljfx.
You are welcome :)
You got me thinking about contexts, so I released a small update: 1.6.9 :D
One thing I changed is I fixed invalidation mechanism for contexts so it does not require iterating over all existing cache entries when new context is created from the old one. That's a minor performance improvement that bothered me with contexts for a long time.
Another, more noticable change, is that you can now subscribe to just a context itself with (fx/sub ctx)
, so you can have full access to the context if you need it.
from cljfx.
Oops. Looks like force-pushes don't kill commits permanently.. Typoes are immortalized in Github :)
Thank you some much again for the help. It took me quite a while to refactor everything with contexts/subscriptions - but the end result is significantly better and very decoupled. I'm extremely pleased with the result!
Once I deal with packaging (I get weird issues with jdeps
/jlink
- have you had any luck minimizing the bundled run time?) I'll need to work on the meta data for the project now (README/wiki/page etc.) and I'll make sure to mention you. This project deserves a lot more traction
from cljfx.
You are welcome! :)
I didn't look into minimizing bundle size, I would start with looking at created uberjars to determine what takes so much space. One thing that comes to mind is org.openjfx/javafx-web
that cljfx depends on by default — it has a packaged browser engine (webkit) that you can exclude if you aren't using web views in your cljfx app.
from cljfx.
Yeah, this I've already done. It's a bit messy, you run clj -Stree
and then sorta guess which parts you probably aren't touching. Then you can add exclusions in your deps.edn
.
So I end up with:
{:deps
{org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/core.cache {:mvn/version "1.0.207"}
org.boofcv/boofcv-core {:mvn/version "0.35"
:exclusions [org.boofcv/boofcv-recognition]}
cljfx {:mvn/version "1.6.9"
:exclusions [org.openjfx/javafx-web
org.openjfx/javafx-media]}
org.clojure/data.csv {:mvn/version "0.1.4"} ;; data injestion
thi.ng/geom {:mvn/version "1.0.0-RC4"
:exclusions [org.clojure/clojurescript
org.jogamp.jogl/jogl-all]}
guru.nidi.com.kitfox/svgSalamander {:mvn/version "1.1.3"}
}
I'm sure you can get a higher graularity.. but past a certain point you stop seeing large dividends in terms of file size.
I tried to minimize the uberjar with Proguard but I think b/c Clojure's late binding it just can't figure out what's removable.
The issue is that theoretically jlink
is supposed to just give you the parts of the runtime you need/use. But with all this zillions of unused vestigial files left around (that you didn't catch in the exlusion) jlink
ends up sort of including the while runtime - ie all the modules. And running jdeps
produces a crash related to cljfx/javafx - so I'm not entirely sure how to inspect things further. I think I'm going to have to just accept that the application will never get to an "emailable" size and it will have to be hundreds of megabytes and hosted on github :) But if you do ever look into this further please do let me know!
from cljfx.
For reference the starting uberjar is down to about 20MB
And well I just tried to create an AppImage and it turned out to be 1.1GB with ~44,000 files inside haha. It does launch very quickly though ;)
Making an AppImage with the AOT version is ~200MB with ~4,000 files
from cljfx.
Related Issues (20)
- Question : list-view/with-selection-props gives error " No such prop: :v-box/vgrow {:prop :v-box/vgrow}" HOT 3
- tooltip extension in a gridpane HOT 2
- Spinner Example
- controlsfx, CheckComboBox HOT 7
- depstar is deprecated and archived; switch to tools.build HOT 2
- The example project fails with (UnsupportedOperationException) HOT 6
- Mouseclick and textfield ?
- Support for CheckBoxTreeItem/CheckBoxTreeCell
- Method for adding another context-like property HOT 1
- Runnning examples/e20_markdown_editor results in NullPointerException: HOT 1
- How do I use StyledTextArea or any new class? HOT 2
- Broken encoding for Sanskrit text in WebView HOT 2
- How do I exit the packaged app on window close or menu quit command HOT 2
- Unable to open DISPLAY HOT 4
- Trying to understand the lifecycle again
- Using cljfx with Gradle HOT 2
- Problem using leiningen HOT 2
- how to pass args to constructors HOT 3
- how to set the background of a region HOT 5
- Children: duplicate children added: when using pie-chart and dynamic rendering HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cljfx.