package org.argeo.app.geo.swt;
-import java.util.Locale;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.function.Function;
-import org.argeo.api.cms.CmsLog;
import org.argeo.app.geo.ux.JsImplementation;
import org.argeo.app.geo.ux.MapPart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.browser.Browser;
-import org.eclipse.swt.browser.BrowserFunction;
-import org.eclipse.swt.browser.ProgressEvent;
-import org.eclipse.swt.browser.ProgressListener;
-import org.eclipse.swt.layout.GridData;
+import org.argeo.app.swt.js.SwtBrowserJsPart;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
/**
- * An SWT implementation of {@link MapPart} based on JavaScript execute in a
- * {@link Browser} control.
+ * An SWT implementation of {@link MapPart} based on JavaScript.
*/
-public class SwtJSMapPart implements MapPart {
+public class SwtJSMapPart extends SwtBrowserJsPart implements MapPart {
static final long serialVersionUID = 2713128477504858552L;
- private final static CmsLog log = CmsLog.getLog(SwtJSMapPart.class);
-
- private final static String GLOBAL_THIS_ = "globalThis.";
-
- private final Browser browser;
-
- private final CompletableFuture<Boolean> pageLoaded = new CompletableFuture<>();
-
private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
private final String mapName;// = "argeoMap";
public SwtJSMapPart(String mapName, Composite parent, int style) {
+ super(parent, style, "/pkg/org.argeo.app.geo.js/index.html");
this.mapName = mapName;
- browser = new Browser(parent, 0);
- browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
- browser.setUrl("/pkg/org.argeo.app.geo.js/index.html");
- browser.addProgressListener(new ProgressListener() {
- static final long serialVersionUID = 1L;
-
- @Override
- public void completed(ProgressEvent event) {
- try {
- // create map
- browser.execute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
- loadExtensions();
- pageLoaded.complete(true);
- } catch (Exception e) {
- log.error("Cannot create map in browser", e);
- pageLoaded.complete(false);
- }
- }
-
- @Override
- public void changed(ProgressEvent event) {
- }
- });
}
- public Control getControl() {
- return browser;
+ @Override
+ protected void init() {
+ // create map
+ doExecute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
}
/*
return callMethod(getJsMapVar(), methodCall, args);
}
- protected CompletionStage<Object> callMethod(String jsObject, String methodCall, Object... args) {
- return evaluate(jsObject + '.' + methodCall, args);
- }
-
private String getJsMapVar() {
- return GLOBAL_THIS_ + mapName;
- }
-
- /**
- * Execute this JavaScript on the client side after making sure that the page
- * has been loaded and the map object has been created.
- *
- * @param js the JavaScript code, possibly formatted according to
- * {@link String#format}, with {@link Locale#ROOT} as locale (for
- * stability of decimal separator, as expected by JavaScript.
- * @param args the optional arguments of
- * {@link String#format(String, Object...)}
- */
- protected CompletionStage<Object> evaluate(String js, Object... args) {
- CompletableFuture<Object> res = pageLoaded.thenApply((ready) -> {
- if (!ready)
- throw new IllegalStateException("Map " + mapName + " is not initialised.");
- Object result = browser.evaluate(String.format(Locale.ROOT, js, args));
- return result;
- });
- return res.minimalCompletionStage();
- }
-
- protected void loadExtension(String url) {
-// String js = """
-// var script = document.createElement("script");
-// script.src = '%s';
-// document.head.appendChild(script);
-// """;
-// browser.evaluate(String.format(Locale.ROOT, js, url));
- browser.evaluate(String.format(Locale.ROOT, "import('%s')", url));
- }
-
- /** To be overridden with calls to {@link #loadExtension(String)}. */
- protected void loadExtensions() {
-
+ return getJsVarName(mapName);
}
/*
}
protected void addCallback(String suffix, Function<Object[], Object> toDo) {
- pageLoaded.thenAccept((ready) -> {
- // browser functions must be directly on window (RAP specific)
- new BrowserFunction(browser, mapName + "__on" + suffix) {
-
- @Override
- public Object function(Object[] arguments) {
- Object result = toDo.apply(arguments);
- return result;
- }
-
- };
- browser.execute(getJsMapVar() + ".callbacks['on" + suffix + "']=window." + mapName + "__on" + suffix + ";");
+ getReadyStage().thenAccept((ready) -> {
+ String functionName = createJsFunction(mapName + "__on" + suffix, toDo);
+ doExecute(getJsMapVar() + ".callbacks['on" + suffix + "']=" + functionName + ";");
callMethod(mapName, "enable" + suffix + "()");
});
}
--- /dev/null
+package org.argeo.app.swt.js;
+
+import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.function.Function;
+
+import org.argeo.api.cms.CmsLog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.BrowserFunction;
+import org.eclipse.swt.browser.ProgressEvent;
+import org.eclipse.swt.browser.ProgressListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * A part using a {@link Browser} and remote JavaScript components on the client
+ * side.
+ */
+public class SwtBrowserJsPart {
+ private final static CmsLog log = CmsLog.getLog(SwtBrowserJsPart.class);
+
+ private final static String GLOBAL_THIS_ = "globalThis.";
+
+ private final Browser browser;
+ private final CompletableFuture<Boolean> pageLoaded = new CompletableFuture<>();
+
+ public SwtBrowserJsPart(Composite parent, int style, String url) {
+ this.browser = new Browser(parent, 0);
+ if (parent.getLayout() instanceof GridLayout)
+ browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ // TODO other layouts
+
+ browser.setUrl(url);
+ browser.addProgressListener(new ProgressListener() {
+ static final long serialVersionUID = 1L;
+
+ @Override
+ public void completed(ProgressEvent event) {
+ try {
+ init();
+ loadExtensions();
+ pageLoaded.complete(true);
+ } catch (Exception e) {
+ log.error("Cannot initialise " + url + " in browser", e);
+ pageLoaded.complete(false);
+ }
+ }
+
+ @Override
+ public void changed(ProgressEvent event) {
+ }
+ });
+ }
+
+ /*
+ * LIFECYCLE
+ */
+
+ /** Called when the page has been loaded. */
+ protected void init() {
+ }
+
+ /** To be overridden with calls to {@link #loadExtension(String)}. */
+ protected void loadExtensions() {
+
+ }
+
+ protected void loadExtension(String url) {
+// String js = """
+// var script = document.createElement("script");
+// script.src = '%s';
+// document.head.appendChild(script);
+// """;
+// browser.evaluate(String.format(Locale.ROOT, js, url));
+ browser.evaluate(String.format(Locale.ROOT, "import('%s')", url));
+ }
+
+ protected CompletionStage<Boolean> getReadyStage() {
+ return pageLoaded.minimalCompletionStage();
+ }
+
+ /*
+ * JAVASCRIPT ACCESS
+ */
+
+ /**
+ * Execute this JavaScript on the client side after making sure that the page
+ * has been loaded and the map object has been created.
+ *
+ * @param js the JavaScript code, possibly formatted according to
+ * {@link String#format}, with {@link Locale#ROOT} as locale (for
+ * stability of decimal separator, as expected by JavaScript.
+ * @param args the optional arguments of
+ * {@link String#format(String, Object...)}
+ */
+ protected CompletionStage<Object> evaluate(String js, Object... args) {
+ CompletableFuture<Object> res = pageLoaded.thenApply((ready) -> {
+ if (!ready)
+ throw new IllegalStateException("Component is not initialised.");
+ Object result = browser.evaluate(String.format(Locale.ROOT, js, args));
+ return result;
+ });
+ return res.minimalCompletionStage();
+ }
+
+ /** @return the globally usable function name. */
+ protected String createJsFunction(String name, Function<Object[], Object> toDo) {
+ // browser functions must be directly on window (RAP specific)
+ new BrowserFunction(browser, name) {
+
+ @Override
+ public Object function(Object[] arguments) {
+ Object result = toDo.apply(arguments);
+ return result;
+ }
+
+ };
+ return "window." + name;
+ }
+
+ /** Directly executes */
+ protected void doExecute(String js, Object... args) {
+ browser.execute(String.format(Locale.ROOT, js, args));
+ }
+
+ protected CompletionStage<Object> callMethod(String jsObject, String methodCall, Object... args) {
+ return evaluate(jsObject + '.' + methodCall, args);
+ }
+
+ protected String getJsVarName(String name) {
+ return GLOBAL_THIS_ + name;
+ }
+
+ /*
+ * ACCESSORS
+ */
+
+ public Control getControl() {
+ return browser;
+ }
+
+}