1.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Sep 12 17:18:18 2013 +0200
1.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Sep 12 17:59:10 2013 +0200
1.3 @@ -36,6 +36,7 @@
1.4 */
1.5 public final class FnUtils {
1.6 private static final ThreadLocal<Fn.Presenter> CURRENT = new ThreadLocal<Fn.Presenter>();
1.7 + private static final ThreadLocal<List<Runnable>> LATER = new ThreadLocal<List<Runnable>>();
1.8
1.9 private FnUtils() {
1.10 }
1.11 @@ -130,8 +131,28 @@
1.12 }
1.13
1.14 public static Fn.Presenter currentPresenter(Fn.Presenter p) {
1.15 + List<Runnable> list = LATER.get();
1.16 + LATER.set(null);
1.17 + if (list != null) for (Runnable runnable : list) {
1.18 + try {
1.19 + runnable.run();
1.20 + } catch (Throwable t) {
1.21 + t.printStackTrace();
1.22 + }
1.23 + }
1.24 +
1.25 Fn.Presenter prev = CURRENT.get();
1.26 CURRENT.set(p);
1.27 return prev;
1.28 }
1.29 +
1.30 + public static void runLater(Runnable r) {
1.31 + currentPresenter();
1.32 + List<Runnable> list = LATER.get();
1.33 + if (list == null) {
1.34 + list = new ArrayList<Runnable>();
1.35 + LATER.set(list);
1.36 + }
1.37 + list.add(r);
1.38 + }
1.39 }
2.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java Thu Sep 12 17:18:18 2013 +0200
2.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java Thu Sep 12 17:59:10 2013 +0200
2.3 @@ -22,7 +22,13 @@
2.4
2.5 import java.io.IOException;
2.6 import java.io.InputStream;
2.7 +import java.util.ArrayList;
2.8 +import java.util.HashMap;
2.9 +import java.util.HashSet;
2.10 +import java.util.List;
2.11 +import java.util.Map;
2.12 import java.util.ServiceLoader;
2.13 +import java.util.Set;
2.14 import java.util.logging.Logger;
2.15 import javafx.application.Platform;
2.16 import net.java.html.js.JavaScriptBody;
2.17 @@ -47,10 +53,12 @@
2.18 * @author Jaroslav Tulach <jtulach@netbeans.org>
2.19 */
2.20 public final class FXContext
2.21 -implements Technology.BatchInit<JSObject>, Transfer, WSTransfer<LoadWS> {
2.22 +implements Technology.BatchInit<JSObject>, Transfer, WSTransfer<LoadWS>, Runnable {
2.23 static final Logger LOG = Logger.getLogger(FXContext.class.getName());
2.24 private static Boolean javaScriptEnabled;
2.25 private final Fn.Presenter browserContext;
2.26 +
2.27 + private final Map<JSObject,Set<String>> notifyMutated = new HashMap<JSObject, Set<String>>();
2.28
2.29 public FXContext(Fn.Presenter browserContext) {
2.30 this.browserContext = browserContext;
2.31 @@ -105,8 +113,16 @@
2.32 }
2.33
2.34 @Override
2.35 - public void valueHasMutated(JSObject data, String propertyName) {
2.36 - Knockout.valueHasMutated(data, propertyName);
2.37 + public synchronized void valueHasMutated(JSObject data, String propertyName) {
2.38 + if (notifyMutated.isEmpty()) {
2.39 + FnUtils.runLater(this);
2.40 + }
2.41 + Set<String> ss = notifyMutated.get(data);
2.42 + if (ss == null) {
2.43 + ss = new HashSet<String>();
2.44 + notifyMutated.put(data, ss);
2.45 + }
2.46 + ss.add(propertyName);
2.47 }
2.48
2.49 @Override
2.50 @@ -149,19 +165,7 @@
2.51
2.52 @Override
2.53 public void runSafe(final Runnable r) {
2.54 - class Wrap implements Runnable {
2.55 - @Override public void run() {
2.56 - Fn.Presenter prev = FnUtils.currentPresenter(browserContext);
2.57 - try {
2.58 - r.run();
2.59 - } finally {
2.60 - FnUtils.currentPresenter(prev);
2.61 - }
2.62 -
2.63 - }
2.64 - }
2.65 - Wrap w = new Wrap();
2.66 -
2.67 + Wrap w = new Wrap(r);
2.68 if (Platform.isFxApplicationThread()) {
2.69 w.run();
2.70 } else {
2.71 @@ -183,6 +187,24 @@
2.72 public void close(LoadWS socket) {
2.73 socket.close();
2.74 }
2.75 +
2.76 + @Override
2.77 + public void run() {
2.78 + List<JSObject> objs = new ArrayList<JSObject>();
2.79 + List<String> strngs = new ArrayList<String>();
2.80 + synchronized (this) {
2.81 + for (Map.Entry<JSObject, Set<String>> entry : notifyMutated.entrySet()) {
2.82 + for (String propertyName : entry.getValue()) {
2.83 + objs.add(entry.getKey());
2.84 + strngs.add(propertyName);
2.85 + }
2.86 + }
2.87 + notifyMutated.clear();
2.88 + }
2.89 + Object arrObj = Knockout.toArray(objs.toArray());
2.90 + Object arrStr = strngs.toArray();
2.91 + Knockout.valuesMutated(arrObj, arrStr);
2.92 + }
2.93
2.94 @ServiceProvider(service = Contexts.Provider.class)
2.95 public static final class Prvdr implements Contexts.Provider {
2.96 @@ -199,4 +221,24 @@
2.97 }
2.98 }
2.99 }
2.100 +
2.101 + private class Wrap implements Runnable {
2.102 + private final Runnable r;
2.103 +
2.104 + public Wrap(Runnable r) {
2.105 + this.r = r;
2.106 + }
2.107 +
2.108 + @Override
2.109 + public void run() {
2.110 + Fn.Presenter prev = FnUtils.currentPresenter(browserContext);
2.111 + try {
2.112 + r.run();
2.113 + } finally {
2.114 + FnUtils.currentPresenter(prev);
2.115 + }
2.116 +
2.117 + }
2.118 + }
2.119 +
2.120 }
3.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java Thu Sep 12 17:18:18 2013 +0200
3.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java Thu Sep 12 17:59:10 2013 +0200
3.3 @@ -48,15 +48,19 @@
3.4 return KObject.call("array", arr);
3.5 }
3.6
3.7 - @JavaScriptBody(args = { "model", "prop" }, body =
3.8 - "if (model) {\n"
3.9 - + " var koProp = model[prop];\n"
3.10 - + " if (koProp && koProp['valueHasMutated']) {\n"
3.11 - + " koProp['valueHasMutated']();\n"
3.12 + @JavaScriptBody(args = { "models", "props" }, body =
3.13 + "for (var i = 0; i < models.length; i++) { \n"
3.14 + + " var model = models[i];\n"
3.15 + + " var prop = props[i];\n"
3.16 + + " if (model) {\n"
3.17 + + " var koProp = model[prop];\n"
3.18 + + " if (koProp && koProp['valueHasMutated']) {\n"
3.19 + + " koProp['valueHasMutated']();\n"
3.20 + + " }\n"
3.21 + " }\n"
3.22 + "}\n"
3.23 )
3.24 - public native static void valueHasMutated(JSObject model, String prop);
3.25 + native static void valuesMutated(Object arrObjs, Object arrProps);
3.26
3.27 @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
3.28 native static void applyBindings(Object bindings);
4.1 --- a/ko-fx/src/test/java/org/apidesign/html/kofx/LessCallbacksCheck.java Thu Sep 12 17:18:18 2013 +0200
4.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/LessCallbacksCheck.java Thu Sep 12 17:59:10 2013 +0200
4.3 @@ -57,4 +57,18 @@
4.4 assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
4.5 }
4.6 }
4.7 +
4.8 + @KOTest public void dontCallForUpdatedValueBackToJavaVM() {
4.9 + LessCalls m = new LessCalls(10).applyBindings();
4.10 + assert m.getPlusOne() == 11 : "Expecting 11: " + m.getPlusOne();
4.11 +
4.12 + m.setValue(11);
4.13 + assert m.getPlusOne() == 12 : "Expecting 12: " + m.getPlusOne();
4.14 +
4.15 + assert sw != null : "StringWriter should be initialized: " + sw;
4.16 +
4.17 + if (sw.toString().contains("$JsCallbacks$")) {
4.18 + assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
4.19 + }
4.20 + }
4.21 }