Browse Source

REPORT-62688 组件商城面向风格优化

【问题原因】
优化JxBrowser接口
1. 减少监听器注册接口的数目
2. 增减全局下载任务的监听接口,以实现列表项/详情页/历史版本等不同页面下载任务状态的同步
3. JS可在Java对象上添加自定义属性,从而在JS环境中跟踪指定下载任务的状态.
4. mini商城中主题下载后弹出的一系列交互窗口的父窗口应当
是mini商城窗口

【改动思路】
同上
feature/x
Starryi 3 years ago
parent
commit
4452ccf7ed
  1. 9
      designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeGridPagesDialog.java
  2. 7
      designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeUsingDialog.java
  3. 3
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/installation/ComponentInstallation.java
  4. 3
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/installation/ComponentsPackageInstallation.java
  5. 22
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/installation/TemplateThemeInstallation.java
  6. 2
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/MiniComponentShopDialog.java
  7. 205
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/bridge/NativeProductBridge.java
  8. 54
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/bridge/NativeTaskBridge.java
  9. 29
      designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/bridge/SafeJSFunctionInvoker.java

9
designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeGridPagesDialog.java

@ -7,6 +7,7 @@ import com.fr.design.mainframe.theme.TemplateThemeGridPagesPane;
import com.fr.design.mainframe.theme.TemplateThemeGridPane;
import javax.swing.JPanel;
import java.awt.Window;
/**
* @author Starryi
@ -19,13 +20,17 @@ public class TemplateThemeGridPagesDialog extends TemplateThemeDialog implements
protected TemplateThemeGridPagesPane overallPane;
public TemplateThemeGridPagesDialog() {
super(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Using_Dialog_Title"), CONTENT_WIDTH, CONTENT_HEIGHT);
public TemplateThemeGridPagesDialog(Window parentWindow) {
super(parentWindow, Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Using_Dialog_Title"), CONTENT_WIDTH, CONTENT_HEIGHT);
setupContentPane();
setupActionButtons();
}
public TemplateThemeGridPagesDialog() {
this(DesignerContext.getDesignerFrame());
}
@Override
protected JPanel createContentPane() {
overallPane = new TemplateThemeGridPagesPane(this);

7
designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeUsingDialog.java

@ -1,11 +1,18 @@
package com.fr.design.mainframe.theme.dialog;
import java.awt.Window;
/**
* @author Starryi
* @version 1.0
* Created by Starryi on 2021/8/13
*/
public class TemplateThemeUsingDialog extends TemplateThemeGridPagesDialog {
public TemplateThemeUsingDialog(Window parentWindow) {
super(parentWindow);
overallPane.showThemeUsingPane();
}
public TemplateThemeUsingDialog() {
super();
overallPane.showThemeUsingPane();

3
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/installation/ComponentInstallation.java

@ -19,6 +19,7 @@ import com.fr.stable.StringUtils;
import javax.swing.SwingWorker;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
/**
@ -75,7 +76,7 @@ public class ComponentInstallation extends AsyncInstallation {
} catch (InterruptedException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
} catch (ExecutionException | CancellationException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}

3
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/installation/ComponentsPackageInstallation.java

@ -15,6 +15,7 @@ import javax.swing.SwingWorker;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
/**
@ -164,7 +165,7 @@ public class ComponentsPackageInstallation extends AsyncInstallation {
} catch (InterruptedException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
} catch (ExecutionException | CancellationException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}

22
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/installation/TemplateThemeInstallation.java

@ -25,6 +25,8 @@ import javax.swing.SwingWorker;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
/**
@ -33,13 +35,19 @@ import java.util.concurrent.ExecutionException;
* Created by Starryi on 2022/1/5
*/
public class TemplateThemeInstallation extends AsyncInstallation {
private final Window parentWindow;
private final String themePath;
private SwingWorker<FormTheme, Void> worker;
public TemplateThemeInstallation(String themePath) {
public TemplateThemeInstallation(Window parentWindow, String themePath) {
this.parentWindow = parentWindow;
this.themePath = themePath;
}
public TemplateThemeInstallation(String themePath) {
this(DesignerContext.getDesignerFrame(), themePath);
}
@Override
public void install() {
fetchTheme();
@ -66,7 +74,7 @@ public class TemplateThemeInstallation extends AsyncInstallation {
} catch (InterruptedException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
} catch (ExecutionException | CancellationException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
@ -78,7 +86,7 @@ public class TemplateThemeInstallation extends AsyncInstallation {
private boolean checkAuthority() {
if (!WorkContext.getCurrent().isRoot()) {
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(),
FineJOptionPane.showMessageDialog(parentWindow,
Toolkit.i18nText("Fine-Design_Share_Download_Suitable_Theme_No_Authority_Tip_Message"),
Toolkit.i18nText("Fine-Design_Share_Download_Suitable_Theme_No_Authority_Tip_Title"),
JOptionPane.WARNING_MESSAGE);
@ -87,7 +95,7 @@ public class TemplateThemeInstallation extends AsyncInstallation {
String userName = DesignerEnvManager.getEnvManager().getDesignerLoginUsername();
if (StringUtils.isEmpty(userName)) {
DesignerLoginHelper.showLoginDialog(DesignerLoginSource.NORMAL);
DesignerLoginHelper.showLoginDialog(DesignerLoginSource.NORMAL, new HashMap<>(), parentWindow);
return false;
}
@ -108,7 +116,7 @@ public class TemplateThemeInstallation extends AsyncInstallation {
return theme;
} else {
String newName = (String) FineJOptionPane.showInputDialog(
DesignerContext.getDesignerFrame(),
parentWindow,
Toolkit.i18nText("Fine-Design_Share_Rename_Suitable_Theme_Tip"),
Toolkit.i18nText("Fine-Design_Basic_Rename"),
FineJOptionPane.QUESTION_MESSAGE, null, null,
@ -147,12 +155,12 @@ public class TemplateThemeInstallation extends AsyncInstallation {
JTemplate<?, ?> currentTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (currentTemplate.getUsingTemplateThemeConfig() instanceof FormThemeConfig) {
TemplateThemeUsingDialog dialog = new TemplateThemeUsingDialog();
TemplateThemeUsingDialog dialog = new TemplateThemeUsingDialog(parentWindow);
dialog.addWindowListener(new UsingDialogAdapter(theme, currentTemplate));
dialog.setVisible(true);
} else {
FineJOptionPane.showConfirmDialog(
MiniComponentShopDialog.getInstance().getContentPane(),
MiniComponentShopDialog.getInstance().getWindow(),
Toolkit.i18nText("Fine-Design_Share_Download_Suitable_Theme_Success_Tip"),
"",
FineJOptionPane.YES_NO_OPTION

2
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/MiniComponentShopDialog.java

@ -3,6 +3,7 @@ package com.fr.design.mainframe.share.ui.online.mini;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.UIDialog;
import com.fr.design.mainframe.share.mini.MiniShopDisposingChecker;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.general.IOUtils;
import javax.swing.JFrame;
@ -93,6 +94,7 @@ public class MiniComponentShopDialog {
}
}
});
GUICoreUtils.centerWindow(frame);
return frame;
}

205
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/bridge/NativeProductBridge.java

@ -17,15 +17,18 @@ import com.fr.form.share.Group;
import com.fr.form.share.SharableWidgetProvider;
import com.fr.form.share.bean.OnlineShareWidget;
import com.fr.form.share.group.DefaultShareGroupManager;
import com.fr.json.JSONArray;
import com.fr.json.JSONObject;
import com.fr.stable.StringUtils;
import com.teamdev.jxbrowser.chromium.JSAccessible;
import com.teamdev.jxbrowser.chromium.JSFunction;
import com.teamdev.jxbrowser.chromium.JSObject;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import java.awt.Window;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
@ -35,6 +38,13 @@ import java.util.Set;
*/
public class NativeProductBridge {
private final JSObject window;
private static final Map<String, ComponentInstallationTask> executingComponentInstallationTasks = new HashMap<>();
private static final Map<String, ComponentsPackageInstallationTask> executingComponentsPackageInstallationTasks = new HashMap<>();
private static final Map<String, TemplateThemeInstallationTask> executingTemplateThemeInstallationTasks = new HashMap<>();
private final Map<String, Set<JSFunction>> componentDownloadTaskStartListeners = new HashMap<>();
private final Map<String, Set<JSFunction>> componentsPackageDownloadTaskStartListeners = new HashMap<>();
private final Map<String, Set<JSFunction>> themeDownloadTaskStartListeners = new HashMap<>();
public NativeProductBridge(JSObject window) {
this.window = window;
@ -42,30 +52,116 @@ public class NativeProductBridge {
@JSAccessible
@JSBridge
public String getDownloadedProductIds() {
Set<String> uuidList = new HashSet<>();
public boolean isProductDownloaded(String uuid) {
for (Group group : DefaultShareGroupManager.getInstance().getAllGroup()) {
SharableWidgetProvider[] widgetProviderList = group.getAllBindInfoList();
for (SharableWidgetProvider widget: widgetProviderList) {
if (StringUtils.isNotEmpty(widget.getId())) {
uuidList.add(widget.getId());
if (StringUtils.equals(uuid, widget.getId())) {
return true;
}
}
}
JSONArray array = JSONArray.create(uuidList);
return array.toString();
return false;
}
@JSAccessible
@JSBridge
public void addProductDownloadTaskStartListener(String json, JSFunction function) {
JSONObject object = new JSONObject(json);
OnlineShareWidget widget = OnlineShareWidget.parseFromJSONObject(object);
String uuid = widget.getUuid();
boolean isPackage = widget.isWidgetPackage();
Map<String, Set<JSFunction>> downloadTaskGetters =
isPackage ? componentsPackageDownloadTaskStartListeners : componentDownloadTaskStartListeners;
Set<JSFunction> startListeners = downloadTaskGetters.get(widget.getUuid());
if (startListeners == null) {
startListeners = new HashSet<>();
}
startListeners.add(function);
downloadTaskGetters.put(widget.getUuid(), startListeners);
}
@JSAccessible
@JSBridge
public void removeProductDownloadTaskStartListener(String json, JSFunction function) {
JSONObject object = new JSONObject(json);
OnlineShareWidget widget = OnlineShareWidget.parseFromJSONObject(object);
String uuid = widget.getUuid();
boolean isPackage = widget.isWidgetPackage();
Map<String, Set<JSFunction>> downloadTaskGetters = isPackage ? componentsPackageDownloadTaskStartListeners : componentDownloadTaskStartListeners;
Set<JSFunction> startListeners = downloadTaskGetters.get(uuid);
if (startListeners == null) {
startListeners = new HashSet<>();
}
startListeners.remove(function);
downloadTaskGetters.put(uuid, startListeners);
}
@JSAccessible
@JSBridge
public Object getExecutingProductDownloadTask(String json) {
JSONObject object = new JSONObject(json);
OnlineShareWidget widget = OnlineShareWidget.parseFromJSONObject(object);
String uuid = widget.getUuid();
boolean isPackage = widget.isWidgetPackage();
Map<String, ? extends NativeTaskBridge> executingDownloadTask =
isPackage ? executingComponentsPackageInstallationTasks : executingComponentInstallationTasks;
NativeTaskBridge task = executingDownloadTask.get(uuid);
if (task != null) {
task.checkJSEnvChange(this.window);
}
return task;
}
@JSAccessible
@JSBridge
public Object createProductDownloadTask(String json) {
return new ComponentInstallationTask(window, json);
JSONObject object = new JSONObject(json);
OnlineShareWidget widget = OnlineShareWidget.parseFromJSONObject(object);
int childrenCount = object.optInt("pkgsize", 0);
if (childrenCount > 0) {
return new ComponentsPackageInstallationTask(window, widget, childrenCount);
} else {
return new ComponentInstallationTask(window, widget);
}
}
@JSAccessible
@JSBridge
public void addThemeDownloadTaskStartListener(String themePath, JSFunction function) {
Set<JSFunction> startListeners = themeDownloadTaskStartListeners.get(themePath);
if (startListeners == null) {
startListeners = new HashSet<>();
}
startListeners.add(function);
themeDownloadTaskStartListeners.put(themePath, startListeners);
}
@JSAccessible
@JSBridge
public Object createProductsPackageDownloadTask(String json) {
return new ComponentsPackageInstallationTask(window, json);
public void removeThemeDownloadTaskStartListener(String themePath, JSFunction function) {
Set<JSFunction> startListeners = themeDownloadTaskStartListeners.get(themePath);
if (startListeners == null) {
startListeners = new HashSet<>();
}
startListeners.remove(function);
themeDownloadTaskStartListeners.put(themePath, startListeners);
}
@JSAccessible
@JSBridge
public Object getExecutingThemeDownloadTask(String themePath) {
NativeTaskBridge task = executingTemplateThemeInstallationTasks.get(themePath);
if (task != null) {
task.checkJSEnvChange(this.window);
}
return task;
}
@JSAccessible
@ -74,15 +170,13 @@ public class NativeProductBridge {
return new TemplateThemeInstallationTask(window, themePath);
}
public static class ComponentInstallationTask extends NativeTaskBridge {
public class ComponentInstallationTask extends NativeTaskBridge {
private final OnlineShareWidget widget;
private final ComponentInstallation action;
public ComponentInstallationTask(JSObject window, String widgetJson) {
public ComponentInstallationTask(JSObject window, OnlineShareWidget widget) {
super(window);
JSONObject object = new JSONObject(widgetJson);
widget = OnlineShareWidget.parseFromJSONObject(object);
this.widget = widget;
action = new ComponentInstallation(widget);
action.setActionListener(new AsyncInstallation.AsyncActionListener() {
@Override
@ -93,8 +187,8 @@ public class NativeProductBridge {
@Override
public void onSuccess() {
fireSuccessEvent(null);
LocalWidgetRepoPane.getInstance().refreshPane();
if (!DesignerContext.getDesignerFrame().isActive()) {
LocalWidgetRepoPane.getInstance().refreshPane();
FormWidgetDetailPane.getInstance().switch2Local();
EastRegionContainerPane.getInstance().switchTabTo(EastRegionContainerPane.KEY_WIDGET_LIB);
}
@ -147,19 +241,38 @@ public class NativeProductBridge {
}
});
}
@Override
protected void fireStartEvent(String event) {
executingComponentInstallationTasks.put(widget.getUuid(), this);
Set<JSFunction> startListeners = componentDownloadTaskStartListeners.get(widget.getUuid());
SafeJSFunctionInvoker.invoke(startListeners, window);
super.fireStartEvent(event);
}
@Override
protected void fireFailureEvent(String event) {
executingComponentInstallationTasks.remove(widget.getUuid());
super.fireFailureEvent(event);
}
@Override
protected void fireSuccessEvent(String event) {
executingComponentInstallationTasks.remove(widget.getUuid());
super.fireSuccessEvent(event);
}
}
public static class ComponentsPackageInstallationTask extends NativeTaskBridge {
public class ComponentsPackageInstallationTask extends NativeTaskBridge {
private final ComponentsPackageInstallation action;
private final OnlineShareWidget widget;
private final int childrenCount;
public ComponentsPackageInstallationTask(JSObject window, String widgetJson) {
public ComponentsPackageInstallationTask(JSObject window, OnlineShareWidget widget, int childrenCount) {
super(window);
JSONObject object = new JSONObject(widgetJson);
widget = OnlineShareWidget.parseFromJSONObject(object);
childrenCount = object.optInt("pkgsize", 0);
this.widget = widget;
this.childrenCount = childrenCount;
action = new ComponentsPackageInstallation(widget, childrenCount);
action.setActionListener(new AsyncInstallation.AsyncActionListener() {
@Override
@ -170,8 +283,8 @@ public class NativeProductBridge {
@Override
public void onSuccess() {
fireSuccessEvent(null);
LocalWidgetRepoPane.getInstance().refreshPane();
if (!DesignerContext.getDesignerFrame().isActive()) {
LocalWidgetRepoPane.getInstance().refreshPane();
FormWidgetDetailPane.getInstance().switch2Local();
EastRegionContainerPane.getInstance().switchTabTo(EastRegionContainerPane.KEY_WIDGET_LIB);
}
@ -232,13 +345,37 @@ public class NativeProductBridge {
}
});
}
@Override
protected void fireStartEvent(String event) {
executingComponentsPackageInstallationTasks.put(widget.getUuid(), this);
super.fireStartEvent(event);
Set<JSFunction> startListeners = componentsPackageDownloadTaskStartListeners.get(widget.getUuid());
SafeJSFunctionInvoker.invoke(startListeners, window);
}
@Override
protected void fireFailureEvent(String event) {
executingComponentsPackageInstallationTasks.remove(widget.getUuid());
super.fireFailureEvent(event);
}
@Override
protected void fireSuccessEvent(String event) {
executingComponentsPackageInstallationTasks.remove(widget.getUuid());
super.fireSuccessEvent(event);
}
}
public static class TemplateThemeInstallationTask extends NativeTaskBridge {
public class TemplateThemeInstallationTask extends NativeTaskBridge {
private final String themePath;
private final TemplateThemeInstallation action;
public TemplateThemeInstallationTask(JSObject window, String themePath) {
super(window);
action = new TemplateThemeInstallation(themePath);
this.themePath = themePath;
Window miniShopWindow = MiniComponentShopDialog.getInstance().getWindow();
action = new TemplateThemeInstallation(miniShopWindow, themePath);
action.setActionListener(new AsyncInstallation.AsyncActionListener() {
@Override
public void onProgress(double value) {
@ -283,5 +420,25 @@ public class NativeProductBridge {
}
});
}
@Override
protected void fireStartEvent(String event) {
executingTemplateThemeInstallationTasks.put(themePath, this);
Set<JSFunction> startListeners = themeDownloadTaskStartListeners.get(themePath);
SafeJSFunctionInvoker.invoke(startListeners, window);
super.fireStartEvent(event);
}
@Override
protected void fireFailureEvent(String event) {
executingTemplateThemeInstallationTasks.remove(themePath);
super.fireFailureEvent(event);
}
@Override
protected void fireSuccessEvent(String event) {
executingTemplateThemeInstallationTasks.remove(themePath);
super.fireSuccessEvent(event);
}
}
}

54
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/bridge/NativeTaskBridge.java

@ -7,6 +7,11 @@ import com.teamdev.jxbrowser.chromium.JSAccessible;
import com.teamdev.jxbrowser.chromium.JSFunction;
import com.teamdev.jxbrowser.chromium.JSObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Starryi
* @version 1.0
@ -14,39 +19,30 @@ import com.teamdev.jxbrowser.chromium.JSObject;
*/
public class NativeTaskBridge implements MiniShopNativeTask {
private final JSObject window;
protected JSFunction startCb;
protected JSFunction progressCb;
protected JSFunction successCb;
protected JSFunction failureCb;
private JSObject window;
protected final Set<JSFunction> statusCbs = new HashSet<>();
public NativeTaskBridge(JSObject window) {
this.window = window;
}
@JSBridge
@JSAccessible
public void setStartCallback(JSFunction cb) {
this.startCb = cb;
}
@JSBridge
@JSAccessible
public void setProgressCallback(JSFunction cb) {
this.progressCb = cb;
public void checkJSEnvChange(JSObject window) {
if (this.window != window) {
this.window = window;
this.statusCbs.clear();
}
}
@JSBridge
@JSAccessible
public void setSuccessCallback(JSFunction cb) {
this.successCb = cb;
public void addStatusCallback(JSFunction cb) {
this.statusCbs.add(cb);
}
@JSBridge
@JSAccessible
public void setFailureCallback(JSFunction cb) {
this.failureCb = cb;
public void removeStatusCallback(JSFunction cb) {
this.statusCbs.remove(cb);
}
@JSBridge
@ -60,30 +56,22 @@ public class NativeTaskBridge implements MiniShopNativeTask {
@Override
public void cancel() {
MiniShopNativeTaskManager.getInstance().removeCompletedTask(this);
fireFailureEvent(null);
}
protected void fireStartEvent(String event) {
MiniShopNativeTaskManager.getInstance().addStartedTask(this);
if (this.startCb != null) {
this.startCb.invoke(window, event);
}
SafeJSFunctionInvoker.invoke(statusCbs, window, "START", event);
}
protected void fireProgressEvent(String event) {
if (this.progressCb != null) {
this.progressCb.invoke(window, event);
}
SafeJSFunctionInvoker.invoke(statusCbs, window, "PROGRESS", event);
}
protected void fireSuccessEvent(String event) {
MiniShopNativeTaskManager.getInstance().removeCompletedTask(this);
if (this.successCb != null) {
this.successCb.invoke(window, event);
}
SafeJSFunctionInvoker.invoke(statusCbs, window, "SUCCESS", event);
}
protected void fireFailureEvent(String event) {
MiniShopNativeTaskManager.getInstance().removeCompletedTask(this);
if (this.failureCb != null) {
this.failureCb.invoke(window, event);
}
SafeJSFunctionInvoker.invoke(statusCbs, window, "FAILURE", event);
}
}

29
designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/bridge/SafeJSFunctionInvoker.java

@ -0,0 +1,29 @@
package com.fr.design.mainframe.share.ui.online.mini.bridge;
import com.teamdev.jxbrowser.chromium.JSFunction;
import com.teamdev.jxbrowser.chromium.JSObject;
import java.util.Collection;
/**
* @author Starryi
* @version 1.0
* Created by Starryi on 2022/1/17
*/
public class SafeJSFunctionInvoker {
public static void invoke(Collection<JSFunction> functions, JSObject instance, Object... args) {
if (functions != null) {
for (JSFunction function: functions) {
if (function != null && !function.getContext().isDisposed()) {
function.invoke(instance, args);
}
}
}
}
public static void invoke(JSFunction function, JSObject instance, Object... args) {
if (function != null && !function.getContext().isDisposed()) {
function.invoke(instance, args);
}
}
}
Loading…
Cancel
Save