* commit '9a8850eeced4bd640999ee0463dba3eb0106dea2': (84 commits) REPORT-91839 模板版本管理二期 修复bug REPORT-94637 单元测试修复 【问题原因】com.fr.design部分单测修复 【改动思路】详见https://kms.fineres.com/pages/viewpage.action?pageId=742496063 【review建议】 无jira任务 代码规范 REPORT-93559 补充兼容插件接口 REPORT-94637 单测修复 REPORT-94637 单测修复 REPORT-91839 模板版本管理二期 多删了一部分 REPORT-91839 模板版本管理二期 补充按钮交互,消除冗余代码 REPORT-91839 模板版本管理二期 优化判断新老模式的逻辑,事件响应改为Config准备后进行 REPORT-91839 模板版本管理二期 完善一下判断逻辑,增加自动保存标签设置 REPORT-91839 模板版本管理二期 修复规范问题 REPORT-91839 模板版本管理二期 REPORT-93559 提升jxbrowser 7.26 REPORT-86153 误传 REPORT-86153【信创】达梦数据库适配存储过程 无jira任务 代码质量注释 REPORT-93559 api优化 REPORT-93559 JxUIPane升级支持Jxbrowser7替换ModernUIPane REPORT-80651 模板版本管理重构一期补交 REPORT-89867 【降本增效】插件管理优化 优化代码 ...release/11.0
@ -0,0 +1,24 @@
|
||||
package com.fr.design.env.processor; |
||||
|
||||
import com.fr.stable.fun.mark.API; |
||||
|
||||
|
||||
/** |
||||
* 远程设计自定义用户名接口实现抽象类 |
||||
* |
||||
* @author John.Ying |
||||
* @since 11.0 |
||||
* Created on 2023/5/17 |
||||
*/ |
||||
@API(level = RemoteDesignerWorkspaceInfoProcessor.CURRENT_LEVEL) |
||||
public abstract class AbstractRemoteDesignerWorkspaceInfoProcessor implements RemoteDesignerWorkspaceInfoProcessor { |
||||
@Override |
||||
public int currentAPILevel() { |
||||
return CURRENT_LEVEL; |
||||
} |
||||
|
||||
@Override |
||||
public int layerIndex() { |
||||
return DEFAULT_LAYER_INDEX; |
||||
} |
||||
} |
@ -0,0 +1,25 @@
|
||||
package com.fr.design.env.processor; |
||||
|
||||
import com.fr.stable.fun.mark.Immutable; |
||||
import com.fr.workspace.connect.WorkspaceConnectionInfo; |
||||
|
||||
/** |
||||
* 远程设计自定义用户名接口 |
||||
* px:为了二开插件开的接口,不建议实现,后面可能会变动 |
||||
* |
||||
* @author John.Ying |
||||
* @since 11.0 |
||||
* Created on 2023/5/17 |
||||
*/ |
||||
public interface RemoteDesignerWorkspaceInfoProcessor extends Immutable { |
||||
|
||||
String XML_TAG = "RemoteDesignerWorkspaceInfoProcessor"; |
||||
|
||||
int CURRENT_LEVEL = 1; |
||||
|
||||
/** |
||||
* 根据链接信息自定义用户名 |
||||
*/ |
||||
WorkspaceConnectionInfo customUserName(WorkspaceConnectionInfo workspaceInfo); |
||||
|
||||
} |
@ -0,0 +1,184 @@
|
||||
package com.fr.design.extra; |
||||
|
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.i18n.Toolkit; |
||||
|
||||
|
||||
import javax.swing.BorderFactory; |
||||
import javax.swing.BoxLayout; |
||||
import javax.swing.JDialog; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JScrollPane; |
||||
import javax.swing.JTextArea; |
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.UIManager; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.Color; |
||||
import java.awt.Cursor; |
||||
import java.awt.Dialog; |
||||
import java.awt.Dimension; |
||||
import java.awt.FlowLayout; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
|
||||
/** |
||||
* 插件批量处理弹窗面板 |
||||
* |
||||
* @author Destiny.Lin |
||||
* @since 11.0 |
||||
* Created on 2023/5/19 |
||||
*/ |
||||
public class PluginBatchModifyDetailPane { |
||||
private UILabel message = new UILabel(); |
||||
private UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Report_OK")); |
||||
private UILabel uiLabel = new UILabel(); |
||||
private UILabel directUiLabel = new UILabel(); |
||||
private UILabel detailLabel = new UILabel(); |
||||
|
||||
private JPanel upPane; |
||||
private JPanel midPane; |
||||
private JPanel downPane; |
||||
private JPanel hiddenPanel; |
||||
private JTextArea jta; |
||||
private JDialog dialog; |
||||
|
||||
/** |
||||
* 弹窗面板默认大小 |
||||
*/ |
||||
public static final Dimension DEFAULT = new Dimension(380, 150); |
||||
|
||||
/** |
||||
* 弹窗面板展开大小 |
||||
*/ |
||||
public static final Dimension DEFAULT_PRO = new Dimension(380, 270); |
||||
|
||||
public PluginBatchModifyDetailPane(Dialog parent) { |
||||
init(parent); |
||||
} |
||||
|
||||
private void init(Dialog parent) { |
||||
message.setBorder(BorderFactory.createEmptyBorder(8, 5, 0, 0)); |
||||
dialog = new JDialog(parent, Toolkit.i18nText("Fine-Design_Basic_Plugin_Manager"), true); |
||||
dialog.setSize(DEFAULT); |
||||
JPanel jp = new JPanel(); |
||||
initUpPane(); |
||||
initDownPane(); |
||||
initMidPane(); |
||||
initHiddenPanel(); |
||||
initListener(); |
||||
jp.setLayout(new BoxLayout(jp, BoxLayout.Y_AXIS)); |
||||
jp.add(upPane); |
||||
jp.add(midPane); |
||||
jp.add(hiddenPanel); |
||||
jp.add(downPane); |
||||
hiddenPanel.setVisible(false); |
||||
dialog.add(jp); |
||||
dialog.setResizable(false); |
||||
dialog.setLocationRelativeTo(SwingUtilities.getWindowAncestor(parent)); |
||||
} |
||||
|
||||
private void initDownPane() { |
||||
downPane = new JPanel(); |
||||
downPane.setLayout(new FlowLayout(FlowLayout.RIGHT, 15, 9)); |
||||
downPane.add(cancelButton); |
||||
} |
||||
|
||||
private void initUpPane() { |
||||
upPane = new JPanel(); |
||||
uiLabel = new UILabel(UIManager.getIcon("OptionPane.errorIcon")); |
||||
upPane.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10)); |
||||
upPane.add(uiLabel); |
||||
upPane.add(message); |
||||
} |
||||
|
||||
private void initMidPane() { |
||||
midPane = new JPanel(); |
||||
midPane.add(directUiLabel); |
||||
midPane.add(detailLabel); |
||||
midPane.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); |
||||
detailLabel.setText(Toolkit.i18nText("Fine_Designer_Look_Detail")); |
||||
detailLabel.setForeground(Color.BLUE); |
||||
detailLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
||||
directUiLabel.setIcon(UIManager.getIcon("OptionPane.narrow.right")); |
||||
} |
||||
|
||||
private void initHiddenPanel() { |
||||
hiddenPanel = new JPanel(); |
||||
hiddenPanel.setLayout(new BorderLayout(2, 0)); |
||||
hiddenPanel.add(new JPanel(), BorderLayout.WEST); |
||||
hiddenPanel.add(new JPanel(), BorderLayout.EAST); |
||||
JPanel borderPanel = new JPanel(); |
||||
borderPanel.setLayout(new BorderLayout()); |
||||
jta = new JTextArea(); |
||||
JScrollPane jsp = new JScrollPane(jta); |
||||
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); |
||||
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); |
||||
jta.setEditable(false); |
||||
borderPanel.add(jsp, BorderLayout.CENTER); |
||||
hiddenPanel.add(borderPanel); |
||||
} |
||||
|
||||
/** |
||||
* 补充更详细的报错信息 |
||||
* |
||||
* @param message 信息 |
||||
*/ |
||||
public void updateDetailArea(String message) { |
||||
jta.append(message + "\n"); |
||||
} |
||||
|
||||
private void initListener() { |
||||
detailLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
if (hiddenPanel.isVisible()) { |
||||
hiddenPanel.setVisible(false); |
||||
dialog.setSize(DEFAULT); |
||||
detailLabel.setText(Toolkit.i18nText("Fine_Designer_Look_Detail")); |
||||
directUiLabel.setIcon(UIManager.getIcon("OptionPane.narrow.right")); |
||||
} else { |
||||
dialog.setSize(DEFAULT_PRO); |
||||
hiddenPanel.setVisible(true); |
||||
detailLabel.setText(Toolkit.i18nText("Fine_Designer_Hide_Detail")); |
||||
directUiLabel.setIcon(UIManager.getIcon("OptionPane.narrow.down")); |
||||
} |
||||
} |
||||
|
||||
}); |
||||
cancelButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
hiddenPanel.removeAll(); |
||||
dialog.dispose(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 更新标题 |
||||
* @param title 标题 |
||||
*/ |
||||
public void updateTitle(String title) { |
||||
dialog.setTitle(title); |
||||
} |
||||
|
||||
/** |
||||
* 更新展示信息 |
||||
* |
||||
* @param failedCount 处理失败个数 |
||||
* @param successCount 处理成功个数 |
||||
*/ |
||||
public void updateMessage(int failedCount, int successCount) { |
||||
message.setText(Toolkit.i18nText("Fine-Design_Basic_Plugin_Batch_Modify_Info", failedCount, successCount)); |
||||
} |
||||
|
||||
/** |
||||
* 显示面板 |
||||
*/ |
||||
public void show() { |
||||
dialog.setVisible(true); |
||||
} |
||||
} |
@ -0,0 +1,127 @@
|
||||
package com.fr.design.extra.exe.callback; |
||||
|
||||
import com.fr.design.bridge.exec.JSCallback; |
||||
import com.fr.design.dialog.FineJOptionPane; |
||||
import com.fr.design.extra.PluginBatchModifyDetailPane; |
||||
import com.fr.design.extra.PluginOperateUtils; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.plugin.DesignerPluginContext; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.plugin.context.PluginMarker; |
||||
import com.fr.plugin.manage.control.PluginTask; |
||||
import com.fr.plugin.manage.control.PluginTaskResult; |
||||
import com.fr.plugin.manage.control.ProgressCallback; |
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import javax.swing.JOptionPane; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 带进度条的批量处理的callback |
||||
* <li> content与title是处理完成后弹出的面板的内容与标题,子类需要在done之前设定好对应的信息 |
||||
* <li> 进度条是以 当前完成任务数/总任务数 来计算的 |
||||
* |
||||
* @author Destiny.Lin |
||||
* @since 11.0 |
||||
* Created on 2023/6/6 |
||||
*/ |
||||
public abstract class AbstractBatchModifyStatusCallback implements ProgressCallback { |
||||
protected JSCallback jsCallback; |
||||
protected Map<String, String> resultMap = new HashMap<>(); |
||||
protected String content = StringUtils.EMPTY; |
||||
protected String title = StringUtils.EMPTY; |
||||
public int pluginCount = 0; |
||||
public int allPluginCount = 0; |
||||
public int successCount = 0; |
||||
public int failedCount = 0; |
||||
public static final int HUNDRED_PERCENT = 100; |
||||
public static final String PERCENT = "%"; |
||||
public static final String DEFAULT = "default"; |
||||
|
||||
public AbstractBatchModifyStatusCallback() { |
||||
} |
||||
|
||||
public AbstractBatchModifyStatusCallback(JSCallback jsCallback, int size) { |
||||
this.jsCallback = jsCallback; |
||||
this.allPluginCount = size; |
||||
} |
||||
|
||||
@Override |
||||
public void done(PluginTaskResult result) { |
||||
String pluginInfo = PluginOperateUtils.getSuccessInfo(result); |
||||
if (result.isSuccess()) { |
||||
successCount++; |
||||
String modifyMessage = updateMessage(pluginInfo); |
||||
FineLoggerFactory.getLogger().info(modifyMessage); |
||||
} else { |
||||
failedCount++; |
||||
resultMap.put(getPluginName(result), pluginInfo); |
||||
} |
||||
updateProgressAndCheckCompletion(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 获取插件名 |
||||
* |
||||
* @param result 任务结果 |
||||
* @return 插件名 |
||||
*/ |
||||
public String getPluginName(PluginTaskResult result) { |
||||
PluginTask pluginTask = result.getCurrentTask(); |
||||
if (pluginTask != null) { |
||||
PluginMarker pluginMarker = pluginTask.getToMarker(); |
||||
if (pluginMarker != null) { |
||||
return pluginMarker.getPluginID(); |
||||
} |
||||
} |
||||
return DEFAULT; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 更新当前Map状态,进度条,如果全部都更新完了就回调 |
||||
*/ |
||||
public void updateProgressAndCheckCompletion() { |
||||
pluginCount++; |
||||
updateProgress(StringUtils.EMPTY, (double) pluginCount / allPluginCount); |
||||
if (pluginCount == allPluginCount) { |
||||
jsCallback.execute("success"); |
||||
showMessageDialog(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 展示信息面板 |
||||
*/ |
||||
public void showMessageDialog() { |
||||
if (failedCount == 0) { |
||||
FineJOptionPane.showMessageDialog(DesignerPluginContext.getPluginDialog(), |
||||
content, |
||||
title, |
||||
JOptionPane.INFORMATION_MESSAGE); |
||||
} else { |
||||
PluginBatchModifyDetailPane detailPane = new PluginBatchModifyDetailPane(DesignerPluginContext.getPluginDialog()); |
||||
for (String key : resultMap.keySet()) { |
||||
detailPane.updateDetailArea(resultMap.get(key)); |
||||
} |
||||
detailPane.updateMessage(failedCount, successCount); |
||||
detailPane.updateTitle(title); |
||||
detailPane.show(); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void updateProgress(String description, double progress) { |
||||
jsCallback.execute(progress * HUNDRED_PERCENT + PERCENT); |
||||
} |
||||
|
||||
/** |
||||
* 更新处理成功返回的日志信息 |
||||
* |
||||
* @return 返回的日志信息 |
||||
*/ |
||||
abstract public String updateMessage(String pluginInfo); |
||||
} |
@ -0,0 +1,43 @@
|
||||
package com.fr.design.extra.exe.callback; |
||||
|
||||
|
||||
import com.fr.design.bridge.exec.JSCallback; |
||||
|
||||
import com.fr.design.i18n.Toolkit; |
||||
|
||||
|
||||
/** |
||||
* 批量启用/禁用插件的Callback |
||||
* |
||||
* @author Destiny.Lin |
||||
* @since 11.0 |
||||
* Created on 2023/5/18 |
||||
*/ |
||||
public class BatchModifyStatusCallback extends AbstractBatchModifyStatusCallback { |
||||
private boolean active; |
||||
private boolean operatorFlag = false; |
||||
|
||||
public BatchModifyStatusCallback(JSCallback jsCallback, int size) { |
||||
super(jsCallback, size); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String updateMessage(String pluginInfo) { |
||||
return active ? pluginInfo + Toolkit.i18nText("Fine-Design_Basic_Plugin_Has_Been_Disabled_Duplicate") : pluginInfo + Toolkit.i18nText("Fine-Design_Plugin_Has_Been_Actived_Duplicate"); |
||||
} |
||||
|
||||
/** |
||||
* 更新启用/禁用信息 |
||||
* |
||||
* @param active |
||||
*/ |
||||
public void updateActiveStatus(boolean active) { |
||||
if (!operatorFlag) { |
||||
this.active = active; |
||||
this.operatorFlag = true; |
||||
this.title = active ? Toolkit.i18nText("Fine-Design_Basic_Plugin_Stop") : Toolkit.i18nText("Fine-Design_Basic_Plugin_Start"); |
||||
this.content = active ? Toolkit.i18nText("Fine-Design_Basic_Plugin_Batch_Modify_Stop_Success") : Toolkit.i18nText("Fine-Design_Basic_Plugin_Batch_Modify_Start_Success"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,77 @@
|
||||
package com.fr.design.extra.exe.callback; |
||||
|
||||
|
||||
import com.fr.design.bridge.exec.JSCallback; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.plugin.manage.control.PluginTask; |
||||
import com.fr.plugin.manage.control.PluginTaskResult; |
||||
|
||||
|
||||
/** |
||||
* 批量更新的callback |
||||
* |
||||
* @author Destiny.Lin |
||||
* @since 11.0 |
||||
* Created on 2023/6/6 |
||||
*/ |
||||
public class BatchUpdateOnlineCallback extends AbstractBatchModifyStatusCallback{ |
||||
public static final BatchUpdateOnlineCallback NONE = new BatchUpdateOnlineCallback(); |
||||
|
||||
/** |
||||
* 可自动处理前置任务的callback,用来处理实际更新逻辑 |
||||
*/ |
||||
private InnerUpdateCallback innerPreTaskCallback; |
||||
|
||||
public BatchUpdateOnlineCallback() { |
||||
} |
||||
|
||||
public BatchUpdateOnlineCallback(JSCallback jsCallback, int size) { |
||||
super(jsCallback, size); |
||||
this.title = Toolkit.i18nText("Fine-Design_Basic_Plugin_Update"); |
||||
this.content = Toolkit.i18nText("Fine-Design_Basic_Plugin_Update_Success"); |
||||
} |
||||
|
||||
/** |
||||
* 更新任务 |
||||
* |
||||
* @param pluginTask 任务 |
||||
* @param jsCallback callback |
||||
*/ |
||||
public void createInnerPreTaskCallback(PluginTask pluginTask, JSCallback jsCallback) { |
||||
innerPreTaskCallback = new InnerUpdateCallback(pluginTask, jsCallback); |
||||
} |
||||
|
||||
|
||||
|
||||
public InnerUpdateCallback getInnerPreTaskCallback() { |
||||
return innerPreTaskCallback; |
||||
} |
||||
|
||||
@Override |
||||
public String updateMessage(String pluginInfo) { |
||||
return pluginInfo + Toolkit.i18nText("Fine-Design_Basic_Plugin_Update_Success"); |
||||
} |
||||
|
||||
/** |
||||
* 可自动处理前置任务的callback,用来处理实际更新逻辑 |
||||
*/ |
||||
public class InnerUpdateCallback extends UpdateOnlineCallback { |
||||
|
||||
|
||||
public InnerUpdateCallback(PluginTask pluginTask, JSCallback jsCallback) { |
||||
super(pluginTask, jsCallback); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void updateProgress(String description, double aProgress) { |
||||
//不进行处理
|
||||
} |
||||
|
||||
@Override |
||||
public void allDone(PluginTaskResult result) { |
||||
BatchUpdateOnlineCallback.this.done(result); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,59 @@
|
||||
package com.fr.design.file; |
||||
|
||||
import com.fr.design.mainframe.JTemplate; |
||||
import com.fr.general.ComparatorUtils; |
||||
|
||||
import java.util.List; |
||||
import java.util.function.Predicate; |
||||
|
||||
public class MultiTemplateTabUtils { |
||||
/** |
||||
* 计算离currentIndex最近的相同模式的模板index值(优先左边) |
||||
* |
||||
* @param currentIndex 当前index |
||||
* @param openedTemplate 模板list |
||||
* @param type 当前显示模式 |
||||
* @return |
||||
*/ |
||||
public static int calShowTemplateIndex(int currentIndex, List<JTemplate<?, ?>> openedTemplate, String type) { |
||||
if (currentIndex < 0 || currentIndex > openedTemplate.size() - 1) { |
||||
return -1; |
||||
} |
||||
int result = getShowJTemplateTab(currentIndex, openedTemplate, template -> showJTemplateTab(type, template)); |
||||
if (result != -1) return result; |
||||
return getShowJTemplateTab(currentIndex, openedTemplate, template -> !showJTemplateTab(type, template)); |
||||
} |
||||
|
||||
/** |
||||
* 先从左找,再从右找离得最近的满足条件的模板 |
||||
* |
||||
* @param currentIndex 当前index |
||||
* @param openedTemplate 模板list |
||||
* @param predicate |
||||
* @return |
||||
*/ |
||||
private static int getShowJTemplateTab(int currentIndex, List<JTemplate<?, ?>> openedTemplate, Predicate<JTemplate<?, ?>> predicate) { |
||||
for (int i = currentIndex; i >= 0; i--) { |
||||
if (predicate.test(openedTemplate.get(i))) { |
||||
return i; |
||||
} |
||||
} |
||||
for (int i = currentIndex + 1; i < openedTemplate.size(); i++) { |
||||
if (predicate.test(openedTemplate.get(i))) { |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
/** |
||||
* 是否显示模板 |
||||
* |
||||
* @param type 模板类型 |
||||
* @param jTemplate 模板 |
||||
* @return |
||||
*/ |
||||
private static boolean showJTemplateTab(String type, JTemplate<?, ?> jTemplate) { |
||||
return ComparatorUtils.equals(type, jTemplate.getTemplateTabOperatorType()); |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.fr.design.bridge.exec.JSExecutor; |
||||
import com.teamdev.jxbrowser.js.JsFunction; |
||||
import com.teamdev.jxbrowser.js.JsObject; |
||||
|
||||
/** |
||||
* 用于 jxbrowser 执行后的回调执行器 |
||||
* 适配7.15之后 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/6/8 |
||||
*/ |
||||
public class BrowserExecutor implements JSExecutor { |
||||
|
||||
/** |
||||
* 创建一个回调执行器 |
||||
* |
||||
* @param window js环境的window对象 |
||||
* @param callback 回调 |
||||
* @return 执行器 |
||||
*/ |
||||
public static BrowserExecutor create(JsObject window, JsFunction callback) { |
||||
return new BrowserExecutor(window, callback); |
||||
} |
||||
|
||||
private final JsObject window; |
||||
private final JsFunction callback; |
||||
|
||||
private BrowserExecutor(JsObject window, JsFunction callback) { |
||||
this.window = window; |
||||
this.callback = callback; |
||||
} |
||||
|
||||
@Override |
||||
public void executor(String newValue) { |
||||
callback.invoke(window, newValue); |
||||
} |
||||
} |
@ -0,0 +1,140 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.ui.ModernUIConstants; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.value.ClearableLazyValue; |
||||
import com.fr.web.struct.AssembleComponent; |
||||
import com.teamdev.jxbrowser.engine.Engine; |
||||
import com.teamdev.jxbrowser.engine.EngineOptions; |
||||
import com.teamdev.jxbrowser.engine.RenderingMode; |
||||
import com.teamdev.jxbrowser.engine.event.EngineCrashed; |
||||
import com.teamdev.jxbrowser.net.Network; |
||||
import com.teamdev.jxbrowser.net.Scheme; |
||||
import com.teamdev.jxbrowser.net.callback.VerifyCertificateCallback; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 可重复启动的 Jxbrowser 引擎 |
||||
* 手动创建的引擎,应当自己负责管理声明周期 {@link #newInstance()} |
||||
* 单例的引擎,由系统管理生命周期,使用后仅需清理browser和map等数据 |
||||
* {@link #getEngine()} 和 {@link #getPublicEngineInstance()} |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/6/8 |
||||
*/ |
||||
public class JxEngine { |
||||
|
||||
private static final JxEngine INSTANCE = new JxEngine(); |
||||
|
||||
private AssembleComponent component; |
||||
private Map<String, String> parameterMap = Collections.emptyMap(); |
||||
|
||||
|
||||
private final ClearableLazyValue<Engine> ENGINE = ClearableLazyValue.create(() -> { |
||||
EngineOptions.Builder builder = EngineOptions |
||||
.newBuilder(RenderingMode.HARDWARE_ACCELERATED) |
||||
.addSwitch("--disable-google-traffic") |
||||
.addScheme(Scheme.of(ModernUIConstants.EMB_TAG), |
||||
new NxInterceptRequestCallback(this::getComponent, this::getParameterMap)); |
||||
Engine engine = Engine.newInstance(builder.build()); |
||||
engine.on(EngineCrashed.class, (event) -> { |
||||
FineLoggerFactory.getLogger().error("jxBrowser engine crashed with exitCode: {}", event.exitCode()); |
||||
event.engine().close(); |
||||
}); |
||||
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { |
||||
// 调试模式下,禁止HTTPS证书验证,使得可以正常访问商城测试服务器等
|
||||
Network network = engine.network(); |
||||
network.set(VerifyCertificateCallback.class, params -> VerifyCertificateCallback.Response.valid()); |
||||
} |
||||
return engine; |
||||
}); |
||||
|
||||
public Map<String, String> getParameterMap() { |
||||
return Collections.unmodifiableMap(parameterMap); |
||||
} |
||||
|
||||
public void setMap(Map<String, String> parameterMap) { |
||||
if (parameterMap == null) { |
||||
return; |
||||
} |
||||
this.parameterMap = parameterMap; |
||||
} |
||||
|
||||
/** |
||||
* 清理map |
||||
*/ |
||||
public void clearMap() { |
||||
this.parameterMap = Collections.emptyMap(); |
||||
} |
||||
|
||||
public AssembleComponent getComponent() { |
||||
return component; |
||||
} |
||||
|
||||
public void setComponent(AssembleComponent component) { |
||||
this.component = component; |
||||
} |
||||
|
||||
/** |
||||
* 清理component |
||||
*/ |
||||
public void clearComponent() { |
||||
this.component = null; |
||||
} |
||||
|
||||
/** |
||||
* 获取单例引擎包装,能够更新渲染 |
||||
* 从单例获取的引擎不用负责关闭, |
||||
* 应用系统管理声明周期 |
||||
* |
||||
* @return jxbrowser 引擎包装类 |
||||
*/ |
||||
public static JxEngine getInstance() { |
||||
return INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* 获取公共引擎,公共引擎使用后不用关闭引擎,但需要自己管理 browser。 |
||||
* 应用系统管理引擎生命周期 |
||||
* |
||||
* @return 引擎 |
||||
*/ |
||||
@NotNull |
||||
public Engine getEngine() { |
||||
return ENGINE.getValue(); |
||||
} |
||||
|
||||
/** |
||||
* 关闭引擎 |
||||
*/ |
||||
public void close() { |
||||
ENGINE.getValue().close(); |
||||
ENGINE.drop(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 获取公共引擎,公共引擎使用后不用关闭引擎,但需要自己管理 browser。 |
||||
* 应用系统管理引擎生命周期 |
||||
* |
||||
* @return 引擎 |
||||
*/ |
||||
public static Engine getPublicEngineInstance() { |
||||
return getInstance().ENGINE.getValue(); |
||||
} |
||||
|
||||
/** |
||||
* 创建一个新的引擎,创建引擎使用后需负责关闭引擎 |
||||
* 但可以独立使用 map 和 component |
||||
* |
||||
* @return 引擎 |
||||
*/ |
||||
public static JxEngine newInstance() { |
||||
return new JxEngine(); |
||||
} |
||||
} |
@ -0,0 +1,536 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.itoolbar.UIToolbar; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.ui.ModernUIConstants; |
||||
import com.fr.design.ui.ModernUIPane; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.collections.combination.Pair; |
||||
import com.fr.web.struct.AssembleComponent; |
||||
import com.teamdev.jxbrowser.browser.Browser; |
||||
import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; |
||||
import com.teamdev.jxbrowser.chromium.events.LoadListener; |
||||
import com.teamdev.jxbrowser.chromium.events.ScriptContextListener; |
||||
import com.teamdev.jxbrowser.event.Observer; |
||||
import com.teamdev.jxbrowser.frame.Frame; |
||||
import com.teamdev.jxbrowser.js.JsObject; |
||||
import com.teamdev.jxbrowser.view.swing.BrowserView; |
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
import javax.swing.SwingUtilities; |
||||
import java.awt.BorderLayout; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
import java.util.Optional; |
||||
|
||||
import static com.fr.design.ui.ModernUIConstants.COMPONENT_TAG; |
||||
import static com.fr.design.ui.ModernUIConstants.DEFAULT_EXPRESSION; |
||||
import static com.fr.design.ui.ModernUIConstants.DEFAULT_NAMESPACE; |
||||
import static com.fr.design.ui.ModernUIConstants.DEFAULT_VARIABLE; |
||||
import static com.fr.design.ui.ModernUIConstants.DOT; |
||||
import static com.fr.design.ui.ModernUIConstants.EMB_TAG; |
||||
import static com.fr.design.ui.ModernUIConstants.SCHEME_HEADER; |
||||
import static com.fr.design.ui.ModernUIConstants.WINDOW; |
||||
|
||||
/** |
||||
* 基于v7 jxbrowser 实现 |
||||
* 用于加载 html5 的Swing容器,可以在设计选项设置中打开调试窗口, |
||||
* 示例可查看:com.fr.design.ui.JxUIPaneTest |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023-06-12 |
||||
*/ |
||||
public class JxUIPane<T> extends ModernUIPane<T> { |
||||
|
||||
private Browser browser; |
||||
private String namespace = "Pool"; |
||||
private String variable = "data"; |
||||
private String expression = "update()"; |
||||
|
||||
private JxUIPane() { |
||||
super(); |
||||
} |
||||
|
||||
private void initialize() { |
||||
setLayout(new BorderLayout()); |
||||
if (browser != null) { |
||||
return; |
||||
} |
||||
initDebugIfNeeded(); |
||||
// 使用公共引擎创建浏览器
|
||||
browser = JxEngine.getPublicEngineInstance().newBrowser(); |
||||
add(BrowserView.newInstance(browser), BorderLayout.CENTER); |
||||
} |
||||
|
||||
/** |
||||
* 按需初始化debug界面UI |
||||
*/ |
||||
private void initDebugIfNeeded() { |
||||
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { |
||||
UIToolbar toolbar = new UIToolbar(); |
||||
add(toolbar, BorderLayout.NORTH); |
||||
UIButton openDebugButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Open_Debug_Window")); |
||||
openDebugButton.addActionListener(e -> browser.devTools().show()); |
||||
toolbar.add(openDebugButton); |
||||
UIButton reloadButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Reload")); |
||||
reloadButton.addActionListener(e -> browser.navigation().reloadIgnoringCache()); |
||||
toolbar.add(reloadButton); |
||||
UIButton closeButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Close_Window")); |
||||
closeButton.addActionListener(e -> SwingUtilities.getWindowAncestor(JxUIPane.this).setVisible(false)); |
||||
toolbar.add(closeButton); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 在初始化时进行注入JS的方法,只被build调用 |
||||
* |
||||
* @param injectJsCallback 回调 |
||||
*/ |
||||
private void initInjectJs(InjectJsCallback injectJsCallback) { |
||||
browser.set(InjectJsCallback.class, params -> { |
||||
// 初始化的时候,就把命名空间对象初始化好,确保window.a.b.c("a.b.c"为命名空间)对象都是初始化过的
|
||||
params.frame().executeJavaScript(String.format(ModernUIConstants.SCRIPT_INIT_NAME_SPACE, namespace)); |
||||
return injectJsCallback.on(params); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 设置 InjectJsCallback。 |
||||
* 这个方法解决重复InjectJsCallback被覆盖的问题。 |
||||
* 用于本类内部方法使用,如{@link #populate(Object)} |
||||
* |
||||
* @param injectJsCallback 回调 |
||||
*/ |
||||
private void setInjectJsCallback(InjectJsCallback injectJsCallback) { |
||||
Optional<InjectJsCallback> callback = browser.get(InjectJsCallback.class); |
||||
if (callback.isPresent()) { |
||||
browser.set(InjectJsCallback.class, params -> { |
||||
callback.get().on(params); |
||||
return injectJsCallback.on(params); |
||||
}); |
||||
} else { |
||||
browser.set(InjectJsCallback.class, injectJsCallback); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 转向一个新的地址,相当于重新加载 |
||||
* |
||||
* @param url 新的地址 |
||||
*/ |
||||
@Override |
||||
public void redirect(String url) { |
||||
browser.navigation().loadUrl(url); |
||||
} |
||||
|
||||
/** |
||||
* 转向一个新的地址,相当于重新加载 |
||||
* |
||||
* @param url 新的地址 |
||||
* @param map 初始化参数 |
||||
*/ |
||||
@Override |
||||
public void redirect(String url, Map<String, String> map) { |
||||
setMap(map); |
||||
browser.navigation().loadUrl(url); |
||||
} |
||||
|
||||
private void setMap(Map<String, String> map) { |
||||
JxEngine.getInstance().setMap(map); |
||||
} |
||||
|
||||
private void setComponent(AssembleComponent component) { |
||||
JxEngine.getInstance().setComponent(component); |
||||
} |
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return "ModernUI7"; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void populate(final T t) { |
||||
setInjectJsCallback(params -> { |
||||
executeJsObject(params.frame(), WINDOW + DOT + namespace) |
||||
.ifPresent(ns -> ns.putProperty(variable, t)); |
||||
return InjectJsCallback.Response.proceed(); |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
@Nullable |
||||
public T update() { |
||||
if (browser.mainFrame().isPresent()) { |
||||
return browser.mainFrame().get().executeJavaScript("window." + namespace + "." + expression); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* 关闭浏览器 |
||||
*/ |
||||
public void disposeBrowser() { |
||||
if (browser != null) { |
||||
browser.close(); |
||||
browser = null; |
||||
JxEngine.getInstance().clearMap(); |
||||
JxEngine.getInstance().clearComponent(); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 清理浏览器缓存 |
||||
*/ |
||||
public void clearCache() { |
||||
if (browser != null) { |
||||
browser.engine().httpCache().clear(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 执行一段js |
||||
* |
||||
* @param javaScript 待执行的js脚本 |
||||
*/ |
||||
public void executeJavaScript(String javaScript) { |
||||
if (browser != null) { |
||||
browser.mainFrame().ifPresent(frame -> { |
||||
frame.executeJavaScript(javaScript); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取js对象 |
||||
* 注意:类内部使用,用于简化编码,提供 Optional 包装 |
||||
* |
||||
* @param frame 页面frame对象 |
||||
* @param name 变量命名 |
||||
* @return js对象 |
||||
*/ |
||||
private static Optional<JsObject> executeJsObject(Frame frame, String name) { |
||||
return Optional.ofNullable(frame.executeJavaScript(name)); |
||||
} |
||||
|
||||
/** |
||||
* JxUIPane 的建造者 |
||||
* |
||||
* @param <T> 参数 |
||||
*/ |
||||
public static class Builder<T> extends ModernUIPane.Builder<T> { |
||||
private String namespace; |
||||
private String variable; |
||||
private String expression; |
||||
private InjectJsCallback callback; |
||||
|
||||
private Pair<Class, Observer> listenerPair; |
||||
private final Map<String, Object> namespacePropertyMap; |
||||
private final Map<String, Object> propertyMap; |
||||
|
||||
private final Map<String, PropertyBuild> buildPropertyMap; |
||||
private Object variableProperty; |
||||
private Map<String, String> parameterMap; |
||||
private AssembleComponent component; |
||||
private String url; |
||||
private String html; |
||||
|
||||
public Builder() { |
||||
this.namespace = DEFAULT_NAMESPACE; |
||||
this.variable = DEFAULT_VARIABLE; |
||||
this.expression = DEFAULT_EXPRESSION; |
||||
this.callback = null; |
||||
this.listenerPair = null; |
||||
this.namespacePropertyMap = new HashMap<>(); |
||||
this.propertyMap = new HashMap<>(); |
||||
this.buildPropertyMap = new HashMap<>(); |
||||
this.variableProperty = null; |
||||
this.parameterMap = null; |
||||
this.component = null; |
||||
this.url = StringUtils.EMPTY; |
||||
this.html = StringUtils.EMPTY; |
||||
} |
||||
|
||||
/** |
||||
* 注入一个回调,回调的js会在初始化进行执行 |
||||
* |
||||
* @param callback 回调 |
||||
* @return builder |
||||
*/ |
||||
public Builder<T> prepare(InjectJsCallback callback) { |
||||
this.callback = callback; |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public Builder<T> prepareForV6(ScriptContextListener contextListener) { |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public Builder<T> prepareForV6(LoadListener loadListener) { |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public JxUIPane.Builder<T> prepareForV7(InjectJsCallback callback) { |
||||
prepare(callback); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public JxUIPane.Builder<T> prepareForV7(Class event, Observer listener) { |
||||
listenerPair = new Pair<>(event, listener); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载jar包中的资源 |
||||
* |
||||
* @param path 资源路径 |
||||
*/ |
||||
public JxUIPane.Builder<T> withEMB(final String path) { |
||||
this.url = EMB_TAG + SCHEME_HEADER + path; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载jar包中的资源 |
||||
* |
||||
* @param path 资源路径 |
||||
*/ |
||||
public JxUIPane.Builder<T> withEMB(final String path, Map<String, String> map) { |
||||
this.parameterMap = map; |
||||
this.url = EMB_TAG + SCHEME_HEADER + path; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载url指向的资源 |
||||
* |
||||
* @param url 文件的地址 |
||||
*/ |
||||
public JxUIPane.Builder<T> withURL(final String url) { |
||||
this.url = url; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载url指向的资源 |
||||
* |
||||
* @param url 文件的地址 |
||||
*/ |
||||
public JxUIPane.Builder<T> withURL(final String url, Map<String, String> map) { |
||||
this.parameterMap = map; |
||||
this.url = url; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载Atom组件 |
||||
* |
||||
* @param component Atom组件 |
||||
*/ |
||||
public JxUIPane.Builder<T> withComponent(AssembleComponent component) { |
||||
return withComponent(component, null); |
||||
} |
||||
|
||||
/** |
||||
* 加载Atom组件 |
||||
* |
||||
* @param component Atom组件 |
||||
*/ |
||||
public JxUIPane.Builder<T> withComponent(AssembleComponent component, Map<String, String> map) { |
||||
this.parameterMap = map; |
||||
this.component = component; |
||||
this.url = COMPONENT_TAG; |
||||
return this; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 加载html文本内容 |
||||
* |
||||
* @param html 要加载html文本内容 |
||||
*/ |
||||
public JxUIPane.Builder<T> withHTML(String html) { |
||||
this.html = html; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 设置该前端页面做数据交换所使用的对象 |
||||
* 相当于: |
||||
* const namespace = "Pool"; |
||||
* 调用: |
||||
* window[namespace]; |
||||
* 默认下结构如: |
||||
* window.Pool |
||||
* |
||||
* @param namespace 对象名 |
||||
*/ |
||||
public JxUIPane.Builder<T> namespace(String namespace) { |
||||
this.namespace = namespace; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* java端往js端传数据时使用的变量名字 |
||||
* 默认值为 data |
||||
* 相当于: |
||||
* const variable = "data"; |
||||
* 调用: |
||||
* window[namespace][variable]; |
||||
* 默认下结构如: |
||||
* window.Pool.data |
||||
* |
||||
* @param name 变量的名字 |
||||
*/ |
||||
public JxUIPane.Builder<T> variable(String name) { |
||||
this.variable = name; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* js端往java端传数据时执行的函数表达式 |
||||
* |
||||
* @param expression 函数表达式 |
||||
*/ |
||||
public JxUIPane.Builder<T> expression(String expression) { |
||||
this.expression = expression; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 注入一个java对象到js中,绑定在全局变量window的指定变量variable。 |
||||
* variable 可由 {@link #namespace(String)} 设置,默认值为 data |
||||
* 这个方法仅在在加载的网页上执行 JavaScript 之前注入 |
||||
* 相当于: |
||||
* window[namespace][property] = javaObject |
||||
* 默认下: |
||||
* window.Pool[property] = javaObject |
||||
* |
||||
* @param obj java对象 |
||||
* @return 链式对象 |
||||
*/ |
||||
public JxUIPane.Builder<T> bindNamespace(String property, @Nullable Object obj) { |
||||
this.namespacePropertyMap.put(property, obj); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 注入一个java对象到js中,绑定在全局变量window的指定变量variable。 |
||||
* variable 可由 {@link #variable(String)} 设置,默认值为 data |
||||
* 这个方法仅在在加载的网页上执行 JavaScript 之前注入 |
||||
* 相当于: |
||||
* window[namespace][variable] = javaObject |
||||
* 默认下: |
||||
* window.Pool.data = javaObject |
||||
* |
||||
* @param obj java对象 |
||||
* @return 链式对象 |
||||
*/ |
||||
public JxUIPane.Builder<T> bindVariable(@NotNull Object obj) { |
||||
this.variableProperty = obj; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 注入一个java对象到js中,绑定在全局变量 window的 |
||||
* property指定的变量。这个方法仅在在加载的网页上执 |
||||
* 行 JavaScript 之前注入 |
||||
* 相当于: |
||||
* window[property] = javaObject |
||||
* |
||||
* @param property 属性 |
||||
* @param obj java对象 |
||||
* @return 链式对象 |
||||
* @see #bindWindow(String, PropertyBuild) |
||||
*/ |
||||
public JxUIPane.Builder<T> bindWindow(String property, @Nullable Object obj) { |
||||
this.propertyMap.put(property, obj); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 注入一个java对象到js中。绑定在全局变量 window的property指定的变量。 |
||||
* PropertyBuild用于动态生成绑定属性。个方法仅在在加载的网页上执行 |
||||
* JavaScript 之前注入 |
||||
* 相当于: |
||||
* window[property] = javaObject |
||||
* |
||||
* @param property 属性构建器 |
||||
* @param obj java对象 |
||||
* @return 链式对象 |
||||
* @see #bindWindow(String, Object) |
||||
*/ |
||||
public JxUIPane.Builder<T> bindWindow(String property, PropertyBuild obj) { |
||||
buildPropertyMap.put(property, obj); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 构建 |
||||
*/ |
||||
public JxUIPane<T> build() { |
||||
JxUIPane<T> pane = new JxUIPane<>(); |
||||
pane.namespace = namespace; |
||||
pane.variable = variable; |
||||
pane.expression = expression; |
||||
pane.setMap(parameterMap); |
||||
pane.setComponent(component); |
||||
pane.initialize(); |
||||
injectJs(pane); |
||||
if (!Objects.isNull(listenerPair)) { |
||||
pane.browser.navigation().on(listenerPair.getFirst(), listenerPair.getSecond()); |
||||
} |
||||
if (StringUtils.isNotEmpty(this.url)) { |
||||
pane.browser.navigation().loadUrl(this.url); |
||||
} else if (StringUtils.isNotEmpty(this.html)) { |
||||
pane.browser.mainFrame().ifPresent(f -> f.loadHtml(html)); |
||||
} |
||||
return pane; |
||||
} |
||||
|
||||
/** |
||||
* 由于 InjectJsCallback 的回调机制,在初始化期间,只有 |
||||
* 在 InjectJsCallback 中 putProperty 才能生效。 |
||||
* 因此,嵌套回调分别做默认初始化、putProperty、外置初始化 |
||||
*/ |
||||
private void injectJs(JxUIPane<T> pane) { |
||||
pane.initInjectJs(params -> { |
||||
Frame frame = params.frame(); |
||||
if (!propertyMap.isEmpty()) { |
||||
propertyMap.forEach((key, value) -> |
||||
executeJsObject(frame, WINDOW) |
||||
.ifPresent(window -> window.putProperty(key, value))); |
||||
} |
||||
if (!buildPropertyMap.isEmpty()) { |
||||
buildPropertyMap.forEach((key, value) -> |
||||
executeJsObject(frame, WINDOW) |
||||
.ifPresent(window -> window.putProperty(key, value.build(window)))); |
||||
} |
||||
if (!namespacePropertyMap.isEmpty()) { |
||||
namespacePropertyMap.forEach((key, value) -> |
||||
executeJsObject(frame, WINDOW + DOT + namespace) |
||||
.ifPresent(pool -> pool.putProperty(key, value))); |
||||
} |
||||
if (variableProperty != null) { |
||||
executeJsObject(frame, WINDOW + DOT + namespace) |
||||
.ifPresent(pool -> pool.putProperty(variable, variableProperty)); |
||||
} |
||||
if (callback != null) { |
||||
return callback.on(params); |
||||
} |
||||
return InjectJsCallback.Response.proceed(); |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,113 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.fr.stable.StringUtils; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
import java.util.Arrays; |
||||
import java.util.Optional; |
||||
|
||||
/** |
||||
* jxbrowser 使用的一些媒体类型 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/6/13 |
||||
*/ |
||||
public enum MimeType { |
||||
/** |
||||
* html 格式 |
||||
*/ |
||||
HTML(".html", "text/html"), |
||||
/** |
||||
* CSS 格式 |
||||
*/ |
||||
CSS(".css", "text/css"), |
||||
/** |
||||
* js 格式 |
||||
*/ |
||||
JS(".js", "text/css"), |
||||
/** |
||||
* svg 格式 |
||||
*/ |
||||
SVG(".svg", "text/javascript"), |
||||
/** |
||||
* png 格式 |
||||
*/ |
||||
PNG(".png", "image/png"), |
||||
|
||||
/** |
||||
* jpg 格式 |
||||
*/ |
||||
JPG(".jpg", "image/jpeg"), |
||||
|
||||
/** |
||||
* jpeg 格式 |
||||
*/ |
||||
JPEG(".jpeg", "image/jpeg"), |
||||
|
||||
/** |
||||
* gif 格式 |
||||
*/ |
||||
GIF(".gif", "image/gif"), |
||||
/** |
||||
* woff 字体格式 |
||||
*/ |
||||
WOFF(".woff", "font/woff"), |
||||
/** |
||||
* ttf 字体格式 |
||||
*/ |
||||
TTF(".ttf", "truetype"), |
||||
|
||||
/** |
||||
* MS 嵌入式开放字体 |
||||
*/ |
||||
EOT(".eot", "embedded-opentype"); |
||||
|
||||
private final String suffix; |
||||
private final String mimeType; |
||||
|
||||
MimeType(String suffix, String mimeType) { |
||||
this.suffix = suffix; |
||||
this.mimeType = mimeType; |
||||
} |
||||
|
||||
public String getMimeType() { |
||||
return mimeType; |
||||
} |
||||
|
||||
/** |
||||
* 获取指定路径对应的 mimetype,优先匹配常量中的类型 |
||||
* 如果没有,尝试使用 Files.probeContentType 检测 |
||||
* 如果没有,默认返回 text/html |
||||
* |
||||
* @param url url路径 |
||||
* @return MimeType |
||||
*/ |
||||
public static String parseMimeType(String url) { |
||||
if (StringUtils.isBlank(url)) { |
||||
return HTML.mimeType; |
||||
} |
||||
String finalPath = url.split("\\?")[0]; |
||||
Optional<MimeType> mimeType = Arrays.stream(values()) |
||||
.filter(type -> finalPath.endsWith(type.suffix)) |
||||
.findFirst(); |
||||
if (mimeType.isPresent()) { |
||||
return mimeType.get().mimeType; |
||||
} else { |
||||
return getFileMimeType(finalPath); |
||||
} |
||||
} |
||||
|
||||
private static String getFileMimeType(String finalPath) { |
||||
Path file = new File(finalPath).toPath(); |
||||
try { |
||||
String s = Files.probeContentType(file); |
||||
return StringUtils.isEmpty(s) ? HTML.mimeType : s; |
||||
} catch (IOException e) { |
||||
return HTML.mimeType; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,171 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.fr.base.TemplateUtils; |
||||
import com.fr.design.ui.ModernRequestClient; |
||||
import com.fr.design.ui.ModernUIConstants; |
||||
import com.fr.general.IOUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.EncodeConstants; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.web.struct.AssembleComponent; |
||||
import com.fr.web.struct.AtomBuilder; |
||||
import com.fr.web.struct.PathGroup; |
||||
import com.fr.web.struct.category.ScriptPath; |
||||
import com.fr.web.struct.category.StylePath; |
||||
import com.teamdev.jxbrowser.net.HttpHeader; |
||||
import com.teamdev.jxbrowser.net.HttpStatus; |
||||
import com.teamdev.jxbrowser.net.UrlRequest; |
||||
import com.teamdev.jxbrowser.net.UrlRequestJob; |
||||
import com.teamdev.jxbrowser.net.callback.InterceptUrlRequestCallback; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.io.InputStream; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
import java.util.function.Supplier; |
||||
|
||||
import static com.fr.design.ui.ModernUIConstants.COMPONENT_TAG; |
||||
|
||||
/** |
||||
* jxbrowser7 自定义 scheme 处理回调 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/6/8 |
||||
*/ |
||||
public class NxInterceptRequestCallback implements InterceptUrlRequestCallback { |
||||
|
||||
private Supplier<AssembleComponent> component; |
||||
|
||||
private Supplier<Map<String, String>> renderParameterBuild; |
||||
|
||||
public NxInterceptRequestCallback(Supplier<Map<String, String>> renderParameterBuild) { |
||||
this.renderParameterBuild = renderParameterBuild; |
||||
} |
||||
|
||||
public NxInterceptRequestCallback(Supplier<AssembleComponent> component, |
||||
Supplier<Map<String, String>> renderParameterBuild) { |
||||
this.component = component; |
||||
this.renderParameterBuild = renderParameterBuild; |
||||
} |
||||
|
||||
/** |
||||
* 主要包括 atom 和 emb协议的文件链接处理, |
||||
* 去掉file文件协议的支持,因此jxbrowser 7之后不支持覆盖内置协议,详细见 |
||||
* {@link com.teamdev.jxbrowser.net.internal.NonInterceptableScheme} |
||||
* |
||||
* @param params 参数 |
||||
* @return 响应 |
||||
*/ |
||||
@Override |
||||
public Response on(Params params) { |
||||
UrlRequest urlRequest = params.urlRequest(); |
||||
String path = urlRequest.url(); |
||||
Optional<UrlRequestJob> urlRequestJobOptional; |
||||
if (path.startsWith(COMPONENT_TAG)) { |
||||
String text = htmlText(renderParameterBuild.get()); |
||||
urlRequestJobOptional = generateBasicUrlRequestJob(params, |
||||
"text/html", text.getBytes(StandardCharsets.UTF_8)); |
||||
} else { |
||||
urlRequestJobOptional = generateFileProtocolUrlRequestJob(params, path); |
||||
} |
||||
return urlRequestJobOptional |
||||
.map(Response::intercept) |
||||
.orElseGet(Response::proceed); |
||||
} |
||||
|
||||
protected Optional<UrlRequestJob> generateFileProtocolUrlRequestJob(Params params, String path) { |
||||
try { |
||||
InputStream inputStream = getResourceStream(path); |
||||
String mimeType = MimeType.parseMimeType(path); |
||||
byte[] bytes; |
||||
if (isHtml(mimeType)) { |
||||
String text = IOUtils.inputStream2String(inputStream, EncodeConstants.ENCODING_UTF_8); |
||||
text = TemplateUtils.renderParameter4Tpl(text, renderParameterBuild.get()); |
||||
bytes = text.getBytes(StandardCharsets.UTF_8); |
||||
} else { |
||||
bytes = IOUtils.inputStream2Bytes(inputStream); |
||||
} |
||||
return generateBasicUrlRequestJob(params, mimeType, bytes); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return Optional.empty(); |
||||
} |
||||
|
||||
/** |
||||
* 获取资源文件流 |
||||
* |
||||
* @param path 文件路径 |
||||
* @return 输入流 |
||||
* @throws Exception IO异常 |
||||
*/ |
||||
private InputStream getResourceStream(String path) throws Exception { |
||||
int index = path.indexOf("="); |
||||
if (index > 0) { |
||||
path = path.substring(index + 1); |
||||
} else { |
||||
// jxbrowser 7之后,协议会自动补齐双斜杠//
|
||||
path = path.split(":/")[1]; |
||||
} |
||||
return IOUtils.readResource(path); |
||||
} |
||||
|
||||
private boolean isHtml(String mimeType) { |
||||
return MimeType.HTML.getMimeType().equals(mimeType); |
||||
} |
||||
|
||||
private Optional<UrlRequestJob> generateBasicUrlRequestJob(Params params, String mimeType, byte[] bytes) { |
||||
if (StringUtils.isEmpty(mimeType)) { |
||||
return Optional.empty(); |
||||
} |
||||
UrlRequestJob.Options options = UrlRequestJob.Options |
||||
.newBuilder(HttpStatus.OK) |
||||
.addHttpHeader(HttpHeader.of("Content-Type", mimeType)) |
||||
.build(); |
||||
UrlRequestJob urlRequestJob = params.newUrlRequestJob(options); |
||||
urlRequestJob.write(bytes); |
||||
urlRequestJob.complete(); |
||||
return Optional.of(urlRequestJob); |
||||
} |
||||
|
||||
private String htmlText(Map<String, String> map) { |
||||
return component.get() == null ? |
||||
StringUtils.EMPTY : |
||||
parseComponent(component.get(), map); |
||||
} |
||||
|
||||
@NotNull |
||||
private static String parseComponent(AssembleComponent component, Map<String, String> map) { |
||||
PathGroup pathGroup = AtomBuilder.create().buildAssembleFilePath(ModernRequestClient.KEY, component); |
||||
StylePath[] stylePaths = pathGroup.toStylePathGroup(); |
||||
StringBuilder styleText = new StringBuilder(); |
||||
for (StylePath path : stylePaths) { |
||||
if (StringUtils.isNotBlank(path.toFilePath())) { |
||||
styleText.append("<link rel=\"stylesheet\" href=\"emb://"); |
||||
styleText.append(path.toFilePath()); |
||||
styleText.append("\"/>"); |
||||
} |
||||
} |
||||
String result = ModernUIConstants.HTML_TPL.replaceAll("##style##", styleText.toString()); |
||||
ScriptPath[] scriptPaths = pathGroup.toScriptPathGroup(); |
||||
StringBuilder scriptText = new StringBuilder(); |
||||
for (ScriptPath path : scriptPaths) { |
||||
if (StringUtils.isNotBlank(path.toFilePath())) { |
||||
scriptText.append("<script src=\"emb://"); |
||||
scriptText.append(path.toFilePath()); |
||||
scriptText.append("\"></script>"); |
||||
} |
||||
} |
||||
result = result.replaceAll("##script##", scriptText.toString()); |
||||
if (map != null) { |
||||
for (Map.Entry<String, String> entry : map.entrySet()) { |
||||
String key = entry.getKey(); |
||||
String value = entry.getValue(); |
||||
result = result.replaceAll("\\$\\{" + key + "}", value); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.teamdev.jxbrowser.js.JsObject; |
||||
|
||||
/** |
||||
* 属性构建器 |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/6/8 |
||||
*/ |
||||
public interface PropertyBuild { |
||||
/** |
||||
* 构建属性 |
||||
* |
||||
* @param window js环境的window对象 |
||||
* @return 待注入java对象 |
||||
*/ |
||||
Object build(JsObject window); |
||||
} |
@ -0,0 +1,23 @@
|
||||
package com.fr.design.mainframe.mobile.processor; |
||||
|
||||
import com.fr.stable.fun.mark.API; |
||||
|
||||
/** |
||||
* 移动端Form控件,样式模板,通用属性替换接口 |
||||
* |
||||
* @author Coral.Chen |
||||
* @since 11.0 |
||||
* Created on 2023/4/11 |
||||
*/ |
||||
@API(level = MobileStyleDefinePaneCreator.CURRENT_LEVEL) |
||||
public abstract class AbstractMobileStyleDefinePaneCreator implements MobileStyleDefinePaneCreator { |
||||
@Override |
||||
public int currentAPILevel() { |
||||
return CURRENT_LEVEL; |
||||
} |
||||
|
||||
@Override |
||||
public int layerIndex() { |
||||
return DEFAULT_LAYER_INDEX; |
||||
} |
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fr.design.mainframe.mobile.processor; |
||||
|
||||
import com.fr.design.beans.BasicBeanPane; |
||||
import com.fr.design.mainframe.mobile.ui.MobileStyleCustomDefinePane; |
||||
import com.fr.form.ui.Widget; |
||||
import com.fr.form.ui.mobile.MobileCommonExtraStyle; |
||||
import com.fr.form.ui.mobile.MobileStyle; |
||||
import com.fr.stable.fun.mark.Immutable; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
/** |
||||
* 移动端Form控件,样式模板,通用属性替换接口 |
||||
* |
||||
* @author Coral.Chen |
||||
* @since 11.0 |
||||
* Created on 2023/4/11 |
||||
*/ |
||||
public interface MobileStyleDefinePaneCreator extends Immutable { |
||||
String XML_TAG = "MobileStyleDefinePaneCreator"; |
||||
|
||||
int CURRENT_LEVEL = 1; |
||||
|
||||
/** |
||||
* <p> 创建通用属性样式界面,可替换{@link com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane} |
||||
* <p> 每种样式的通用属性面板是一样的 |
||||
* |
||||
* @param widget 控件 |
||||
* @param customDefinePane 自定义面板 |
||||
* @param mobileStyle 移动端样式 |
||||
* @return |
||||
*/ |
||||
@Nullable BasicBeanPane<MobileStyle> createBaseBeanPane(Widget widget, Class<? extends MobileStyleCustomDefinePane> customDefinePane, Class<? extends MobileStyle> mobileStyle); |
||||
|
||||
/** |
||||
* 替换通用属性面板,注册额外属性 |
||||
* |
||||
* @return 属性类 |
||||
*/ |
||||
@Nullable Class<? extends MobileCommonExtraStyle> classForCommonExtraStyle(Widget widget); |
||||
} |
@ -0,0 +1,406 @@
|
||||
package com.fr.design.mainframe.vcs.ui; |
||||
|
||||
|
||||
import com.fr.base.svg.IconUtils; |
||||
import com.fr.design.dialog.BasicDialog; |
||||
import com.fr.design.dialog.BasicPane; |
||||
import com.fr.design.dialog.DialogActionAdapter; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.ibutton.UIRadioButton; |
||||
import com.fr.design.gui.ilable.UILabel; |
||||
import com.fr.design.gui.iprogressbar.ModernUIProgressBarUI; |
||||
import com.fr.design.gui.ispinner.UISpinner; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.layout.FRGUIPaneFactory; |
||||
import com.fr.design.layout.VerticalFlowLayout; |
||||
import com.fr.design.mainframe.vcs.common.VcsHelper; |
||||
import com.fr.design.utils.DesignUtils; |
||||
import com.fr.design.widget.FRWidgetFactory; |
||||
import com.fr.general.FRFont; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.workspace.server.vcs.v2.move.VcsMoveService; |
||||
import com.fr.workspace.server.vcs.v2.move.VcsMoveStrategy; |
||||
|
||||
import javax.swing.ButtonGroup; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.JProgressBar; |
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.SwingWorker; |
||||
import java.awt.*; |
||||
import java.awt.event.ActionEvent; |
||||
import java.awt.event.ActionListener; |
||||
import java.awt.event.MouseAdapter; |
||||
import java.awt.event.MouseEvent; |
||||
import java.util.List; |
||||
import java.util.concurrent.ExecutionException; |
||||
|
||||
|
||||
/** |
||||
* 迁移面板 |
||||
* |
||||
* @author Destiny.Lin |
||||
* @since 11.0 |
||||
* Created on 2023/6/13 |
||||
*/ |
||||
public class VcsMovePanel extends BasicPane { |
||||
private static final FRFont FONT = DesignUtils |
||||
.getDefaultGUIFont() |
||||
.applySize(14) |
||||
.applyStyle(FRFont.BOLD); |
||||
|
||||
private static final Color BACK_GROUND_COLOR = new Color(202,232,255); |
||||
|
||||
//提示字体的颜色,直接模仿其他面板的写法
|
||||
private static final Color TIP_COLOR = new Color(51, 51, 52, (int)Math.round(0.5 * 255)); |
||||
|
||||
private static final Color LABEL_COLOR = new Color(34,149,233); |
||||
|
||||
private static final int MIN_VALUE = 1; |
||||
|
||||
private static final int MAX_VALUE = 999; |
||||
|
||||
private static final int STEP = 1; |
||||
|
||||
private static final int DEFAULT_VALUE = 5; |
||||
|
||||
public static final String SETTING = "SETTING"; |
||||
|
||||
public static final String PROCESS = "PROCESS"; |
||||
|
||||
public static final String SUCCESS = "SUCCESS"; |
||||
|
||||
public static final String FAILED = "FAILED"; |
||||
|
||||
public static boolean moving = false; |
||||
private UILabel vcsUpdateExistLabel; |
||||
private UILabel vcsUpdateFireLabel; |
||||
private BasicPane choosePane; |
||||
private CardLayout parentCard; |
||||
private JPanel parentPane; |
||||
private static final JProgressBar PROGRESS_BAR = new JProgressBar(); |
||||
|
||||
private JPanel progressPanel; |
||||
private UILabel tipLabel; |
||||
private UIButton successButton; |
||||
private UILabel iconLabel; |
||||
private UILabel successLabel; |
||||
private UILabel successTipLabel; |
||||
private UIButton failedButton; |
||||
private UILabel failedIconLabel; |
||||
private UILabel failedLabel; |
||||
private UILabel failedTipLabel; |
||||
|
||||
//保留全部
|
||||
private UIRadioButton moveAllButton; |
||||
//默认选项,保留部分
|
||||
private UIRadioButton moveDefaultButton; |
||||
//全部放弃
|
||||
private UIRadioButton moveNothingButton; |
||||
|
||||
private UISpinner spinner; |
||||
|
||||
private MoveCallBack callBack; |
||||
|
||||
private JPanel updatePane; |
||||
|
||||
private boolean visible = false; |
||||
|
||||
|
||||
public VcsMovePanel(CardLayout cardLayout, JPanel parentPane, MoveCallBack callBack) { |
||||
this.parentCard = cardLayout; |
||||
this.parentPane = parentPane; |
||||
this.callBack = callBack; |
||||
this.setLayout(new BorderLayout()); |
||||
updatePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(); |
||||
updatePane.setBackground(BACK_GROUND_COLOR); |
||||
//初始化迁移的面板
|
||||
initVcsLabel(updatePane); |
||||
//initVcsChoosePane
|
||||
initVcsChoosePane(); |
||||
//初始化listener
|
||||
initListener(); |
||||
this.add(updatePane); |
||||
checkVisible(); |
||||
//如果已经在迁移
|
||||
if (VcsMoveService.getInstance().isMoving()) { |
||||
initProcessPane(); |
||||
VcsMovePanel.this.getParentCard().show(getParentPane(), PROCESS); |
||||
} |
||||
} |
||||
|
||||
private void checkVisible() { |
||||
new SwingWorker<Boolean, Void>() { |
||||
@Override |
||||
protected Boolean doInBackground() throws Exception { |
||||
return VcsHelper.getInstance().checkMoveFunctionSupport(); |
||||
} |
||||
@Override |
||||
protected void done() { |
||||
try { |
||||
boolean useMove = get(); |
||||
VcsMovePanel.this.setVisible(useMove); |
||||
} catch (InterruptedException | ExecutionException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
}.execute(); |
||||
} |
||||
|
||||
private void initProcessPane() { |
||||
JPanel processPane = new JPanel(); |
||||
JPanel body = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); |
||||
PROGRESS_BAR.setStringPainted(true); |
||||
PROGRESS_BAR.setUI(new ModernUIProgressBarUI()); |
||||
PROGRESS_BAR.setBorderPainted(false); |
||||
PROGRESS_BAR.setOpaque(false); |
||||
PROGRESS_BAR.setBorder(null); |
||||
PROGRESS_BAR.setSize(BasicDialog.MEDIUM); |
||||
body.add(PROGRESS_BAR); |
||||
body.add(new UILabel(StringUtils.BLANK)); |
||||
tipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_While_Moving")); |
||||
tipLabel.setAlignmentX(CENTER_ALIGNMENT); |
||||
body.add(tipLabel); |
||||
processPane.add(body); |
||||
processPane.setLayout(FRGUIPaneFactory.createCenterLayout(body, 0.5f, 0.5f)); |
||||
parentPane.add(processPane, PROCESS); |
||||
} |
||||
|
||||
private void initVcsChoosePane() { |
||||
choosePane = new BasicPane() { |
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return Toolkit.i18nText("Fine-Design_Vcs_Deal_With_Entry"); |
||||
} |
||||
}; |
||||
VerticalFlowLayout layout = new VerticalFlowLayout(VerticalFlowLayout.TOP); |
||||
layout.setAlignLeft(true); |
||||
choosePane.setLayout(layout); |
||||
|
||||
//初始化上方的文字板块
|
||||
initTopDesc(); |
||||
//初始化中间区域的单选框
|
||||
initRadioButton(); |
||||
//初始化下方的Tip描述
|
||||
initTipDesc(); |
||||
} |
||||
|
||||
private void initTopDesc() { |
||||
UILabel label = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_How_To_Deal_With_Entry")); |
||||
choosePane.add(label); |
||||
} |
||||
|
||||
private void initTipDesc() { |
||||
UILabel descLabel = FRWidgetFactory.createLineWrapLabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Desc")); |
||||
descLabel.setForeground(TIP_COLOR); |
||||
choosePane.add(descLabel); |
||||
} |
||||
|
||||
private void initRadioButton() { |
||||
//保留全部
|
||||
moveAllButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Vcs_Move_All")); |
||||
//默认选项,保留部分
|
||||
moveDefaultButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Default")); |
||||
//全部放弃
|
||||
moveNothingButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Nothing")); |
||||
// 将按钮"保留部分"设置为选中状态
|
||||
moveDefaultButton.setSelected(true); |
||||
// 创建一个按钮组,添加三个按钮
|
||||
ButtonGroup buttonGroup = new ButtonGroup(); |
||||
buttonGroup.add(moveAllButton); |
||||
buttonGroup.add(moveDefaultButton); |
||||
buttonGroup.add(moveNothingButton); |
||||
|
||||
JPanel moveDefaultPanel = new JPanel(); |
||||
JPanel moveAllPane = new JPanel(); |
||||
JPanel moveNothingPane = new JPanel(); |
||||
moveAllPane.add(moveAllButton); |
||||
moveDefaultPanel.add(moveDefaultButton); |
||||
moveDefaultPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Default_Text_Left"))); |
||||
spinner = new UISpinner(MIN_VALUE, MAX_VALUE, STEP, DEFAULT_VALUE); |
||||
moveDefaultPanel.add(spinner); |
||||
moveDefaultPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Default_Text_Right"))); |
||||
moveNothingPane.add(moveNothingButton); |
||||
choosePane.add(moveAllPane); |
||||
choosePane.add(moveDefaultPanel); |
||||
choosePane.add(moveNothingPane); |
||||
} |
||||
|
||||
private void initVcsLabel(JPanel parent) { |
||||
vcsUpdateExistLabel = new UILabel(IconUtils.readIcon("/com/fr/design/vcs/vcs_move_icon.svg")); |
||||
vcsUpdateExistLabel.setText(Toolkit.i18nText("Fine-Design_Vcs_Can_Update")); |
||||
vcsUpdateFireLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Update")); |
||||
vcsUpdateFireLabel.setForeground(LABEL_COLOR); |
||||
vcsUpdateFireLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||
parent.add(vcsUpdateExistLabel); |
||||
parent.add(vcsUpdateFireLabel); |
||||
} |
||||
|
||||
private void initListener() { |
||||
vcsUpdateFireLabel.addMouseListener(new MouseAdapter() { |
||||
@Override |
||||
public void mouseClicked(MouseEvent e) { |
||||
BasicDialog dlg = choosePane.showMediumWindow(SwingUtilities.getWindowAncestor(VcsMovePanel.this), new DialogActionAdapter() { |
||||
@Override |
||||
public void doOk() { |
||||
//进度条面板
|
||||
initProcessPane(); |
||||
VcsMovePanel.this.getParentCard().next(getParentPane()); |
||||
VcsMoveStrategy strategy; |
||||
if (moveDefaultButton.isSelected()) { |
||||
strategy = VcsMoveStrategy.createNumStrategy((int) spinner.getValue()); |
||||
} else if (moveNothingButton.isSelected()) { |
||||
strategy = VcsMoveStrategy.ALL_GIVE_UP; |
||||
} else { |
||||
strategy = VcsMoveStrategy.ALL_RETAIN; |
||||
} |
||||
new MoveWorker(strategy).execute(); |
||||
} |
||||
}); |
||||
dlg.setVisible(true); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void initSuccessPane() { |
||||
JPanel successPane = new JPanel(); |
||||
JPanel body = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); |
||||
successButton = new UIButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Success_Go")); |
||||
initSuccessButtonListener(); |
||||
iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/vcs/move_success.svg")); |
||||
successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Success")); |
||||
successLabel.setFont(FONT); |
||||
successTipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Success_Tip")); |
||||
initStatusPane(successTipLabel, iconLabel, successLabel, successButton, body, SUCCESS, successPane); |
||||
} |
||||
|
||||
private void initSuccessButtonListener() { |
||||
successButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
doAfterMove(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void initFailedButtonListener() { |
||||
failedButton.addActionListener(new ActionListener() { |
||||
@Override |
||||
public void actionPerformed(ActionEvent e) { |
||||
doAfterMove(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void doAfterMove() { |
||||
visible = !VcsHelper.getInstance().isLegacyMode(); |
||||
updatePane.setVisible(!visible); |
||||
callBack.doCallBack(visible); |
||||
parentCard.show(parentPane, SETTING); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return StringUtils.EMPTY; |
||||
} |
||||
|
||||
public CardLayout getParentCard() { |
||||
return parentCard; |
||||
} |
||||
|
||||
public JPanel getParentPane() { |
||||
return parentPane; |
||||
} |
||||
|
||||
private void initFailedPane() { |
||||
JPanel failedPane = new JPanel(); |
||||
JPanel body = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); |
||||
failedButton = new UIButton(Toolkit.i18nText("Fine-Design_Vcs_Move_Failed_Go")); |
||||
initFailedButtonListener(); |
||||
failedIconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/vcs/move_failed.svg")); |
||||
failedLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Failed")); |
||||
failedLabel.setFont(FONT); |
||||
failedTipLabel = new UILabel(Toolkit.i18nText("Fine-Design_Vcs_Move_Failed_Tip")); |
||||
initStatusPane(failedTipLabel, failedIconLabel, failedLabel, failedButton, body, FAILED, failedPane); |
||||
} |
||||
|
||||
private void initStatusPane(UILabel tipLabel, UILabel iconLabel, UILabel label, UIButton button, JPanel body, String tag,JPanel statusPane) { |
||||
tipLabel.setForeground(TIP_COLOR); |
||||
body.add(iconLabel); |
||||
body.add(new UILabel(StringUtils.BLANK)); |
||||
body.add(label); |
||||
body.add(new UILabel(StringUtils.BLANK)); |
||||
body.add(tipLabel); |
||||
body.add(new UILabel(StringUtils.BLANK)); |
||||
body.add(button); |
||||
statusPane.add(body); |
||||
statusPane.setLayout(FRGUIPaneFactory.createCenterLayout(body, 0.5f, 0.5f)); |
||||
parentPane.add(statusPane, tag); |
||||
iconLabel.setAlignmentX(CENTER_ALIGNMENT); |
||||
label.setAlignmentX(CENTER_ALIGNMENT); |
||||
button.setAlignmentX(CENTER_ALIGNMENT); |
||||
tipLabel.setAlignmentX(CENTER_ALIGNMENT); |
||||
} |
||||
|
||||
|
||||
private class MoveWorker extends SwingWorker<Void, Integer> { |
||||
|
||||
private VcsMoveStrategy strategy; |
||||
|
||||
public MoveWorker(VcsMoveStrategy strategy) { |
||||
this.strategy = strategy; |
||||
} |
||||
|
||||
@Override |
||||
protected Void doInBackground() throws Exception { |
||||
try { |
||||
//开始迁移
|
||||
VcsMoveService.getInstance().startMove(new VcsMoveService.BaseMoveServiceWhileMoving() { |
||||
@Override |
||||
public void publishProgress() { |
||||
int num = VcsMoveService.getInstance().getCurrentMove(); |
||||
publish(num); |
||||
} |
||||
@Override |
||||
public void prepare4Move() { |
||||
PROGRESS_BAR.setMaximum(VcsMoveService.getInstance().getTotal()); |
||||
} |
||||
}, strategy); |
||||
} catch (Exception e) { |
||||
this.cancel(true); |
||||
VcsMoveService.getInstance().stopMoving(); |
||||
initFailedPane(); |
||||
VcsMovePanel.this.getParentCard().show(getParentPane(), FAILED); |
||||
FineLoggerFactory.getLogger().error("[VcsV2] Vcs move failed!"); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
protected void process(List<Integer> chunks) { |
||||
PROGRESS_BAR.setValue(chunks.get(chunks.size() - 1)); |
||||
} |
||||
|
||||
@Override |
||||
protected void done() { |
||||
VcsMoveService.getInstance().stopMoving(); |
||||
initSuccessPane(); |
||||
VcsMovePanel.this.getParentCard().show(getParentPane(), SUCCESS); |
||||
VcsHelper.getInstance().updateLegacyMode(); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 迁移回调事件 |
||||
* |
||||
*/ |
||||
public static class MoveCallBack { |
||||
/** |
||||
* 处理回调 |
||||
*/ |
||||
public void doCallBack(boolean visible){} |
||||
} |
||||
|
||||
} |
@ -1,36 +1,45 @@
|
||||
package com.fr.design.ui.compatible; |
||||
|
||||
import com.fr.design.jxbrowser.JxUIPane; |
||||
import com.fr.design.ui.ModernUIPane; |
||||
import com.fr.stable.os.OperatingSystem; |
||||
|
||||
/** |
||||
* 根据版本选择构造器 |
||||
* |
||||
* @author hades |
||||
* @version 10.0 |
||||
* Created by hades on 2021/6/13 |
||||
* @see {@link JxUIPane} |
||||
* @since 10.0 |
||||
* Created on 2021/6/13 |
||||
* @deprecated 6在下个版本弃用 |
||||
*/ |
||||
public class ModernUIPaneFactory { |
||||
|
||||
/** |
||||
* 获取一个 JxBrowser pane 的构造器 |
||||
* |
||||
* @param <T> 参数 |
||||
* @return 构造器 |
||||
*/ |
||||
public static <T> ModernUIPane.Builder<T> modernUIPaneBuilder() { |
||||
|
||||
if (isV7()) { |
||||
return new NewModernUIPane.Builder<>(); |
||||
return new JxUIPane.Builder<>(); |
||||
} else { |
||||
return new ModernUIPane.Builder<>(); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 判断 JxBrowser 版本是否在7或之上 |
||||
* |
||||
* @return 是否7或之上 |
||||
*/ |
||||
public static boolean isV7() { |
||||
|
||||
// 7.15的class不存在时 走老版本
|
||||
boolean hasJxBrowserV7_15 = true; |
||||
boolean jxBrowserV7 = true; |
||||
try { |
||||
Class.forName("com.teamdev.jxbrowser.net.Scheme"); |
||||
} catch (ClassNotFoundException e) { |
||||
hasJxBrowserV7_15 = false; |
||||
jxBrowserV7 = false; |
||||
} |
||||
|
||||
return OperatingSystem.isWindows() && hasJxBrowserV7_15; |
||||
|
||||
return jxBrowserV7; |
||||
} |
||||
} |
||||
|
@ -1,362 +0,0 @@
|
||||
package com.fr.design.ui.compatible; |
||||
|
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.gui.ibutton.UIButton; |
||||
import com.fr.design.gui.itoolbar.UIToolbar; |
||||
import com.fr.design.i18n.Toolkit; |
||||
import com.fr.design.ui.ModernUIConstants; |
||||
import com.fr.design.ui.ModernUIPane; |
||||
import com.fr.design.utils.gui.GUICoreUtils; |
||||
import com.fr.web.struct.AssembleComponent; |
||||
import com.teamdev.jxbrowser.browser.Browser; |
||||
import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; |
||||
import com.teamdev.jxbrowser.chromium.events.LoadListener; |
||||
import com.teamdev.jxbrowser.chromium.events.ScriptContextListener; |
||||
import com.teamdev.jxbrowser.engine.Engine; |
||||
import com.teamdev.jxbrowser.engine.EngineOptions; |
||||
import com.teamdev.jxbrowser.engine.RenderingMode; |
||||
import com.teamdev.jxbrowser.event.Observer; |
||||
import com.teamdev.jxbrowser.js.JsObject; |
||||
import com.teamdev.jxbrowser.net.Network; |
||||
import com.teamdev.jxbrowser.net.Scheme; |
||||
import com.teamdev.jxbrowser.net.callback.VerifyCertificateCallback; |
||||
import com.teamdev.jxbrowser.view.swing.BrowserView; |
||||
import org.jetbrains.annotations.Nullable; |
||||
|
||||
|
||||
import java.awt.BorderLayout; |
||||
import java.awt.Dimension; |
||||
import java.util.Map; |
||||
|
||||
import javax.swing.JDialog; |
||||
import javax.swing.SwingUtilities; |
||||
import javax.swing.WindowConstants; |
||||
|
||||
/** |
||||
* 基于v7 jxbrowser实现 |
||||
* |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2019-03-04 |
||||
* 用于加载html5的Swing容器,可以在设计选项设置中打开调试窗口,示例可查看:com.fr.design.ui.ModernUIPaneTest |
||||
*/ |
||||
public class NewModernUIPane<T> extends ModernUIPane<T> { |
||||
|
||||
private Browser browser; |
||||
private String namespace = "Pool"; |
||||
private String variable = "data"; |
||||
private String expression = "update()"; |
||||
private Scheme scheme; |
||||
private NxInterceptRequestCallback requestCallback; |
||||
|
||||
private NewModernUIPane() { |
||||
super(); |
||||
initialize(); |
||||
} |
||||
|
||||
private void initialize() { |
||||
setLayout(new BorderLayout()); |
||||
if (browser == null) { |
||||
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { |
||||
UIToolbar toolbar = new UIToolbar(); |
||||
add(toolbar, BorderLayout.NORTH); |
||||
UIButton openDebugButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Open_Debug_Window")); |
||||
toolbar.add(openDebugButton); |
||||
UIButton reloadButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Reload")); |
||||
toolbar.add(reloadButton); |
||||
UIButton closeButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Close_Window")); |
||||
toolbar.add(closeButton); |
||||
|
||||
openDebugButton.addActionListener(e -> showDebuggerDialog()); |
||||
|
||||
reloadButton.addActionListener(e -> browser.navigation().reloadIgnoringCache()); |
||||
|
||||
closeButton.addActionListener(e -> SwingUtilities.getWindowAncestor( |
||||
NewModernUIPane.this).setVisible(false)); |
||||
initializeBrowser(); |
||||
add(BrowserView.newInstance(browser), BorderLayout.CENTER); |
||||
} else { |
||||
initializeBrowser(); |
||||
add(BrowserView.newInstance(browser), BorderLayout.CENTER); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void showDebuggerDialog() { |
||||
JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(this)); |
||||
|
||||
Browser debugger = browser.engine().newBrowser(); |
||||
BrowserView debuggerView = BrowserView.newInstance(debugger); |
||||
dialog.add(debuggerView, BorderLayout.CENTER); |
||||
dialog.setSize(new Dimension(800, 400)); |
||||
GUICoreUtils.centerWindow(dialog); |
||||
dialog.setVisible(true); |
||||
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
||||
|
||||
browser.devTools().remoteDebuggingUrl().ifPresent(url -> { |
||||
debugger.navigation().loadUrl(url); |
||||
}); |
||||
} |
||||
|
||||
private void initializeBrowser() { |
||||
EngineOptions.Builder builder; |
||||
if (scheme != null && requestCallback != null) { |
||||
builder = EngineOptions.newBuilder(RenderingMode.HARDWARE_ACCELERATED).addSwitch("--disable-google-traffic").addScheme(scheme, requestCallback); |
||||
} else { |
||||
builder = EngineOptions.newBuilder(RenderingMode.HARDWARE_ACCELERATED).addSwitch("--disable-google-traffic"); |
||||
} |
||||
|
||||
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { |
||||
builder.remoteDebuggingPort(9222); |
||||
} |
||||
|
||||
Engine engine = Engine.newInstance(builder.build()); |
||||
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { |
||||
// 调试模式下,禁止HTTPS证书验证,使得可以正常访问商城测试服务器等
|
||||
Network network = engine.network(); |
||||
network.set(VerifyCertificateCallback.class, new VerifyCertificateCallback() { |
||||
@Nullable |
||||
@Override |
||||
public Response on(Params params) { |
||||
return VerifyCertificateCallback.Response.valid(); |
||||
} |
||||
}); |
||||
} |
||||
browser = engine.newBrowser(); |
||||
|
||||
// 初始化的时候,就把命名空间对象初始化好,确保window.a.b.c("a.b.c"为命名空间)对象都是初始化过的
|
||||
browser.set(InjectJsCallback.class, params -> { |
||||
params.frame().executeJavaScript(String.format(ModernUIConstants.SCRIPT_INIT_NAME_SPACE, namespace)); |
||||
return InjectJsCallback.Response.proceed(); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* 转向一个新的地址,相当于重新加载 |
||||
* |
||||
* @param url 新的地址 |
||||
*/ |
||||
@Override |
||||
public void redirect(String url) { |
||||
browser.navigation().loadUrl(url); |
||||
} |
||||
|
||||
/** |
||||
* 转向一个新的地址,相当于重新加载 |
||||
* |
||||
* @param url 新的地址 |
||||
* @param map 初始化参数 |
||||
*/ |
||||
@Override |
||||
public void redirect(String url, Map<String, String> map) { |
||||
if (requestCallback != null) { |
||||
requestCallback.setMap(map); |
||||
} |
||||
browser.navigation().loadUrl(url); |
||||
} |
||||
|
||||
@Override |
||||
protected String title4PopupWindow() { |
||||
return "Modern"; |
||||
} |
||||
|
||||
@Override |
||||
public void populate(final T t) { |
||||
browser.set(InjectJsCallback.class, params -> { |
||||
JsObject ns = params.frame().executeJavaScript("window." + namespace); |
||||
if (ns != null) { |
||||
ns.putProperty(variable, t); |
||||
} |
||||
return InjectJsCallback.Response.proceed(); |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
public T update() { |
||||
if (browser.mainFrame().isPresent()) { |
||||
return browser.mainFrame().get().executeJavaScript("window." + namespace + "." + expression); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public void disposeBrowser() { |
||||
|
||||
if (browser != null) { |
||||
browser.engine().close(); |
||||
browser = null; |
||||
} |
||||
|
||||
} |
||||
|
||||
public void clearCache() { |
||||
if (browser != null) { |
||||
browser.engine().httpCache().clear(); |
||||
} |
||||
} |
||||
|
||||
public void executeJavaScript(String javaScript) { |
||||
if (browser != null) { |
||||
browser.mainFrame().ifPresent(frame -> { |
||||
frame.executeJavaScript(javaScript); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public static class Builder<T> extends ModernUIPane.Builder<T> { |
||||
|
||||
private NewModernUIPane<T> pane = new NewModernUIPane<>(); |
||||
|
||||
public Builder() { |
||||
super((ModernUIPane<T>)null); |
||||
} |
||||
|
||||
public NewModernUIPane.Builder<T> prepare(InjectJsCallback callback) { |
||||
pane.browser.set(InjectJsCallback.class, callback); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载jar包中的资源 |
||||
* |
||||
* @param path 资源路径 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> withEMB(final String path) { |
||||
pane.scheme = Scheme.of("emb"); |
||||
pane.requestCallback = new NxComplexInterceptRequestCallback(null); |
||||
pane.browser.navigation().loadUrl("emb:" + path); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载url指向的资源 |
||||
* |
||||
* @param url 文件的地址 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> withURL(final String url) { |
||||
pane.scheme = Scheme.of("file"); |
||||
pane.requestCallback = new NxComplexInterceptRequestCallback(null); |
||||
pane.browser.navigation().loadUrl(url); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载url指向的资源 |
||||
* |
||||
* @param url 文件的地址 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> withURL(final String url, Map<String, String> map) { |
||||
pane.scheme = Scheme.of("file"); |
||||
pane.requestCallback = new NxInterceptRequestCallback(map); |
||||
pane.browser.navigation().loadUrl(url); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载Atom组件 |
||||
* |
||||
* @param component Atom组件 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> withComponent(AssembleComponent component) { |
||||
pane.scheme = Scheme.of("emb"); |
||||
pane.requestCallback = new NxComplexInterceptRequestCallback(component); |
||||
pane.browser.navigation().loadUrl("emb:dynamic"); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 加载Atom组件 |
||||
* |
||||
* @param component Atom组件 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> withComponent(AssembleComponent component, Map<String, String> map) { |
||||
pane.scheme = Scheme.of("emb"); |
||||
pane.requestCallback = new NxComplexInterceptRequestCallback(component, map); |
||||
pane.browser.navigation().loadUrl("emb:dynamic"); |
||||
return this; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 加载html文本内容 |
||||
* |
||||
* @param html 要加载html文本内容 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> withHTML(String html) { |
||||
pane.scheme = Scheme.of("html"); |
||||
pane.requestCallback = new NxInterceptRequestCallback(); |
||||
pane.browser.mainFrame().ifPresent(frame -> { |
||||
frame.loadHtml(html); |
||||
}); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 设置该前端页面做数据交换所使用的对象 |
||||
* |
||||
* @param namespace 对象名 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> namespace(String namespace) { |
||||
pane.namespace = namespace; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* java端往js端传数据时使用的变量名字 |
||||
* |
||||
* @param name 变量的名字 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> variable(String name) { |
||||
pane.variable = name; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* js端往java端传数据时执行的函数表达式 |
||||
* |
||||
* @param expression 函数表达式 |
||||
*/ |
||||
@Override |
||||
public NewModernUIPane.Builder<T> expression(String expression) { |
||||
pane.expression = expression; |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public NewModernUIPane.Builder<T> prepareForV6(ScriptContextListener contextListener) { |
||||
// do nothing
|
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public NewModernUIPane.Builder<T> prepareForV6(LoadListener loadListener) { |
||||
// do nothing
|
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public NewModernUIPane.Builder<T> prepareForV7(InjectJsCallback callback) { |
||||
return prepare(callback); |
||||
} |
||||
|
||||
@Override |
||||
public ModernUIPane.Builder<T> prepareForV7(Class event, Observer listener) { |
||||
|
||||
pane.browser.navigation().on(event, listener); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public NewModernUIPane<T> build() { |
||||
return pane; |
||||
} |
||||
} |
||||
} |
@ -1,86 +0,0 @@
|
||||
package com.fr.design.ui.compatible; |
||||
|
||||
import com.fr.design.ui.ModernRequestClient; |
||||
import com.fr.design.ui.ModernUIConstants; |
||||
import com.fr.general.IOUtils; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.web.struct.AssembleComponent; |
||||
import com.fr.web.struct.AtomBuilder; |
||||
import com.fr.web.struct.PathGroup; |
||||
import com.fr.web.struct.category.ScriptPath; |
||||
import com.fr.web.struct.category.StylePath; |
||||
|
||||
import java.io.InputStream; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2020/3/25 |
||||
*/ |
||||
public class NxComplexInterceptRequestCallback extends NxInterceptRequestCallback { |
||||
|
||||
private AssembleComponent component; |
||||
|
||||
public NxComplexInterceptRequestCallback(AssembleComponent component) { |
||||
this.component = component; |
||||
} |
||||
|
||||
public NxComplexInterceptRequestCallback(AssembleComponent component, Map<String, String> map) { |
||||
super(map); |
||||
this.component = component; |
||||
} |
||||
|
||||
@Override |
||||
protected Response next(Params params, String path) { |
||||
if (path.startsWith("emb:dynamic")) { |
||||
String text = htmlText(map); |
||||
return Response.intercept(generateBasicUrlRequestJob(params, "text/html", text.getBytes(StandardCharsets.UTF_8))); |
||||
} else { |
||||
int index = path.indexOf("="); |
||||
if (index > 0) { |
||||
path = path.substring(index + 1); |
||||
} else { |
||||
path = path.substring(4); |
||||
} |
||||
InputStream inputStream = IOUtils.readResource(path); |
||||
if (inputStream == null) { |
||||
return Response.proceed(); |
||||
} |
||||
return Response.intercept(generateBasicUrlRequestJob(params, getMimeType(path), IOUtils.inputStream2Bytes(inputStream))); |
||||
} |
||||
} |
||||
|
||||
private String htmlText(Map<String, String> map) { |
||||
PathGroup pathGroup = AtomBuilder.create().buildAssembleFilePath(ModernRequestClient.KEY, component); |
||||
StylePath[] stylePaths = pathGroup.toStylePathGroup(); |
||||
StringBuilder styleText = new StringBuilder(); |
||||
for (StylePath path : stylePaths) { |
||||
if (StringUtils.isNotBlank(path.toFilePath())) { |
||||
styleText.append("<link rel=\"stylesheet\" href=\"emb:"); |
||||
styleText.append(path.toFilePath()); |
||||
styleText.append("\"/>"); |
||||
} |
||||
} |
||||
String result = ModernUIConstants.HTML_TPL.replaceAll("##style##", styleText.toString()); |
||||
ScriptPath[] scriptPaths = pathGroup.toScriptPathGroup(); |
||||
StringBuilder scriptText = new StringBuilder(); |
||||
for (ScriptPath path : scriptPaths) { |
||||
if (StringUtils.isNotBlank(path.toFilePath())) { |
||||
scriptText.append("<script src=\"emb:"); |
||||
scriptText.append(path.toFilePath()); |
||||
scriptText.append("\"></script>"); |
||||
} |
||||
} |
||||
result = result.replaceAll("##script##", scriptText.toString()); |
||||
if (map != null) { |
||||
for (Map.Entry<String, String> entry : map.entrySet()) { |
||||
String key = entry.getKey(); |
||||
String value = entry.getValue(); |
||||
result = result.replaceAll("\\$\\{" + key + "}", value); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
} |
@ -1,148 +0,0 @@
|
||||
package com.fr.design.ui.compatible; |
||||
|
||||
import com.fr.base.TemplateUtils; |
||||
import com.fr.general.IOUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.stable.ArrayUtils; |
||||
import com.fr.stable.EncodeConstants; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.third.org.apache.commons.codec.net.URLCodec; |
||||
import com.teamdev.jxbrowser.net.HttpHeader; |
||||
import com.teamdev.jxbrowser.net.HttpStatus; |
||||
import com.teamdev.jxbrowser.net.UrlRequest; |
||||
import com.teamdev.jxbrowser.net.UrlRequestJob; |
||||
|
||||
import com.teamdev.jxbrowser.net.callback.InterceptUrlRequestCallback; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.URI; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2020/3/25 |
||||
*/ |
||||
public class NxInterceptRequestCallback implements InterceptUrlRequestCallback { |
||||
|
||||
Map<String, String> map; |
||||
|
||||
public NxInterceptRequestCallback() { |
||||
} |
||||
|
||||
public NxInterceptRequestCallback(Map<String, String> map) { |
||||
this.map = map; |
||||
} |
||||
|
||||
@Override |
||||
public Response on(Params params) { |
||||
UrlRequest urlRequest = params.urlRequest(); |
||||
String path = urlRequest.url(); |
||||
if (path.startsWith("file:")) { |
||||
Optional<UrlRequestJob> optional = generateFileProtocolUrlRequestJob(params, path); |
||||
if (optional.isPresent()) { |
||||
return Response.intercept(optional.get()); |
||||
} |
||||
} else { |
||||
return next(params, path); |
||||
} |
||||
return Response.proceed(); |
||||
} |
||||
|
||||
Response next(Params params, String path) { |
||||
return Response.proceed(); |
||||
} |
||||
|
||||
private Optional<UrlRequestJob> generateFileProtocolUrlRequestJob(Params params, String path) { |
||||
try { |
||||
String url = new URLCodec().decode(path); |
||||
String filePath = TemplateUtils.renderParameter4Tpl(url, map); |
||||
File file = new File(URI.create(filePath).getPath()); |
||||
InputStream inputStream = IOUtils.readResource(file.getAbsolutePath()); |
||||
String mimeType = getMimeType(path); |
||||
byte[] bytes; |
||||
if (isPlainText(mimeType)) { |
||||
String text = IOUtils.inputStream2String(inputStream, EncodeConstants.ENCODING_UTF_8); |
||||
text = TemplateUtils.renderParameter4Tpl(text, map); |
||||
bytes = text.getBytes(StandardCharsets.UTF_8); |
||||
} else { |
||||
bytes = IOUtils.inputStream2Bytes(inputStream); |
||||
} |
||||
return Optional.of(generateBasicUrlRequestJob(params, mimeType, bytes)); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return Optional.empty(); |
||||
} |
||||
|
||||
private boolean isPlainText(String mimeType) { |
||||
return ArrayUtils.contains(new String[]{"text/html", "text/javascript", "text/css"}, mimeType); |
||||
} |
||||
|
||||
UrlRequestJob generateBasicUrlRequestJob(Params params, String mimeType, byte[] bytes) { |
||||
UrlRequestJob.Options options = UrlRequestJob.Options |
||||
.newBuilder(HttpStatus.OK) |
||||
.addHttpHeader(HttpHeader.of("Content-Type", mimeType)) |
||||
.build(); |
||||
UrlRequestJob urlRequestJob = params.newUrlRequestJob(options); |
||||
urlRequestJob.write(bytes); |
||||
urlRequestJob.complete(); |
||||
return urlRequestJob; |
||||
} |
||||
|
||||
String getMimeType(String path) { |
||||
// 去除 xxx?xxx 后面部分
|
||||
int index = path.indexOf("?"); |
||||
if (index != -1) { |
||||
path = path.substring(0, path.indexOf("?")); |
||||
} |
||||
if (StringUtils.isBlank(path)) { |
||||
return "text/html"; |
||||
} |
||||
if (path.endsWith(".html")) { |
||||
return "text/html"; |
||||
} |
||||
if (path.endsWith(".css")) { |
||||
return "text/css"; |
||||
} |
||||
if (path.endsWith(".js")) { |
||||
return "text/javascript"; |
||||
} |
||||
if (path.endsWith(".svg")) { |
||||
return "image/svg+xml"; |
||||
} |
||||
if (path.endsWith(".png")) { |
||||
return "image/png"; |
||||
} |
||||
if (path.endsWith(".jpeg")) { |
||||
return "image/jpeg"; |
||||
} |
||||
if (path.endsWith(".gif")) { |
||||
return "image/gif"; |
||||
} |
||||
if (path.endsWith(".woff")) { |
||||
return "font/woff"; |
||||
} |
||||
if (path.endsWith(".ttf")) { |
||||
return "truetype"; |
||||
} |
||||
if (path.endsWith(".eot")) { |
||||
return "embedded-opentype"; |
||||
} |
||||
Path file = new File(path).toPath(); |
||||
try { |
||||
return Files.probeContentType(file); |
||||
} catch (IOException e) { |
||||
return "text/html"; |
||||
} |
||||
} |
||||
|
||||
public void setMap(Map<String, String> map) { |
||||
this.map = map; |
||||
} |
||||
} |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
@ -1,57 +0,0 @@
|
||||
.background-login-close { |
||||
color: white !important; |
||||
font-weight: bold; |
||||
font-size: 14px; |
||||
} |
||||
.background-guide-close { |
||||
background: url(./img/icon_install_normal.png) no-repeat center center; |
||||
background-size: cover; |
||||
width: 20px; |
||||
height: 20px; |
||||
cursor: pointer; |
||||
} |
||||
.background-guide-close:hover { |
||||
background-color: #E8E8E9; |
||||
} |
||||
.background-login-loading { |
||||
background: url(./img/login_loading.gif) no-repeat center center; |
||||
} |
||||
.background-plugin-need-update { |
||||
background: url(./img/icon_new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-cant-use { |
||||
background: url(./img/icon_cantuse.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable { |
||||
background: url(./img/icon_disable.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable-new { |
||||
background: url(./img/icon_disable-new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-selected { |
||||
background: url(./img/icon_marked.png) no-repeat center center; |
||||
} |
||||
.background-shop-title-close { |
||||
background: url(./img/icon_close40x40_normal.svg) no-repeat center center; |
||||
} |
||||
.background-dialog-confirm { |
||||
background: url(./img/warning.png) no-repeat center center; |
||||
background-size: contain; |
||||
} |
||||
.background-close-button { |
||||
background: url(./img/icon_close9x9_normal.png) no-repeat center center; |
||||
} |
||||
.background-close-button:hover { |
||||
background: url(./img/icon_close9x9_hover.png) no-repeat center center; |
||||
} |
||||
.designer-login-guide { |
||||
background: url(./img/login_guide.png) no-repeat center center; |
||||
background-size: cover; |
||||
} |
||||
.designer-guide-login-button { |
||||
font-size: 14px; |
||||
color: white; |
||||
border-radius: 4px; |
||||
background-color: #3685F2; |
||||
} |
||||
|
After Width: | Height: | Size: 459 KiB |
After Width: | Height: | Size: 459 KiB |
Before Width: | Height: | Size: 473 KiB After Width: | Height: | Size: 473 KiB |
After Width: | Height: | Size: 482 KiB |
@ -0,0 +1,57 @@
|
||||
.background-login-close { |
||||
color: white !important; |
||||
font-weight: bold; |
||||
font-size: 14px; |
||||
} |
||||
.background-guide-close { |
||||
background: url(../../../img/icon_install_normal.png) no-repeat center center; |
||||
background-size: cover; |
||||
width: 20px; |
||||
height: 20px; |
||||
cursor: pointer; |
||||
} |
||||
.background-guide-close:hover { |
||||
background-color: #E8E8E9; |
||||
} |
||||
.background-login-loading { |
||||
background: url(../../../img/login_loading.gif) no-repeat center center; |
||||
} |
||||
.background-plugin-need-update { |
||||
background: url(../../../img/icon_new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-cant-use { |
||||
background: url(../../../img/icon_cantuse.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable { |
||||
background: url(../../../img/icon_disable.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable-new { |
||||
background: url(../../../img/icon_disable-new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-selected { |
||||
background: url(../../../img/icon_marked.png) no-repeat center center; |
||||
} |
||||
.background-shop-title-close { |
||||
background: url(../../../img/icon_close40x40_normal.svg) no-repeat center center; |
||||
} |
||||
.background-dialog-confirm { |
||||
background: url(../../../img/warning.png) no-repeat center center; |
||||
background-size: contain; |
||||
} |
||||
.background-close-button { |
||||
background: url(../../../img/icon_close9x9_normal.png) no-repeat center center; |
||||
} |
||||
.background-close-button:hover { |
||||
background: url(../../../img/icon_close9x9_hover.png) no-repeat center center; |
||||
} |
||||
.designer-login-guide { |
||||
background: url(../../../img/guide/login_guide_en.png) no-repeat center center; |
||||
background-size: cover; |
||||
} |
||||
.designer-guide-login-button { |
||||
font-size: 14px; |
||||
color: white; |
||||
border-radius: 4px; |
||||
background-color: #3685F2; |
||||
} |
||||
|
@ -0,0 +1,57 @@
|
||||
.background-login-close { |
||||
color: white !important; |
||||
font-weight: bold; |
||||
font-size: 14px; |
||||
} |
||||
.background-guide-close { |
||||
background: url(../../../img/icon_install_normal.png) no-repeat center center; |
||||
background-size: cover; |
||||
width: 20px; |
||||
height: 20px; |
||||
cursor: pointer; |
||||
} |
||||
.background-guide-close:hover { |
||||
background-color: #E8E8E9; |
||||
} |
||||
.background-login-loading { |
||||
background: url(../../../img/login_loading.gif) no-repeat center center; |
||||
} |
||||
.background-plugin-need-update { |
||||
background: url(../../../img/icon_new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-cant-use { |
||||
background: url(../../../img/icon_cantuse.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable { |
||||
background: url(../../../img/icon_disable.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable-new { |
||||
background: url(../../../img/icon_disable-new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-selected { |
||||
background: url(../../../img/icon_marked.png) no-repeat center center; |
||||
} |
||||
.background-shop-title-close { |
||||
background: url(../../../img/icon_close40x40_normal.svg) no-repeat center center; |
||||
} |
||||
.background-dialog-confirm { |
||||
background: url(../../../img/warning.png) no-repeat center center; |
||||
background-size: contain; |
||||
} |
||||
.background-close-button { |
||||
background: url(../../../img/icon_close9x9_normal.png) no-repeat center center; |
||||
} |
||||
.background-close-button:hover { |
||||
background: url(../../../img/icon_close9x9_hover.png) no-repeat center center; |
||||
} |
||||
.designer-login-guide { |
||||
background: url(../../../img/guide/login_guide_en.png) no-repeat center center; |
||||
background-size: cover; |
||||
} |
||||
.designer-guide-login-button { |
||||
font-size: 14px; |
||||
color: white; |
||||
border-radius: 4px; |
||||
background-color: #3685F2; |
||||
} |
||||
|
@ -0,0 +1,57 @@
|
||||
.background-login-close { |
||||
color: white !important; |
||||
font-weight: bold; |
||||
font-size: 14px; |
||||
} |
||||
.background-guide-close { |
||||
background: url(../../../img/icon_install_normal.png) no-repeat center center; |
||||
background-size: cover; |
||||
width: 20px; |
||||
height: 20px; |
||||
cursor: pointer; |
||||
} |
||||
.background-guide-close:hover { |
||||
background-color: #E8E8E9; |
||||
} |
||||
.background-login-loading { |
||||
background: url(../../../img/login_loading.gif) no-repeat center center; |
||||
} |
||||
.background-plugin-need-update { |
||||
background: url(../../../img/icon_new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-cant-use { |
||||
background: url(../../../img/icon_cantuse.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable { |
||||
background: url(../../../img/icon_disable.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable-new { |
||||
background: url(../../../img/icon_disable-new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-selected { |
||||
background: url(../../../img/icon_marked.png) no-repeat center center; |
||||
} |
||||
.background-shop-title-close { |
||||
background: url(../../../img/icon_close40x40_normal.svg) no-repeat center center; |
||||
} |
||||
.background-dialog-confirm { |
||||
background: url(../../../img/warning.png) no-repeat center center; |
||||
background-size: contain; |
||||
} |
||||
.background-close-button { |
||||
background: url(../../../img/icon_close9x9_normal.png) no-repeat center center; |
||||
} |
||||
.background-close-button:hover { |
||||
background: url(../../../img/icon_close9x9_hover.png) no-repeat center center; |
||||
} |
||||
.designer-login-guide { |
||||
background: url(../../../img/guide/login_guide_en.png) no-repeat center center; |
||||
background-size: cover; |
||||
} |
||||
.designer-guide-login-button { |
||||
font-size: 14px; |
||||
color: white; |
||||
border-radius: 4px; |
||||
background-color: #3685F2; |
||||
} |
||||
|
@ -0,0 +1,57 @@
|
||||
.background-login-close { |
||||
color: white !important; |
||||
font-weight: bold; |
||||
font-size: 14px; |
||||
} |
||||
.background-guide-close { |
||||
background: url(../../../img/icon_install_normal.png) no-repeat center center; |
||||
background-size: cover; |
||||
width: 20px; |
||||
height: 20px; |
||||
cursor: pointer; |
||||
} |
||||
.background-guide-close:hover { |
||||
background-color: #E8E8E9; |
||||
} |
||||
.background-login-loading { |
||||
background: url(../../../img/login_loading.gif) no-repeat center center; |
||||
} |
||||
.background-plugin-need-update { |
||||
background: url(../../../img/icon_new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-cant-use { |
||||
background: url(../../../img/icon_cantuse.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable { |
||||
background: url(../../../img/icon_disable.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable-new { |
||||
background: url(../../../img/icon_disable-new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-selected { |
||||
background: url(../../../img/icon_marked.png) no-repeat center center; |
||||
} |
||||
.background-shop-title-close { |
||||
background: url(../../../img/icon_close40x40_normal.svg) no-repeat center center; |
||||
} |
||||
.background-dialog-confirm { |
||||
background: url(../../../img/warning.png) no-repeat center center; |
||||
background-size: contain; |
||||
} |
||||
.background-close-button { |
||||
background: url(../../../img/icon_close9x9_normal.png) no-repeat center center; |
||||
} |
||||
.background-close-button:hover { |
||||
background: url(../../../img/icon_close9x9_hover.png) no-repeat center center; |
||||
} |
||||
.designer-login-guide { |
||||
background: url(../../../img/guide/login_guide_zh.png) no-repeat center center; |
||||
background-size: cover; |
||||
} |
||||
.designer-guide-login-button { |
||||
font-size: 14px; |
||||
color: white; |
||||
border-radius: 4px; |
||||
background-color: #3685F2; |
||||
} |
||||
|
@ -0,0 +1,57 @@
|
||||
.background-login-close { |
||||
color: white !important; |
||||
font-weight: bold; |
||||
font-size: 14px; |
||||
} |
||||
.background-guide-close { |
||||
background: url(../../../img/icon_install_normal.png) no-repeat center center; |
||||
background-size: cover; |
||||
width: 20px; |
||||
height: 20px; |
||||
cursor: pointer; |
||||
} |
||||
.background-guide-close:hover { |
||||
background-color: #E8E8E9; |
||||
} |
||||
.background-login-loading { |
||||
background: url(../../../img/login_loading.gif) no-repeat center center; |
||||
} |
||||
.background-plugin-need-update { |
||||
background: url(../../../img/icon_new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-cant-use { |
||||
background: url(../../../img/icon_cantuse.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable { |
||||
background: url(../../../img/icon_disable.png) no-repeat center center; |
||||
} |
||||
.background-plugin-is-disable-new { |
||||
background: url(../../../img/icon_disable-new.png) no-repeat center center; |
||||
} |
||||
.background-plugin-selected { |
||||
background: url(../../../img/icon_marked.png) no-repeat center center; |
||||
} |
||||
.background-shop-title-close { |
||||
background: url(../../../img/icon_close40x40_normal.svg) no-repeat center center; |
||||
} |
||||
.background-dialog-confirm { |
||||
background: url(../../../img/warning.png) no-repeat center center; |
||||
background-size: contain; |
||||
} |
||||
.background-close-button { |
||||
background: url(../../../img/icon_close9x9_normal.png) no-repeat center center; |
||||
} |
||||
.background-close-button:hover { |
||||
background: url(../../../img/icon_close9x9_hover.png) no-repeat center center; |
||||
} |
||||
.designer-login-guide { |
||||
background: url(../../../img/guide/login_guide_zh_TW.png) no-repeat center center; |
||||
background-size: cover; |
||||
} |
||||
.designer-guide-login-button { |
||||
font-size: 14px; |
||||
color: white; |
||||
border-radius: 4px; |
||||
background-color: #3685F2; |
||||
} |
||||
|
After Width: | Height: | Size: 549 B |
After Width: | Height: | Size: 533 B |
After Width: | Height: | Size: 821 B |
@ -0,0 +1,49 @@
|
||||
package com.fr.design.file; |
||||
|
||||
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; |
||||
import com.fr.file.filetree.FileNode; |
||||
import org.junit.Assert; |
||||
import org.junit.Test; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author Levy.Xie |
||||
* @version 11.0 |
||||
* Created by Levy.Xie on 2023/05/23 |
||||
*/ |
||||
public class FileOperationHelperTest { |
||||
|
||||
@Test |
||||
public void testIsSubDirectory() { |
||||
FileNode sourceNode1 = new FileNode("/usr/local/webroot/reportlets", true); |
||||
FileNode sourceNode2 = new FileNode("/usr/local/webroot/reportlets/demo", true); |
||||
FileNode sourceNode3 = new FileNode("/usr/local/webroot/reportlets/doc", true); |
||||
FileNode sourceNode4 = new FileNode("/usr/local/webroot/reportlets/doc/1.cpt", false); |
||||
FileNode sourceNode5 = new FileNode("/usr/local/webroot/reportlets/demo/test", true); |
||||
FileNode sourceNode6 = new FileNode("/usr/local/webroot/reportlets/demo/test", false); |
||||
FileNode sourceNode7 = new FileNode("/usr/local/webroot/reportlets/demo/test123", true); |
||||
FileNode sourceNode8 = new FileNode("/usr/local/webroot/reportlets/../reportlets/demo/test", true); |
||||
FileNode sourceNode9 = new FileNode("/usr/local/webroot/reportlets/../reportlets/demo/test/c", true); |
||||
|
||||
FileNode targetNode = new FileNode("/usr/local/webroot/reportlets/demo/test", true); |
||||
|
||||
Assert.assertTrue(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode1, targetNode)); |
||||
Assert.assertTrue(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode2, targetNode)); |
||||
Assert.assertFalse(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode3, targetNode)); |
||||
Assert.assertFalse(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode4, targetNode)); |
||||
Assert.assertTrue(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode5, targetNode)); |
||||
Assert.assertFalse(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode6, targetNode)); |
||||
Assert.assertFalse(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode7, targetNode)); |
||||
Assert.assertTrue(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode8, targetNode)); |
||||
Assert.assertFalse(FileOperationHelper.getInstance().isSubDirectoryOrSame(sourceNode9, targetNode)); |
||||
|
||||
|
||||
ExpandMutableTreeNode treeNode1 = new ExpandMutableTreeNode(sourceNode1); |
||||
ExpandMutableTreeNode treeNode2 = new ExpandMutableTreeNode(sourceNode2); |
||||
ExpandMutableTreeNode treeNode3 = new ExpandMutableTreeNode(sourceNode3); |
||||
ExpandMutableTreeNode treeNode4 = new ExpandMutableTreeNode(sourceNode4); |
||||
Assert.assertTrue(FileOperationHelper.getInstance().isSubDirectoryOrSame(new ExpandMutableTreeNode[]{treeNode1, treeNode2, treeNode3}, targetNode)); |
||||
Assert.assertFalse(FileOperationHelper.getInstance().isSubDirectoryOrSame(new ExpandMutableTreeNode[]{treeNode3, treeNode4}, targetNode)); |
||||
} |
||||
} |
@ -0,0 +1,289 @@
|
||||
package com.fr.design.file; |
||||
|
||||
import com.fr.design.DesignModelAdapter; |
||||
import com.fr.design.ExtraDesignClassManager; |
||||
import com.fr.design.designer.TargetComponent; |
||||
import com.fr.design.gui.frpane.HyperlinkGroupPane; |
||||
import com.fr.design.gui.frpane.HyperlinkGroupPaneActionProvider; |
||||
import com.fr.design.gui.imenu.UIMenuItem; |
||||
import com.fr.design.mainframe.AuthorityEditPane; |
||||
import com.fr.design.mainframe.BaseUndoState; |
||||
import com.fr.design.mainframe.JTemplate; |
||||
import com.fr.design.mainframe.template.info.TemplateProcessInfo; |
||||
import com.fr.design.menu.ShortCut; |
||||
import com.fr.design.menu.ToolBarDef; |
||||
import com.fr.plugin.injectable.PluginModule; |
||||
import junit.framework.TestCase; |
||||
import org.junit.Assert; |
||||
|
||||
import javax.swing.Icon; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.JPanel; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class MultiTemplateTabPaneTest extends TestCase { |
||||
@Override |
||||
protected void setUp() throws Exception { |
||||
PluginModule.registerAgent(PluginModule.ExtraDesign, new ExtraDesignClassManager()); |
||||
} |
||||
|
||||
/** |
||||
* 当前显示模式A,传入index左边(含当前)或右边有模式A的模板,返回最近的模式A模板index(优先左边) |
||||
*/ |
||||
public void test_index_left_has_same_mode_temp() { |
||||
//当前显示模式A,传入index左边(含当前)有模式A的模板,返回左边最近的模式A模板index
|
||||
List<JTemplate<?, ?>> openedTemplateList = new ArrayList<>(); |
||||
openedTemplateList.add(new A_Mode()); |
||||
Assert.assertEquals(0, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
openedTemplateList.add(new A_Mode()); |
||||
Assert.assertEquals(1, MultiTemplateTabUtils.calShowTemplateIndex(1, openedTemplateList, "A_Mode")); |
||||
openedTemplateList.add(new B_Mode()); |
||||
Assert.assertEquals(1, MultiTemplateTabUtils.calShowTemplateIndex(1, openedTemplateList, "A_Mode")); |
||||
} |
||||
|
||||
public void test_index_left_has_not_but_right_has_same_mode_temp() { |
||||
//当前显示模式A,传入index左边没有但是右边有模式A的模板,返回右边最近的模式A模板index
|
||||
List<JTemplate<?, ?>> openedTemplateList = new ArrayList<>(); |
||||
openedTemplateList.add(new B_Mode()); |
||||
openedTemplateList.add(new A_Mode()); |
||||
Assert.assertEquals(1, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
openedTemplateList.add(1, new B_Mode()); |
||||
openedTemplateList.add(new A_Mode()); |
||||
Assert.assertEquals(2, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
openedTemplateList.add(new A_Mode()); |
||||
Assert.assertEquals(2, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
} |
||||
|
||||
/** |
||||
* 当前显示模式A,没有模式A的模板,左边(含当前)或者右边有其他模式的模板,返回最近的其他模式模式模板index(优先左边) |
||||
*/ |
||||
public void test_no_same_mode_temp_but_index_left_has_other_mode_temp() { |
||||
//当前显示模式A,没有模式A的模板,左边(含当前)有其他模式模板,返回左边最近的其他模式模板index
|
||||
List<JTemplate<?, ?>> openedTemplateList = new ArrayList<>(); |
||||
openedTemplateList.add(new B_Mode()); |
||||
Assert.assertEquals(0, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
openedTemplateList.add(new B_Mode()); |
||||
Assert.assertEquals(1, MultiTemplateTabUtils.calShowTemplateIndex(1, openedTemplateList, "A_Mode")); |
||||
} |
||||
|
||||
|
||||
public void test_has_no_temp() { |
||||
//当前显示模式A,没有模式A的模板,也没有其他模式的模板,返回-1
|
||||
List<JTemplate<?, ?>> openedTemplateList = new ArrayList<>(); |
||||
Assert.assertEquals(-1, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
} |
||||
|
||||
|
||||
public void test_if_index_less_than_zero_or_more_than_open_temp_size() { |
||||
//index<0 或者超出openTemplateList.size时,返回-1
|
||||
List<JTemplate<?, ?>> openedTemplateList = new ArrayList<>(); |
||||
Assert.assertEquals(-1, MultiTemplateTabUtils.calShowTemplateIndex(-1, openedTemplateList, "A_Mode")); |
||||
Assert.assertEquals(-1, MultiTemplateTabUtils.calShowTemplateIndex(0, openedTemplateList, "A_Mode")); |
||||
openedTemplateList.add(new A_Mode()); |
||||
Assert.assertEquals(-1, MultiTemplateTabUtils.calShowTemplateIndex(1, openedTemplateList, "A_Mode")); |
||||
} |
||||
|
||||
private class A_Mode extends AbstractTestMode { |
||||
public String getTemplateTabOperatorType() { |
||||
return "A_Mode"; |
||||
} |
||||
} |
||||
|
||||
private class B_Mode extends AbstractTestMode { |
||||
public String getTemplateTabOperatorType() { |
||||
return "B_Mode"; |
||||
} |
||||
} |
||||
|
||||
private abstract class AbstractTestMode extends JTemplate { |
||||
public AbstractTestMode() { |
||||
} |
||||
|
||||
@Override |
||||
public void copy() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public boolean paste() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public boolean cut() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public AuthorityEditPane createAuthorityEditPane() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public JPanel getEastUpPane() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public JPanel getEastDownPane() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public ToolBarDef[] toolbars4Target() { |
||||
return new ToolBarDef[0]; |
||||
} |
||||
|
||||
@Override |
||||
public JPanel[] toolbarPanes4Form() { |
||||
return new JPanel[0]; |
||||
} |
||||
|
||||
@Override |
||||
public JComponent[] toolBarButton4Form() { |
||||
return new JComponent[0]; |
||||
} |
||||
|
||||
@Override |
||||
public JComponent toolBar4Authority() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public int getToolBarHeight() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public void refreshEastPropertiesPane() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public TargetComponent getCurrentElementCasePane() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public JComponent getCurrentReportComponentPane() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public TemplateProcessInfo getProcessInfo() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void setJTemplateResolution(int resolution) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int getJTemplateResolution() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
protected JComponent createCenterPane() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void removeTemplateSelection() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void refreshContainer() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void removeParameterPaneSelection() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void setScale(int resolution) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int getScale() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public int selfAdaptUpdate() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
protected DesignModelAdapter createDesignModel() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public UIMenuItem[] createMenuItem4Preview() { |
||||
return new UIMenuItem[0]; |
||||
} |
||||
|
||||
@Override |
||||
protected BaseUndoState<?> createUndoState() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String suffix() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public ShortCut[] shortcut4TemplateMenu() { |
||||
return new ShortCut[0]; |
||||
} |
||||
|
||||
@Override |
||||
public ShortCut[] shortCuts4Authority() { |
||||
return new ShortCut[0]; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isJWorkBook() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public HyperlinkGroupPane getHyperLinkPane(HyperlinkGroupPaneActionProvider hyperlinkGroupPaneActionProvider) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public HyperlinkGroupPane getHyperLinkPaneNoPop(HyperlinkGroupPaneActionProvider hyperlinkGroupPaneActionProvider) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void setAuthorityMode(boolean isUpMode) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public Icon getIcon() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String route() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
protected void applyUndoState(BaseUndoState baseUndoState) { |
||||
|
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,72 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.teamdev.jxbrowser.js.JsAccessible; |
||||
|
||||
import javax.swing.JButton; |
||||
import javax.swing.JFrame; |
||||
import javax.swing.JOptionPane; |
||||
import javax.swing.JPanel; |
||||
import javax.swing.WindowConstants; |
||||
import java.awt.BorderLayout; |
||||
import java.awt.FlowLayout; |
||||
|
||||
public class JxUIPaneTest { |
||||
|
||||
public static void main(String... args) { |
||||
final JFrame frame = new JFrame(); |
||||
frame.setSize(1200, 800); |
||||
JPanel contentPane = (JPanel) frame.getContentPane(); |
||||
// 是否需要开启调试窗口
|
||||
DesignerEnvManager.getEnvManager().setOpenDebug(true); |
||||
final JxUIPane<Model> pane = new JxUIPane.Builder<Model>() |
||||
.withEMB("/com/fr/design/ui/demo.html").namespace("Pool").build(); |
||||
contentPane.add(pane, BorderLayout.CENTER); |
||||
|
||||
Model model = new Model(); |
||||
model.setAge(20); |
||||
model.setName("Pick"); |
||||
pane.populate(model); |
||||
|
||||
JPanel panel = new JPanel(new FlowLayout()); |
||||
contentPane.add(panel, BorderLayout.SOUTH); |
||||
JButton button = new JButton("点击我可以看到Swing的弹框,输出填写的信息"); |
||||
panel.add(button); |
||||
button.addActionListener(e -> { |
||||
Model returnValue = pane.update(); |
||||
if (returnValue != null) { |
||||
JOptionPane.showMessageDialog(frame, String.format("姓名为:%s,年龄为:%d", returnValue.getName(), returnValue.getAge())); |
||||
} |
||||
}); |
||||
frame.setVisible(true); |
||||
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); |
||||
} |
||||
|
||||
@JsAccessible |
||||
public static class Model { |
||||
private String name; |
||||
private int age; |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public int getAge() { |
||||
return age; |
||||
} |
||||
|
||||
public void setAge(int age) { |
||||
this.age = age; |
||||
} |
||||
|
||||
public void print(String message) { |
||||
System.out.println(message); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,21 @@
|
||||
package com.fr.design.jxbrowser; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.Test; |
||||
|
||||
/** |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/6/13 |
||||
*/ |
||||
public class MimeTypeTest { |
||||
|
||||
@Test |
||||
public void getMimeType() { |
||||
Assert.assertEquals("text/html", MimeType.parseMimeType("http://a.html")); |
||||
Assert.assertEquals("text/html", MimeType.parseMimeType("http://a.html?a=ji")); |
||||
Assert.assertEquals("text/html", MimeType.parseMimeType("http://a.xml?a=ji")); |
||||
Assert.assertEquals("image/jpeg", MimeType.parseMimeType("http://a.jpg?a=ji")); |
||||
Assert.assertEquals("image/jpeg", MimeType.parseMimeType("http://a.jpeg?a=ji")); |
||||
} |
||||
} |