diff --git a/build.gradle b/build.gradle index e0bf79aefe..f9c687731c 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ ext { outputPath = "build" ignoreTestFailureSetting = true languageLevelSetting = 1.8 + jxBrowserVersion = '7.26' } applyGlobalConfigPathIfExist() @@ -66,8 +67,8 @@ allprojects { implementation 'com.fr.cbb:fine-universal-skeleton:' + cbbVersion implementation 'com.install4j:install4j-runtime:8.0.4' implementation 'com.fr.third:jxbrowser:6.23' - implementation 'com.fr.third:jxbrowser-v7:7.22' - implementation 'com.fr.third:jxbrowser-swing-v7:7.22' + implementation "com.fr.third:jxbrowser-v7:${jxBrowserVersion}" + implementation "com.fr.third:jxbrowser-swing-v7:${jxBrowserVersion}" implementation 'com.fr.third.server:servlet-api:3.0' implementation 'org.swingexplorer:swexpl:2.0.1' implementation 'org.swingexplorer:swag:1.0' @@ -89,18 +90,19 @@ allprojects { testImplementation 'org.easymock:easymock:3.5.1' testImplementation 'org.powermock:powermock-module-junit4:1.7.1' testImplementation 'org.powermock:powermock-api-easymock:1.7.1' + testImplementation 'org.powermock:powermock-api-mockito2:1.7.4' testImplementation 'junit:junit:4.12' } if (OperatingSystem.current().isMacOsX()) { dependencies { implementation 'com.fr.third:jxbrowser-mac:6.23' - implementation 'com.fr.third:jxbrowser-mac-v7:7.22' + implementation "com.fr.third:jxbrowser-mac-v7:${jxBrowserVersion}" } } else if (OperatingSystem.current().isWindows()) { dependencies { implementation 'com.fr.third:jxbrowser-win64:6.23' - implementation 'com.fr.third:jxbrowser-win64-v7:7.22' + implementation "com.fr.third:jxbrowser-win64-v7:${jxBrowserVersion}" } } } diff --git a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java index 16c1a7e8a4..1f3055d40a 100644 --- a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java +++ b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java @@ -1,6 +1,7 @@ package com.fr.design; import com.fr.common.report.ReportState; +import com.fr.design.env.processor.RemoteDesignerWorkspaceInfoProcessor; import com.fr.design.mainframe.manager.clip.TemplateTreeClipboard; import com.fr.design.plugin.remind.PluginErrorDesignReminder; import com.fr.design.data.DesignTableDataManager; @@ -101,6 +102,29 @@ public class EnvChangeEntrance { } } + /** + * 插件进行用户名转换 + * + * @param workspaceInfo 环境信息 + */ + private DesignerWorkspaceInfo customUserName(DesignerWorkspaceInfo workspaceInfo) { + //本地环境直接返回 + if (workspaceInfo == null || workspaceInfo.getType() == DesignerWorkspaceType.Local) { + return workspaceInfo; + } + RemoteDesignerWorkspaceInfoProcessor processor = ExtraDesignClassManager.getInstance().getSingle(RemoteDesignerWorkspaceInfoProcessor.XML_TAG); + if (processor == null) { + return workspaceInfo; + } + try { + WorkspaceConnectionInfo workspaceConnectionInfo = processor.customUserName(workspaceInfo.getConnection()); + return (RemoteDesignerWorkspaceInfo) ((RemoteDesignerWorkspaceInfo) workspaceInfo).cloneWithConnectionInfo(workspaceConnectionInfo); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return workspaceInfo; + } + } + /** * 切换到新环境 * @@ -109,7 +133,7 @@ public class EnvChangeEntrance { */ private boolean switch2Env(final String envName, PopTipStrategy strategy) { DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); - DesignerWorkspaceInfo selectedEnv = envManager.getWorkspaceInfo(envName); + DesignerWorkspaceInfo selectedEnv = customUserName(envManager.getWorkspaceInfo(envName)); DesignerWorkspaceInfoContext.setWorkspaceInfo(selectedEnv); WorkspaceConnectionInfo connectionInfo = selectedEnv.getConnection(); diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index 46485409bb..4d31904f1f 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -36,6 +36,7 @@ import com.fr.design.layout.TableLayoutHelper; import com.fr.design.layout.VerticalFlowLayout; import com.fr.design.mainframe.vcs.VcsConfigManager; import com.fr.design.mainframe.vcs.common.VcsHelper; +import com.fr.design.mainframe.vcs.ui.VcsMovePanel; import com.fr.design.os.impl.SupportOSImpl; import com.fr.design.unit.UnitConvertUtil; import com.fr.design.utils.gui.GUICoreUtils; @@ -54,9 +55,12 @@ import com.fr.stable.os.OperatingSystem; import com.fr.third.apache.logging.log4j.Level; import com.fr.transaction.Configurations; import com.fr.transaction.Worker; +import com.fr.transaction.WorkerAdaptor; import com.fr.workspace.WorkContext; +import com.fr.workspace.server.vcs.VcsConfig; import com.fr.workspace.server.vcs.VcsOperator; import com.fr.workspace.server.vcs.git.config.GcConfig; +import com.fr.workspace.server.vcs.v2.scheduler.VcsAutoCleanOperator; import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; @@ -77,14 +81,7 @@ import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dialog; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Window; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; @@ -149,6 +146,32 @@ public class PreferencePane extends BasicPane { private static final Level[] LOG = {Level.FATAL, Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG}; + private static final int ONE_DAY_INT = 1; + private static final int ONE_WEEK_INT = 7; + private static final int ONE_MONTH_INT = 30; + private static final int THREE_MONTH_INT = 90; + private static final int SIX_MONTH_INT = 180; + + private static final int ONE_DAY_INDEX = 0; + private static final int ONE_WEEK_INDEX = 1; + private static final int ONE_MONTH_INDEX = 2; + private static final int THREE_MONTH_INDEX = 3; + private static final int SIX_MONTH_INDEX = 4; + private static final String ONE_DAY = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_ONE_DAY"); + private static final String ONE_WEEK = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_ONE_WEEK"); + private static final String ONE_MONTH = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_ONE_MONTH"); + private static final String THREE_MONTH = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_THREE_MONTH"); + private static final String SIX_MONTH = Toolkit.i18nText("Fine-Design_Vcs_Auto_Clean_SIX_MONTH"); + private static final String[] INTERVAL = { + ONE_DAY, + ONE_WEEK, + ONE_MONTH, + THREE_MONTH, + SIX_MONTH + }; + + private static final int DEFAULT_INDEX = 3; + private boolean languageChanged; // 是否修改了设计器语言设置 //设置是否支持undo private UICheckBox supportUndoCheckBox; @@ -186,8 +209,20 @@ public class PreferencePane extends BasicPane { private UICheckBox cloudAnalyticsDelayCheckBox; private UICheckBox vcsEnableCheckBox; + private UICheckBox useVcsAutoSaveScheduleCheckBox; + + private UICheckBox useVcsAutoCleanScheduleCheckBox; + + private UIComboBox autoCleanIntervalComboBox; + private UIComboBox autoCleanRetainIntervalComboBox; + + private IntegerEditor autoSaveIntervalEditor; private UICheckBox saveCommitCheckBox; private UICheckBox useIntervalCheckBox; + private VcsMovePanel movePanel; + + private JPanel saveIntervalPane; + private JPanel autoCleanPane; private UICheckBox startupPageEnabledCheckBox; private IntegerEditor saveIntervalEditor; private UICheckBox gcEnableCheckBox; @@ -227,13 +262,26 @@ public class PreferencePane extends BasicPane { JPanel advancePane = FRGUIPaneFactory.createY_AXISBoxInnerContainer_L_Pane(); UIScrollPane adviceScrollPane = patchScroll(advancePane); jtabPane.addTab(i18nText("Fine-Design_Basic_Advanced"), adviceScrollPane); + //初始化vcs总面板 + JPanel vcsParentPane = new JPanel(); + CardLayout cardLayout = new CardLayout(); + vcsParentPane.setLayout(cardLayout); + //vcs配置面板 + JPanel vcsPane = new JPanel(new BorderLayout()); + //添加滚动条 + UIScrollPane vcsScrollPane = patchScroll(vcsPane); + //配置面板作为vcs总面板的一张卡片 + vcsParentPane.add(vcsScrollPane, VcsMovePanel.SETTING); + jtabPane.addTab(i18nText("Fine-Design_Vcs_Title"), vcsParentPane); contentPane.add(jtabPane, BorderLayout.NORTH); createFunctionPane(generalPane); createEditPane(generalPane); createColorSettingPane(generalPane); - createVcsSettingPane(generalPane); + + // vcsPane + createVcsSettingPane(vcsPane, vcsParentPane, cardLayout); // ConfPane JPanel confLocationPane = FRGUIPaneFactory.createX_AXISBoxInnerContainer_S_Pane(); @@ -345,21 +393,31 @@ public class PreferencePane extends BasicPane { return generalPanelWithScroll; } - private void createVcsSettingPane(JPanel generalPane) { - JPanel vcsPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(i18nText("Fine-Design_Vcs_Title")); - generalPane.add(vcsPane); + private void createVcsSettingPane(JPanel generalPane,JPanel parentPane, CardLayout cardLayout) { + //迁移面板 + movePanel = createMovePane(cardLayout, parentPane); + generalPane.add(movePanel, BorderLayout.NORTH); + JPanel savePane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(i18nText("Fine-Design_Vcs_Save_Setting")); + JPanel vcsPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(i18nText("Fine-Design_Vcs_Clean_Setting")); + JPanel containPane = new JPanel(new GridLayout(10,1,0,8)); + containPane.add(savePane); + containPane.add(vcsPane); + generalPane.add(containPane, BorderLayout.CENTER); remindVcsLabel = new UILabel(i18nText("Fine-Design_Vcs_Remind")); remindVcsLabel.setVisible(!VcsHelper.getInstance().needInit()); vcsEnableCheckBox = new UICheckBox(i18nText("Fine-Design_Vcs_SaveAuto")); + + saveIntervalPane = createSaveIntervalPane(); saveCommitCheckBox = new UICheckBox(i18nText("Fine-Design_Vcs_No_Delete")); saveIntervalEditor = new IntegerEditor(60); useIntervalCheckBox = new UICheckBox(); + savePane.add(vcsEnableCheckBox); + savePane.add(saveIntervalPane); //gc面板 JPanel gcControlPane = createGcControlPane(); JPanel enableVcsPanel = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); - enableVcsPanel.add(vcsEnableCheckBox); enableVcsPanel.add(remindVcsLabel); JPanel intervalPanel = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); final UILabel everyLabel = new UILabel(i18nText("Fine-Design_Vcs_Every")); @@ -368,6 +426,7 @@ public class PreferencePane extends BasicPane { intervalPanel.add(everyLabel); intervalPanel.add(saveIntervalEditor); intervalPanel.add(delayLabel); + autoCleanPane = createAutoCleanPane(); vcsEnableCheckBox.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { @@ -390,9 +449,58 @@ public class PreferencePane extends BasicPane { vcsPane.add(enableVcsPanel); vcsPane.add(intervalPanel); vcsPane.add(saveCommitCheckBox); - vcsPane.add(gcControlPane); + vcsPane.add(autoCleanPane); + saveIntervalPane.setVisible(!VcsHelper.getInstance().isLegacyMode()); + autoCleanPane.setVisible(!VcsHelper.getInstance().isLegacyMode()); + if (VcsHelper.getInstance().isLegacyMode()) { + // 老版本时才显示gc选项 + vcsPane.add(gcControlPane); + } } + private VcsMovePanel createMovePane(CardLayout cardLayout, JPanel parentPane) { + return new VcsMovePanel(cardLayout, parentPane, new VcsMovePanel.MoveCallBack(){ + @Override + public void doCallBack(boolean visible) { + saveIntervalPane.setVisible(visible); + autoCleanPane.setVisible(visible); + } + }); + }; + + private JPanel createAutoCleanPane() { + JPanel autoCleanPane = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); + useVcsAutoCleanScheduleCheckBox = new UICheckBox(); + autoCleanIntervalComboBox = new UIComboBox(INTERVAL); + autoCleanIntervalComboBox.setSelectedIndex(DEFAULT_INDEX); + autoCleanRetainIntervalComboBox = new UIComboBox(INTERVAL); + autoCleanRetainIntervalComboBox.setSelectedIndex(DEFAULT_INDEX); + autoCleanPane.add(useVcsAutoCleanScheduleCheckBox); + autoCleanPane.add(new UILabel(i18nText("Fine-Design_Vcs_Auto_Clean_Every"))); + autoCleanPane.add(autoCleanIntervalComboBox); + autoCleanPane.add(new UILabel(i18nText("Fine-Design_Vcs_Auto_Clean_Content"))); + autoCleanPane.add(autoCleanRetainIntervalComboBox); + autoCleanPane.add(new UILabel(i18nText("Fine-Design_Vcs_Auto_Clean_Last"))); + useVcsAutoCleanScheduleCheckBox.setEnabled(!VcsHelper.getInstance().isLegacyMode()); + autoCleanPane.setVisible(false); + return autoCleanPane; + } + + private JPanel createSaveIntervalPane() { + JPanel saveIntervalPane = new JPanel(FRGUIPaneFactory.createLeftZeroLayout()); + useVcsAutoSaveScheduleCheckBox = new UICheckBox(); + autoSaveIntervalEditor = new IntegerEditor(60); + saveIntervalPane.add(useVcsAutoSaveScheduleCheckBox); + saveIntervalPane.add(new UILabel(i18nText("Fine-Design_Vcs_Every"))); + saveIntervalPane.add(autoSaveIntervalEditor); + saveIntervalPane.add(new UILabel(i18nText("Fine-Design_Vcs_Save_Delay"))); + useVcsAutoSaveScheduleCheckBox.setEnabled(!VcsHelper.getInstance().isLegacyMode()); + saveIntervalPane.setVisible(false); + return saveIntervalPane; + } + + + /** * 模创建板版本gc 配置操作面板 * @@ -796,6 +904,10 @@ public class PreferencePane extends BasicPane { gcEnableCheckBox.setSelected(GcConfig.getInstance().isGcEnable()); gcButton.setEnabled(gcEnableCheckBox.isSelected()); + useVcsAutoSaveScheduleCheckBox.setSelected(vcsConfigManager.isUseAutoSave()); + useVcsAutoCleanScheduleCheckBox.setSelected(VcsConfig.getInstance().isUseV2AutoClean()); + autoSaveIntervalEditor.setValue(vcsConfigManager.getAutoSaveInterval()); + gridLineColorTBButton.setColor(designerEnvManager.getGridLineColor()); paginationLineColorTBButton.setColor(designerEnvManager.getPaginationLineColor()); @@ -875,6 +987,21 @@ public class PreferencePane extends BasicPane { } } + private int getDay(int dateIndex) { + switch (dateIndex) { + case ONE_DAY_INDEX: + return ONE_DAY_INT; + case ONE_WEEK_INDEX: + return ONE_WEEK_INT; + case ONE_MONTH_INDEX: + return ONE_MONTH_INT; + case SIX_MONTH_INDEX: + return SIX_MONTH_INT; + default: + return THREE_MONTH_INT; + } + } + /** * The method of update. */ @@ -920,6 +1047,17 @@ public class PreferencePane extends BasicPane { vcsConfigManager.setVcsEnable(this.vcsEnableCheckBox.isSelected()); vcsConfigManager.setSaveCommit(this.saveCommitCheckBox.isSelected()); vcsConfigManager.setUseInterval(this.useIntervalCheckBox.isSelected()); + vcsConfigManager.setUseAutoSave(this.useVcsAutoSaveScheduleCheckBox.isSelected()); + vcsConfigManager.setAutoSaveInterval(this.autoSaveIntervalEditor.getValue()); + Configurations.update(new WorkerAdaptor(VcsConfig.class) { + @Override + public void run() { + VcsConfig.getInstance().setUseV2AutoClean(useVcsAutoCleanScheduleCheckBox.isSelected()); + VcsConfig.getInstance().setV2CleanInterval(getDay(autoCleanIntervalComboBox.getSelectedIndex())); + VcsConfig.getInstance().setV2RetainInterval(getDay(autoCleanRetainIntervalComboBox.getSelectedIndex())); + } + }); + dealWithSchedule(); designerEnvManager.setStartupPageEnabled(this.startupPageEnabledCheckBox.isSelected()); Configurations.update(new Worker() { @Override @@ -994,6 +1132,31 @@ public class PreferencePane extends BasicPane { } + private void dealWithSchedule() { + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + boolean v2FunctionSupport = VcsHelper.getInstance().checkV2FunctionSupport(); + if (v2FunctionSupport) { + //如果支持V2 + if (useVcsAutoSaveScheduleCheckBox.isSelected()) { + FineLoggerFactory.getLogger().info("[VcsV2] start auto save!"); + VcsHelper.getInstance().startAutoSave(autoSaveIntervalEditor.getValue()); + } else { + VcsHelper.getInstance().stopAutoSave(); + } + if (useVcsAutoCleanScheduleCheckBox.isSelected()) { + FineLoggerFactory.getLogger().info("[VcsV2] start auto clean!"); + WorkContext.getCurrent().get(VcsAutoCleanOperator.class).addOrUpdateVcsAutoCleanJob(getDay(autoCleanIntervalComboBox.getSelectedIndex())); + } else { + WorkContext.getCurrent().get(VcsAutoCleanOperator.class).stopVcsAutoCleanJob(); + } + } + return null; + } + }.execute(); + } + // 如果语言设置改变了,则显示重启对话框 public void showRestartDialog() { if (!languageChanged) { diff --git a/designer-base/src/main/java/com/fr/design/actions/help/FineUIAction.java b/designer-base/src/main/java/com/fr/design/actions/help/FineUIAction.java index 0839c14767..3037322275 100644 --- a/designer-base/src/main/java/com/fr/design/actions/help/FineUIAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/help/FineUIAction.java @@ -3,14 +3,15 @@ package com.fr.design.actions.help; import com.fr.design.actions.UpdateAction; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.mainframe.DesignerContext; -import com.fr.design.ui.ModernUIPane; import com.fr.locale.InterProviderFactory; import com.fr.web.struct.AssembleComponent; import com.fr.web.struct.Atom; import com.fr.web.struct.browser.RequestClient; import com.fr.web.struct.category.ScriptPath; import com.fr.web.struct.impl.FineUI; +import com.teamdev.jxbrowser.js.JsAccessible; import java.awt.event.ActionEvent; @@ -27,14 +28,8 @@ public class FineUIAction extends UpdateAction { @Override public void actionPerformed(final ActionEvent e) { - ModernUIPane pane = new ModernUIPane.Builder<>() -// .prepare(new ScriptContextAdapter() { -// @Override -// public void onScriptContextCreated(ScriptContextEvent event) { -// JSValue pool = event.getBrowser().executeJavaScriptAndReturnValue("window.Pool"); -// pool.asObject().setProperty("i18n", new I18n()); -// } -// }) + JxUIPane pane = new JxUIPane.Builder<>() + .bindNamespace("i18n", new I18n()) .withComponent(new AssembleComponent() { @Override @@ -44,20 +39,21 @@ public class FineUIAction extends UpdateAction { @Override public Atom[] refer() { - return new Atom[] {FineUI.KEY}; + return new Atom[]{FineUI.KEY}; } }) .build(); - BasicDialog dialog = pane.showLargeWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + BasicDialog dialog = pane.showLargeWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { @Override public void doOk() { // Do nothing } }); - dialog.setVisible(true); + dialog.setVisible(true); } + @JsAccessible public static class I18n { public String i18nText(String key) { diff --git a/designer-base/src/main/java/com/fr/design/actions/help/SystemInfoPane.java b/designer-base/src/main/java/com/fr/design/actions/help/SystemInfoPane.java index 7fe26054f6..db5c21e692 100644 --- a/designer-base/src/main/java/com/fr/design/actions/help/SystemInfoPane.java +++ b/designer-base/src/main/java/com/fr/design/actions/help/SystemInfoPane.java @@ -41,16 +41,12 @@ public class SystemInfoPane extends JPanel { for (int i = 0; i < keys.length; i++) { Object[] tableRowData = new Object[2]; String keyValue = keys[i].toString(); - // james:屏蔽掉exe4j的内容 - if (keyValue.indexOf("exe4j") != -1) { - continue; - } - // james:这个也是exe4j的东东 - if ("install4j.exeDir".equals(keyValue)) { + + if (needToShield(keyValue)) { continue; } - if(keyValue.indexOf("FineReport") != -1){ + if(keyValue.contains("FineReport")){ keys[i] = keyValue.replaceAll("FineReport", ProductConstants.APP_NAME); } @@ -66,4 +62,14 @@ public class SystemInfoPane extends JPanel { add(new JScrollPane(table), BorderLayout.CENTER); } + + /** + * 是否属于需要屏蔽的内容(当前屏蔽掉exe4j与jxbrowser的内容) + * + * @param keyValue 对应的key值 + * @return 需要屏蔽则返回true + */ + private boolean needToShield(String keyValue) { + return keyValue.contains("exe4j") || keyValue.contains("jxbrowser") || "install4j.exeDir".equals(keyValue); + } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/actions/help/alphafine/RemindPane.java b/designer-base/src/main/java/com/fr/design/actions/help/alphafine/RemindPane.java index 1d8d99266e..aeab19aca3 100644 --- a/designer-base/src/main/java/com/fr/design/actions/help/alphafine/RemindPane.java +++ b/designer-base/src/main/java/com/fr/design/actions/help/alphafine/RemindPane.java @@ -4,10 +4,21 @@ import com.fr.design.dialog.UIDialog; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.general.IOUtils; - - -import javax.swing.*; -import java.awt.*; +import com.fr.general.locale.image.I18nImage; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.LayoutManager; +import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -23,8 +34,10 @@ public class RemindPane extends JPanel { private Icon checkIcon = IOUtils.readIcon("/com/fr/design/mainframe/alphafine/images/check.png"); private Icon unCheckIcon = IOUtils.readIcon("/com/fr/design/mainframe/alphafine/images/uncheck.png"); private Icon closeIcon = IOUtils.readIcon("/com/fr/design/mainframe/alphafine/images/remind_close.png"); - private Icon labelIcon = IOUtils.readIcon("/com/fr/design/mainframe/alphafine/images/remind.png"); - private Icon openIcon = IOUtils.readIcon("com/fr/design/mainframe/alphafine/images/open.png"); + private static final String REMIND_IMAGE_URL = "/com/fr/design/mainframe/alphafine/images/open/open.png"; + private Icon labelIcon = new ImageIcon(I18nImage.getImage(REMIND_IMAGE_URL)); + private static final String OPEN_IMAGE_URL = "/com/fr/design/mainframe/alphafine/images/open/open.png"; + private final Icon openIcon = new ImageIcon(I18nImage.getImage(OPEN_IMAGE_URL)); private static final int WIDTH = 600; private static final int HEIGHT = 400; diff --git a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java index 317a171954..b2e8ceb35a 100644 --- a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java +++ b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java @@ -161,6 +161,20 @@ public class TablePanel extends JPanel { } cellPanel.add(component); } + + /** + * 为单元格Panel更新tooltip + * + * @param row 行数 + * @param column 列数 + * @param value tooltip值 + */ + public void updateCellToolTip(int row, int column, String value) { + int x = row - 1; + int y = column - 1; + JPanel cellPanel = this.cellPanels[x][y]; + cellPanel.setToolTipText(value); + } public void updateCell(int row, int column, String value) { diff --git a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java index 712dcbd33b..2354007e18 100644 --- a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java +++ b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java @@ -572,7 +572,7 @@ public abstract class DesignTableDataManager { parameter.setValue(parameterMap.get(parameter.getName())); } } - parameterMap.put(SqlNoteConstants.SQL_NOTE_TEMPLATE, HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().getEditingFILE().getPath()); + addTemplateInfoIfNeed(parameterMap); return DataOperator.getInstance().previewTableData(TableDataSourceTailor.extractTableData(tableDataSource), tabledata, parameterMap, rowCount); } catch (Exception e) { throw new TableDataException(e.getMessage(), e); @@ -592,6 +592,12 @@ public abstract class DesignTableDataManager { } } + private static void addTemplateInfoIfNeed(Map parameterMap) { + if (JTemplate.isValid(HistoryTemplateListCache.getInstance().getCurrentEditingTemplate())) { + parameterMap.put(SqlNoteConstants.SQL_NOTE_TEMPLATE, HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().getEditingFILE().getPath()); + } + } + private static boolean needInputParams(boolean mustInputParameters, ParameterProvider[] parameters) { if (mustInputParameters && ArrayUtils.isNotEmpty(parameters)) { return true; @@ -669,7 +675,7 @@ public abstract class DesignTableDataManager { if (needLoadingBar) { MultiResultTableDataWrapper.loadingBar.start(); } - parameterMap.put(SqlNoteConstants.SQL_NOTE_TEMPLATE, HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().getEditingFILE().getPath()); + addTemplateInfoIfNeed(parameterMap); return DataOperator.getInstance().previewMultiResultTableData(tableData, parameterMap, 0); } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/DatabaseConnectionPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/DatabaseConnectionPane.java index 0a00f37041..3c5c2b82a8 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/DatabaseConnectionPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/DatabaseConnectionPane.java @@ -589,7 +589,7 @@ public abstract class DatabaseConnectionPane imp "com.microsoft.sqlserver.jdbc.SQLServerDriver", "com.ibm.db2.jcc.DB2Driver", "com.mysql.jdbc.Driver", - "org.gjt.mm.mysql.Driver" + "org.gjt.mm.mysql.Driver", + "dm.jdbc.driver.DmDriver" }; // 需要隐藏面板的数据库的驱动 private static final String PREVIEW_BUTTON = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Preview"); private ConnectionTableProcedurePane connectionTableProcedurePane; diff --git a/designer-base/src/main/java/com/fr/design/dcm/UniversalDatabasePane.java b/designer-base/src/main/java/com/fr/design/dcm/UniversalDatabasePane.java index e29a9f6698..f195acaca1 100644 --- a/designer-base/src/main/java/com/fr/design/dcm/UniversalDatabasePane.java +++ b/designer-base/src/main/java/com/fr/design/dcm/UniversalDatabasePane.java @@ -1,12 +1,9 @@ package com.fr.design.dcm; import com.fr.design.dialog.BasicPane; -import com.fr.design.ui.ModernUIPane; -import com.teamdev.jxbrowser.chromium.JSValue; -import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; -import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; +import com.fr.design.jxbrowser.JxUIPane; -import java.awt.*; +import java.awt.BorderLayout; /** * @author richie @@ -15,8 +12,6 @@ import java.awt.*; */ public class UniversalDatabasePane extends BasicPane { - private ModernUIPane modernUIPane; - @Override protected String title4PopupWindow() { return "Database"; @@ -24,15 +19,9 @@ public class UniversalDatabasePane extends BasicPane { public UniversalDatabasePane() { setLayout(new BorderLayout()); - modernUIPane = new ModernUIPane.Builder<>() + JxUIPane modernUIPane = new JxUIPane.Builder<>() .withComponent(UniversalDatabaseComponent.KEY) - .prepare(new ScriptContextAdapter() { - @Override - public void onScriptContextCreated(ScriptContextEvent event) { - JSValue window = event.getBrowser().executeJavaScriptAndReturnValue("window"); - window.asObject().setProperty("DcmHelper", UniversalDcmBridge.getBridge(event.getBrowser())); - } - }) + .bindWindow("DcmHelper", UniversalDcmBridge::getBridge) .build(); add(modernUIPane, BorderLayout.CENTER); } diff --git a/designer-base/src/main/java/com/fr/design/dcm/UniversalDcmBridge.java b/designer-base/src/main/java/com/fr/design/dcm/UniversalDcmBridge.java index 577acada97..3d569ea2c9 100644 --- a/designer-base/src/main/java/com/fr/design/dcm/UniversalDcmBridge.java +++ b/designer-base/src/main/java/com/fr/design/dcm/UniversalDcmBridge.java @@ -2,8 +2,8 @@ package com.fr.design.dcm; import com.fr.decision.webservice.bean.BaseBean; import com.fr.design.bridge.exec.JSBridge; -import com.teamdev.jxbrowser.chromium.Browser; -import com.teamdev.jxbrowser.chromium.JSObject; +import com.teamdev.jxbrowser.js.JsAccessible; +import com.teamdev.jxbrowser.js.JsObject; /** * @author richie @@ -11,20 +11,28 @@ import com.teamdev.jxbrowser.chromium.JSObject; * Created by richie on 2019-05-17 * 桥接Java和JavaScript的类 */ +@JsAccessible public class UniversalDcmBridge { - public static UniversalDcmBridge getBridge(Browser browser) { - return new UniversalDcmBridge(browser); + /** + * 获取 js-java bridge + * + * @param window 全局环境 + * @return bridge + */ + public static UniversalDcmBridge getBridge(JsObject window) { + return new UniversalDcmBridge(window); } - private JSObject window; + private JsObject window; - private UniversalDcmBridge(Browser browser) { - this.window = browser.executeJavaScriptAndReturnValue("window").asObject(); + private UniversalDcmBridge(JsObject window) { + this.window = window; } /** * 获取所有的数据连接 + * * @return 数据连接集合 */ @JSBridge diff --git a/designer-base/src/main/java/com/fr/design/env/RemoteDesignerWorkspaceInfo.java b/designer-base/src/main/java/com/fr/design/env/RemoteDesignerWorkspaceInfo.java index 920a13dbfb..39abe75cfb 100644 --- a/designer-base/src/main/java/com/fr/design/env/RemoteDesignerWorkspaceInfo.java +++ b/designer-base/src/main/java/com/fr/design/env/RemoteDesignerWorkspaceInfo.java @@ -6,6 +6,7 @@ import com.fr.log.FineLoggerFactory; import com.fr.security.SecurityToolbox; import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; +import com.fr.stable.fun.mark.Immutable; import com.fr.stable.project.ProjectConstants; import com.fr.stable.xml.XMLPrintWriter; import com.fr.stable.xml.XMLableReader; @@ -166,6 +167,15 @@ public class RemoteDesignerWorkspaceInfo implements DesignerWorkspaceInfo { return object; } + /** + * clone一个自定义连接信息的RemoteDesignerWorkspaceInfo + */ + public Object cloneWithConnectionInfo(WorkspaceConnectionInfo workspaceConnectionInfo) throws CloneNotSupportedException { + RemoteDesignerWorkspaceInfo object = (RemoteDesignerWorkspaceInfo) super.clone(); + object.connection = workspaceConnectionInfo; + return object; + } + @Override public boolean checkValid() throws Exception { diff --git a/designer-base/src/main/java/com/fr/design/env/processor/AbstractRemoteDesignerWorkspaceInfoProcessor.java b/designer-base/src/main/java/com/fr/design/env/processor/AbstractRemoteDesignerWorkspaceInfoProcessor.java new file mode 100644 index 0000000000..6917f1745f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/env/processor/AbstractRemoteDesignerWorkspaceInfoProcessor.java @@ -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; + } +} diff --git a/designer-base/src/main/java/com/fr/design/env/processor/RemoteDesignerWorkspaceInfoProcessor.java b/designer-base/src/main/java/com/fr/design/env/processor/RemoteDesignerWorkspaceInfoProcessor.java new file mode 100644 index 0000000000..b74d21159f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/env/processor/RemoteDesignerWorkspaceInfoProcessor.java @@ -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); + +} diff --git a/designer-base/src/main/java/com/fr/design/extra/PluginBatchModifyDetailPane.java b/designer-base/src/main/java/com/fr/design/extra/PluginBatchModifyDetailPane.java new file mode 100644 index 0000000000..3c086b7ea8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/extra/PluginBatchModifyDetailPane.java @@ -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); + } +} diff --git a/designer-base/src/main/java/com/fr/design/extra/PluginOperateUtils.java b/designer-base/src/main/java/com/fr/design/extra/PluginOperateUtils.java index 379daab28e..8baccba847 100644 --- a/designer-base/src/main/java/com/fr/design/extra/PluginOperateUtils.java +++ b/designer-base/src/main/java/com/fr/design/extra/PluginOperateUtils.java @@ -3,6 +3,8 @@ package com.fr.design.extra; import com.fr.design.DesignerEnvManager; import com.fr.design.bridge.exec.JSCallback; import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.extra.exe.callback.BatchModifyStatusCallback; +import com.fr.design.extra.exe.callback.BatchUpdateOnlineCallback; import com.fr.design.extra.exe.callback.InstallFromDiskCallback; import com.fr.design.extra.exe.callback.InstallOnlineCallback; import com.fr.design.extra.exe.callback.ModifyStatusCallback; @@ -28,11 +30,13 @@ import com.fr.plugin.view.PluginView; import com.fr.plugin.xml.PluginElementName; import com.fr.plugin.xml.PluginXmlElement; import com.fr.stable.StringUtils; +import com.teamdev.jxbrowser.chromium.JSArray; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import java.io.File; import java.net.HttpURLConnection; +import java.util.ArrayList; import java.util.List; @@ -65,12 +69,14 @@ public class PluginOperateUtils { public static void updatePluginOnline(List pluginMarkerList, JSCallback jsCallback) { - for (int i = 0; i < pluginMarkerList.size(); i++) { - updatePluginOnline(pluginMarkerList.get(i), jsCallback); + int size = pluginMarkerList.size(); + BatchUpdateOnlineCallback batchUpdateOnlineCallback = new BatchUpdateOnlineCallback(jsCallback, size); + for (int i = 0; i < size; i++) { + updatePluginOnline(pluginMarkerList.get(i), jsCallback, batchUpdateOnlineCallback); } } - public static void updatePluginOnline(PluginMarker pluginMarker, JSCallback jsCallback) { + private static void updatePluginOnline(PluginMarker pluginMarker, JSCallback jsCallback, BatchUpdateOnlineCallback batchUpdateOnlineCallback) { try { JSONObject latestPluginInfo = PluginUtils.getLatestPluginInfo(pluginMarker.getPluginID()); String latestPluginVersion = latestPluginInfo.getString("version"); @@ -79,11 +85,25 @@ public class PluginOperateUtils { //当前已经安装的相同ID插件marker PluginMarker currentMarker = PluginMarkerAdapter.create(PluginUtils.getInstalledPluginMarkerByID(pluginMarker.getPluginID()), pluginName); PluginTask pluginTask = PluginTask.updateTask(currentMarker, toPluginMarker); - PluginControllerHelper.updateOnline(currentMarker, toPluginMarker, new UpdateOnlineCallback(pluginTask, jsCallback), PluginExtraInfo.newBuilder().username(DesignerEnvManager.getEnvManager().getDesignerLoginUsername()).build()); + if (!batchUpdateOnlineCallback.equals(BatchUpdateOnlineCallback.NONE)) { + batchUpdateOnlineCallback.createInnerPreTaskCallback(pluginTask, jsCallback); + PluginControllerHelper.updateOnline(currentMarker, toPluginMarker, batchUpdateOnlineCallback.getInnerPreTaskCallback(), PluginExtraInfo.newBuilder().username(DesignerEnvManager.getEnvManager().getDesignerLoginUsername()).build()); + } else { + PluginControllerHelper.updateOnline(currentMarker, toPluginMarker, new UpdateOnlineCallback(pluginTask, jsCallback), PluginExtraInfo.newBuilder().username(DesignerEnvManager.getEnvManager().getDesignerLoginUsername()).build()); + } } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } + } + /** + * 更新插件 + * + * @param pluginMarker 插件marker + * @param jsCallback 回调 + */ + public static void updatePluginOnline(PluginMarker pluginMarker, JSCallback jsCallback) { + updatePluginOnline(pluginMarker, jsCallback, BatchUpdateOnlineCallback.NONE); } @@ -92,6 +112,60 @@ public class PluginOperateUtils { } + /** + * 批量启用或禁用插件 + * + * @param pluginIDs 要处理的插件信息 + * @param jsCallback 回调函数 + */ + public static void setPluginActive(JSArray pluginIDs, JSCallback jsCallback) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + int len = pluginIDs.length(); + BatchModifyStatusCallback modifyStatusCallback = new BatchModifyStatusCallback(jsCallback, len); + for (int i = 0; i < len; i++) { + String pluginInfo = pluginIDs.get(i).asString().getValue(); + PluginMarker pluginMarker = PluginUtils.createPluginMarker(pluginInfo); + dealWithPluginActive(pluginMarker, modifyStatusCallback); + } + } + }); + } + + private static void dealWithPluginActive(PluginMarker pluginMarker, BatchModifyStatusCallback modifyStatusCallback) { + PluginContext plugin = PluginManager.getContext(pluginMarker); + boolean running = plugin.isRunning(); + modifyStatusCallback.updateActiveStatus(running); + changePluginActive(running, pluginMarker, modifyStatusCallback, plugin); + + } + + private static void changePluginActive(boolean isRunning, PluginMarker pluginMarker, PluginTaskCallback modifyStatusCallback, PluginContext plugin) { + if (isRunning) { + PluginXmlElement forbidReminder = plugin.getXml().getElement(PluginElementName.ForbidReminder); + if (forbidReminder != null && forbidReminder.getContent() != null) { + // 禁用前提示 + int rv = FineJOptionPane.showConfirmDialog( + null, + forbidReminder.getContent(), + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE + ); + if (rv == JOptionPane.OK_OPTION) { + PluginManager.getController().forbidPersistently(pluginMarker, modifyStatusCallback); + } + } else { + // 正常禁用 + PluginManager.getController().forbidPersistently(pluginMarker, modifyStatusCallback); + } + } else { + PluginManager.getController().enablePersistently(pluginMarker, modifyStatusCallback); + } + } + public static void setPluginActive(String pluginInfo, JSCallback jsCallback) { SwingUtilities.invokeLater(new Runnable() { @@ -101,27 +175,7 @@ public class PluginOperateUtils { PluginContext plugin = PluginManager.getContext(pluginMarker); boolean isRunning = plugin.isRunning(); PluginTaskCallback modifyStatusCallback = new ModifyStatusCallback(isRunning, jsCallback); - if (isRunning) { - PluginXmlElement forbidReminder = plugin.getXml().getElement(PluginElementName.ForbidReminder); - if (forbidReminder != null && forbidReminder.getContent() != null) { - // 禁用前提示 - int rv = FineJOptionPane.showConfirmDialog( - null, - forbidReminder.getContent(), - com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE - ); - if (rv == JOptionPane.OK_OPTION) { - PluginManager.getController().forbidPersistently(pluginMarker, modifyStatusCallback); - } - } else { - // 正常禁用 - PluginManager.getController().forbidPersistently(pluginMarker, modifyStatusCallback); - } - } else { - PluginManager.getController().enablePersistently(pluginMarker, modifyStatusCallback); - } + changePluginActive(isRunning, pluginMarker, modifyStatusCallback, plugin); } }); } @@ -255,8 +309,7 @@ public class PluginOperateUtils { private static String getPluginName(PluginContext pluginContext, PluginMarker pluginMarker) { if (pluginContext != null) { return pluginContext.getName(); - } - else if (pluginMarker instanceof PluginMarkerAdapter) { + } else if (pluginMarker instanceof PluginMarkerAdapter) { return ((PluginMarkerAdapter) pluginMarker).getPluginName(); } return pluginMarker == null ? StringUtils.EMPTY : pluginMarker.getPluginID(); diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/AbstractBatchModifyStatusCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/AbstractBatchModifyStatusCallback.java new file mode 100644 index 0000000000..1b3663a2ba --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/AbstractBatchModifyStatusCallback.java @@ -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 + *
  • content与title是处理完成后弹出的面板的内容与标题,子类需要在done之前设定好对应的信息 + *
  • 进度条是以 当前完成任务数/总任务数 来计算的 + * + * @author Destiny.Lin + * @since 11.0 + * Created on 2023/6/6 + */ +public abstract class AbstractBatchModifyStatusCallback implements ProgressCallback { + protected JSCallback jsCallback; + protected Map 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); +} diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/BatchModifyStatusCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/BatchModifyStatusCallback.java new file mode 100644 index 0000000000..00c0e8b07a --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/BatchModifyStatusCallback.java @@ -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"); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/extra/exe/callback/BatchUpdateOnlineCallback.java b/designer-base/src/main/java/com/fr/design/extra/exe/callback/BatchUpdateOnlineCallback.java new file mode 100644 index 0000000000..6d93ac1a10 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/extra/exe/callback/BatchUpdateOnlineCallback.java @@ -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); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java b/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java index 1bcfe3c622..1d2048fa28 100644 --- a/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java +++ b/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java @@ -28,7 +28,6 @@ import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; import com.fr.stable.collections.CollectionUtils; import com.fr.stable.project.ProjectConstants; -import com.fr.workspace.WorkContext; import javax.swing.BorderFactory; import javax.swing.JDialog; @@ -221,6 +220,7 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi /** * 检测是否能够黏贴 + * * @param treeNodeList * @return */ @@ -271,7 +271,7 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi } } } catch (Exception e) { - FineLoggerFactory.getLogger().error(e,"Template paste failed.", e.getMessage()); + FineLoggerFactory.getLogger().error(e, "Template paste failed.", e.getMessage()); FineJOptionPane.showConfirmDialog(null, Toolkit.i18nText("Fine-Design_Basic_Paste_Failure"), Toolkit.i18nText("Fine-Design_Basic_Error"), @@ -286,7 +286,7 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi /** * 确认粘贴的目标目录是否是复制文件的子目录,并确认是否继续执行粘贴任务 * - * @param targetDir 目标文件夹 + * @param targetDir 目标文件夹 * @param pasteNodes 待粘贴的文件 * @return 是否继续 */ @@ -433,6 +433,14 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi WARNING_MESSAGE); return false; } + // 检查移动的源文件夹是否为目标文件夹相同文件夹或子文件夹 + if (FileOperationHelper.getInstance().isSubDirectoryOrSame(getFileTree().getSelectedTreeNodes(), getTargetFileNode())) { + FineJOptionPane.showMessageDialog(this, + Toolkit.i18nText("Fine-Design_Basic_Move_To_SubDirectory"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return false; + } if (TemplateUtils.checkSelectedTemplateIsEditing()) { return FineJOptionPane.showConfirmDialog(this, Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), @@ -443,9 +451,7 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi } private boolean doMove() { - FileNode fileNode = getDirTree().getSelectedFileNode(); - ExpandMutableTreeNode rootTreeNode = (ExpandMutableTreeNode) getDirTree().getModel().getRoot(); - fileNode = fileNode == null ? (FileNode) rootTreeNode.getUserObject() : fileNode; + FileNode fileNode = getTargetFileNode(); boolean moveSuccess = true; try { //待移动的文件可以有多个 @@ -465,6 +471,12 @@ public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefi return moveSuccess; } + private FileNode getTargetFileNode() { + FileNode fileNode = getDirTree().getSelectedFileNode(); + ExpandMutableTreeNode rootTreeNode = (ExpandMutableTreeNode) getDirTree().getModel().getRoot(); + return fileNode == null ? (FileNode) rootTreeNode.getUserObject() : fileNode; + } + @Override public void dispose() { TemplateDirTreeSearchManager.getInstance().outOfSearchMode(); diff --git a/designer-base/src/main/java/com/fr/design/file/FileOperationHelper.java b/designer-base/src/main/java/com/fr/design/file/FileOperationHelper.java index a3abaf4606..8bf57ae4ee 100644 --- a/designer-base/src/main/java/com/fr/design/file/FileOperationHelper.java +++ b/designer-base/src/main/java/com/fr/design/file/FileOperationHelper.java @@ -16,7 +16,10 @@ import com.fr.stable.StringUtils; import com.fr.stable.project.ProjectConstants; import com.fr.workspace.WorkContext; import com.fr.workspace.resource.ResourceIOException; +import org.jetbrains.annotations.NotNull; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import static javax.swing.JOptionPane.WARNING_MESSAGE; @@ -32,6 +35,38 @@ public class FileOperationHelper { return INSTANCE; } + /** + * 检查目标文件夹是否为多个源文件夹中的任一文件夹,或者任一文件夹的子文件夹 + * + * @param fileNodes 需移动的源文件 + * @param targetNode 移动后的目标文件夹 + * @return + */ + public boolean isSubDirectoryOrSame(@NotNull ExpandMutableTreeNode[] fileNodes, @NotNull FileNode targetNode) { + for (ExpandMutableTreeNode treeNode : fileNodes) { + FileNode sourceFileNode = (FileNode) treeNode.getUserObject(); + if (isSubDirectoryOrSame(sourceFileNode, targetNode)) { + return true; + } + } + return false; + } + + /** + * 检查目标文件夹是否为源文件夹,或源文件夹的子文件夹 + * + * @param sourceFileNode 需移动的源文件 + * @param targetNode 移动后的目标文件夹 + */ + public boolean isSubDirectoryOrSame(@NotNull FileNode sourceFileNode, @NotNull FileNode targetNode) { + if (!sourceFileNode.isDirectory() || !targetNode.isDirectory()) { + return false; + } + Path sourceDir = Paths.get(sourceFileNode.getEnvPath()).normalize(); + Path targetDir = Paths.get(targetNode.getEnvPath()).normalize(); + return targetDir.startsWith(sourceDir); + } + public String moveFile(FileNode sourceFileNode, String targetDir) { String targetPath = copyFileAndVcs(sourceFileNode, targetDir); FileNodeFILE nodeFILE = new FileNodeFILE(sourceFileNode); @@ -51,8 +86,9 @@ public class FileOperationHelper { /** * 拷贝文件的同时拷贝对应的版本控制文件 + * * @param sourceFile 源文件或目录 - * @param targetDir 目标目录 + * @param targetDir 目标目录 * @return 复制后的目标文件的路径 */ public String copyFileAndVcs(FileNode sourceFile, String targetDir) { @@ -61,8 +97,9 @@ public class FileOperationHelper { /** * 只拷贝文件, 不拷贝对应的版本控制文件 + * * @param sourceFile 源文件或目录 - * @param targetDir 目标目录 + * @param targetDir 目标目录 * @return 复制后的目标文件的路径 */ public String copyFile(FileNode sourceFile, String targetDir) { @@ -71,7 +108,8 @@ public class FileOperationHelper { /** * 检测节点是否被锁住了 - * @param node 待检测节点 + * + * @param node 待检测节点 * @param dNodes 没有锁住的节点集合 * @param lNodes 锁住的节点集合 * @return 是否存在被锁住的文件 @@ -165,7 +203,7 @@ public class FileOperationHelper { } else { if (!TemplateResourceManager.getResource().copy(sourcePath, targetPath)) { throw new ResourceIOException(String.format("copy file failed, from %s to %s", sourcePath, targetPath)); - } else if (withCopyVcs){ + } else if (withCopyVcs) { sourcePath = sourcePath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); targetPath = targetPath.replaceFirst(ProjectConstants.REPORTLETS_NAME, StringUtils.EMPTY); VcsHelper.getInstance().moveVcs(sourcePath, targetPath); diff --git a/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabPane.java b/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabPane.java index 9236deb4ad..6658fc6638 100644 --- a/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabPane.java +++ b/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabPane.java @@ -1108,18 +1108,8 @@ public class MultiTemplateTabPane extends JComponent { * @return */ public int calNextShowJTemplateIndex(int currentIndex) { - //先看是否有可以展示的模板 - for (int i = currentIndex; i >= 0; i--) { - if (showJTemplateTab(openedTemplate.get(i))) { - return i; - } - } - for (int i = currentIndex; i >= 0; i--) { - if (!showJTemplateTab(openedTemplate.get(i))) { - return i; - } - } - return -1; + JTemplate jTemplate= HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + return MultiTemplateTabUtils.calShowTemplateIndex(currentIndex, openedTemplate, jTemplate.getTemplateTabOperatorType()); } diff --git a/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabUtils.java b/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabUtils.java new file mode 100644 index 0000000000..344abe64f2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/file/MultiTemplateTabUtils.java @@ -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> 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> openedTemplate, Predicate> 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()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java index 74b1596207..4116f4f1d0 100644 --- a/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java +++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java @@ -20,6 +20,7 @@ import com.fr.design.javascript.jsapi.JSAPITreeHelper; import com.fr.design.javascript.jsapi.JSAPIUserObject; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.general.ComparatorUtils; +import com.fr.general.GeneralContext; import com.fr.general.http.HttpToolbox; import com.fr.json.JSONArray; import com.fr.json.JSONException; @@ -70,6 +71,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.concurrent.ExecutionException; public class JSContentWithDescriptionPane extends JSContentPane implements KeyListener { @@ -393,9 +395,13 @@ public class JSContentWithDescriptionPane extends JSContentPane implements KeyLi private void updateHelpDocuments(Object value, List helpDocuments) { String url = LocaleLinkProvider.getInstance().getLink(PROPS_LINK_KEY, PROPS_LINK_KEY_DEFAULT) + value.toString(); try { - String result = HttpToolbox.get(url); - JSONObject jsonObject = new JSONObject(result); - JSONArray jsonArray = jsonObject.optJSONArray("list"); + JSONArray jsonArray = null; + //目前简中繁中之外的语言高级编辑器功能及文档不完善,右侧展示的文档链接列表暂时为空白 + if(GeneralContext.isChineseEnv()) { + String result = HttpToolbox.get(url); + JSONObject jsonObject = new JSONObject(result); + jsonArray = jsonObject.optJSONArray("list"); + } if (jsonArray != null) { for (int i = 0; i < jsonArray.length(); i++) { JSONObject resultJSONObject = jsonArray.optJSONObject(i); diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/BrowserExecutor.java b/designer-base/src/main/java/com/fr/design/jxbrowser/BrowserExecutor.java new file mode 100644 index 0000000000..f21609ef6b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/BrowserExecutor.java @@ -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); + } +} diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java b/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java new file mode 100644 index 0000000000..bb2c1f7375 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java @@ -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 parameterMap = Collections.emptyMap(); + + + private final ClearableLazyValue 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 getParameterMap() { + return Collections.unmodifiableMap(parameterMap); + } + + public void setMap(Map 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(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java b/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java new file mode 100644 index 0000000000..1a968551df --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java @@ -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 extends ModernUIPane { + + 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 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 map) { + setMap(map); + browser.navigation().loadUrl(url); + } + + private void setMap(Map 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 executeJsObject(Frame frame, String name) { + return Optional.ofNullable(frame.executeJavaScript(name)); + } + + /** + * JxUIPane 的建造者 + * + * @param 参数 + */ + public static class Builder extends ModernUIPane.Builder { + private String namespace; + private String variable; + private String expression; + private InjectJsCallback callback; + + private Pair listenerPair; + private final Map namespacePropertyMap; + private final Map propertyMap; + + private final Map buildPropertyMap; + private Object variableProperty; + private Map 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 prepare(InjectJsCallback callback) { + this.callback = callback; + return this; + } + + @Override + public Builder prepareForV6(ScriptContextListener contextListener) { + return this; + } + + @Override + public Builder prepareForV6(LoadListener loadListener) { + return this; + } + + @Override + public JxUIPane.Builder prepareForV7(InjectJsCallback callback) { + prepare(callback); + return this; + } + + @Override + public JxUIPane.Builder prepareForV7(Class event, Observer listener) { + listenerPair = new Pair<>(event, listener); + return this; + } + + /** + * 加载jar包中的资源 + * + * @param path 资源路径 + */ + public JxUIPane.Builder withEMB(final String path) { + this.url = EMB_TAG + SCHEME_HEADER + path; + return this; + } + + /** + * 加载jar包中的资源 + * + * @param path 资源路径 + */ + public JxUIPane.Builder withEMB(final String path, Map map) { + this.parameterMap = map; + this.url = EMB_TAG + SCHEME_HEADER + path; + return this; + } + + /** + * 加载url指向的资源 + * + * @param url 文件的地址 + */ + public JxUIPane.Builder withURL(final String url) { + this.url = url; + return this; + } + + /** + * 加载url指向的资源 + * + * @param url 文件的地址 + */ + public JxUIPane.Builder withURL(final String url, Map map) { + this.parameterMap = map; + this.url = url; + return this; + } + + /** + * 加载Atom组件 + * + * @param component Atom组件 + */ + public JxUIPane.Builder withComponent(AssembleComponent component) { + return withComponent(component, null); + } + + /** + * 加载Atom组件 + * + * @param component Atom组件 + */ + public JxUIPane.Builder withComponent(AssembleComponent component, Map map) { + this.parameterMap = map; + this.component = component; + this.url = COMPONENT_TAG; + return this; + } + + + /** + * 加载html文本内容 + * + * @param html 要加载html文本内容 + */ + public JxUIPane.Builder withHTML(String html) { + this.html = html; + return this; + } + + /** + * 设置该前端页面做数据交换所使用的对象 + * 相当于: + * const namespace = "Pool"; + * 调用: + * window[namespace]; + * 默认下结构如: + * window.Pool + * + * @param namespace 对象名 + */ + public JxUIPane.Builder 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 variable(String name) { + this.variable = name; + return this; + } + + /** + * js端往java端传数据时执行的函数表达式 + * + * @param expression 函数表达式 + */ + public JxUIPane.Builder 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 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 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 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 bindWindow(String property, PropertyBuild obj) { + buildPropertyMap.put(property, obj); + return this; + } + + /** + * 构建 + */ + public JxUIPane build() { + JxUIPane 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 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(); + }); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/MimeType.java b/designer-base/src/main/java/com/fr/design/jxbrowser/MimeType.java new file mode 100644 index 0000000000..602e463803 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/MimeType.java @@ -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 = 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; + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/NxInterceptRequestCallback.java b/designer-base/src/main/java/com/fr/design/jxbrowser/NxInterceptRequestCallback.java new file mode 100644 index 0000000000..6a578afa6e --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/NxInterceptRequestCallback.java @@ -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 component; + + private Supplier> renderParameterBuild; + + public NxInterceptRequestCallback(Supplier> renderParameterBuild) { + this.renderParameterBuild = renderParameterBuild; + } + + public NxInterceptRequestCallback(Supplier component, + Supplier> 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 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 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 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 map) { + return component.get() == null ? + StringUtils.EMPTY : + parseComponent(component.get(), map); + } + + @NotNull + private static String parseComponent(AssembleComponent component, Map 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(""); + } + } + 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(""); + } + } + result = result.replaceAll("##script##", scriptText.toString()); + if (map != null) { + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + result = result.replaceAll("\\$\\{" + key + "}", value); + } + } + return result; + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/PropertyBuild.java b/designer-base/src/main/java/com/fr/design/jxbrowser/PropertyBuild.java new file mode 100644 index 0000000000..ca82ca2475 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/PropertyBuild.java @@ -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); +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/login/DesignerLoginBridge.java b/designer-base/src/main/java/com/fr/design/login/DesignerLoginBridge.java index 4789cc4f9b..ad7d180552 100644 --- a/designer-base/src/main/java/com/fr/design/login/DesignerLoginBridge.java +++ b/designer-base/src/main/java/com/fr/design/login/DesignerLoginBridge.java @@ -7,9 +7,9 @@ import com.fr.design.gui.ilable.ActionLabel; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.LocaleLinkProvider; import com.fr.design.i18n.Toolkit; +import com.fr.design.jxbrowser.BrowserExecutor; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.locale.impl.BbsResetMark; -import com.fr.design.login.executor.DesignerLoginBrowserExecutor; import com.fr.design.login.executor.DesignerLoginExecutor; import com.fr.design.login.executor.DesignerSendCaptchaExecutor; import com.fr.design.login.executor.DesignerSmsLoginExecutor; @@ -21,25 +21,26 @@ import com.fr.general.CloudCenter; import com.fr.general.locale.LocaleCenter; import com.fr.general.locale.LocaleMark; import com.fr.log.FineLoggerFactory; -import com.teamdev.jxbrowser.chromium.Browser; -import com.teamdev.jxbrowser.chromium.JSFunction; -import com.teamdev.jxbrowser.chromium.JSObject; +import com.teamdev.jxbrowser.js.JsAccessible; +import com.teamdev.jxbrowser.js.JsFunction; +import com.teamdev.jxbrowser.js.JsObject; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import java.awt.Desktop; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.net.URI; import java.util.Map; import java.util.Set; /** + * 设计器登录通行证js-java桥 + * * @author Lanlan * @version 10.0 * Created by Lanlan on 2021/5/21 */ +@JsAccessible public class DesignerLoginBridge { /** @@ -52,20 +53,26 @@ public class DesignerLoginBridge { */ private static final String PROPS_LINK_KEY_DEFAULT = "Fine-Design-CloudCenter_Market_Template_Default"; - private Map params; + private final Map params; - public static DesignerLoginBridge getBridge(Browser browser, Map params) { - return new DesignerLoginBridge(browser, params); + /** + * 获取 js-java bridge + * + * @param window 全局环境 + * @return bridge + */ + public static DesignerLoginBridge getBridge(JsObject window, Map params) { + return new DesignerLoginBridge(window, params); } - private JSObject window; + private final JsObject window; - private DesignerLoginBridge(Browser browser, Map params) { + private DesignerLoginBridge(JsObject window, Map params) { this.params = params; - this.window = browser.executeJavaScriptAndReturnValue("window").asObject(); + this.window = window; Set> entries = params.entrySet(); for (Map.Entry entry : entries) { - this.window.setProperty(entry.getKey(), entry.getValue()); + this.window.putProperty(entry.getKey(), entry.getValue()); } } @@ -183,9 +190,9 @@ public class DesignerLoginBridge { * @param callback 回调函数 */ @JSBridge - public void normalLogin(String username, String password, final JSFunction callback) { + public void normalLogin(String username, String password, final JsFunction callback) { DesignerLoginTaskWorker worker = new DesignerLoginTaskWorker<>( - new JSCallback(DesignerLoginBrowserExecutor.create(window, callback)), + new JSCallback(BrowserExecutor.create(window, callback)), new DesignerLoginExecutor(username, password)); worker.execute(); } @@ -198,9 +205,9 @@ public class DesignerLoginBridge { * @param callback 回调函数 */ @JSBridge - public void sendCaptcha(String regionCode, String phone, final JSFunction callback) { + public void sendCaptcha(String regionCode, String phone, final JsFunction callback) { DesignerLoginTaskWorker worker = new DesignerLoginTaskWorker<>( - new JSCallback(DesignerLoginBrowserExecutor.create(window, callback)), + new JSCallback(BrowserExecutor.create(window, callback)), new DesignerSendCaptchaExecutor(regionCode, phone)); worker.execute(); } @@ -214,9 +221,9 @@ public class DesignerLoginBridge { * @param callback 回调函数 */ @JSBridge - public void smsLogin(String regionCode, String phone, String code, final JSFunction callback) { + public void smsLogin(String regionCode, String phone, String code, final JsFunction callback) { DesignerLoginTaskWorker worker = new DesignerLoginTaskWorker<>( - new JSCallback(DesignerLoginBrowserExecutor.create(window, callback)), + new JSCallback(BrowserExecutor.create(window, callback)), new DesignerSmsLoginExecutor(regionCode, phone, code)); worker.execute(); } @@ -231,9 +238,9 @@ public class DesignerLoginBridge { * @param callback 回调函数 */ @JSBridge - public void smsRegister(String regionCode, String phone, String password, String regToken, final JSFunction callback) { + public void smsRegister(String regionCode, String phone, String password, String regToken, final JsFunction callback) { DesignerLoginTaskWorker worker = new DesignerLoginTaskWorker<>( - new JSCallback(DesignerLoginBrowserExecutor.create(window, callback)), + new JSCallback(BrowserExecutor.create(window, callback)), new DesignerSmsRegisterExecutor(regionCode, phone, password, regToken)); worker.execute(); } @@ -275,13 +282,10 @@ public class DesignerLoginBridge { private JPanel getHyperlinkPane(String title, String hyperlinkText, String hyperlink) { ActionLabel actionLabel = new ActionLabel(hyperlinkText); - actionLabel.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - try { - Desktop.getDesktop().browse(new URI(hyperlink)); - } catch (Exception ignore) { - } + actionLabel.addActionListener(e -> { + try { + Desktop.getDesktop().browse(new URI(hyperlink)); + } catch (Exception ignore) { } }); JPanel panel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); diff --git a/designer-base/src/main/java/com/fr/design/login/DesignerLoginPane.java b/designer-base/src/main/java/com/fr/design/login/DesignerLoginPane.java index be5df3a84d..73811c0c83 100644 --- a/designer-base/src/main/java/com/fr/design/login/DesignerLoginPane.java +++ b/designer-base/src/main/java/com/fr/design/login/DesignerLoginPane.java @@ -2,21 +2,23 @@ package com.fr.design.login; import com.fr.design.DesignerEnvManager; import com.fr.design.dialog.BasicPane; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.login.utils.DesignerLoginUtils; -import com.fr.design.ui.ModernUIPane; -import com.teamdev.jxbrowser.chromium.JSValue; -import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; -import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; + import java.awt.BorderLayout; import java.util.Map; /** + * 设计器登录通行证面板 + * * @author Lanlan * @version 10.0 * Created by Lanlan on 2021/5/21 */ public class DesignerLoginPane extends BasicPane { + private static final String DESIGNER_LOGIN_HELPER = "DesignerLoginHelper"; + @Override protected String title4PopupWindow() { return "DESIGNER_LOGIN"; @@ -27,14 +29,8 @@ public class DesignerLoginPane extends BasicPane { params.put("lastLoginType", String.valueOf(DesignerEnvManager.getEnvManager().getLastLoginType().getType())); params.put("lastLoginAccount", DesignerEnvManager.getEnvManager().getLastLoginAccount()); setLayout(new BorderLayout()); - ModernUIPane modernUIPane = new ModernUIPane.Builder<>() - .prepare(new ScriptContextAdapter() { - @Override - public void onScriptContextCreated(ScriptContextEvent event) { - JSValue window = event.getBrowser().executeJavaScriptAndReturnValue("window"); - window.asObject().setProperty("DesignerLoginHelper", DesignerLoginBridge.getBridge(event.getBrowser(), params)); - } - }) + JxUIPane modernUIPane = new JxUIPane.Builder<>() + .bindWindow(DESIGNER_LOGIN_HELPER, window -> DesignerLoginBridge.getBridge(window, params)) .withEMB(DesignerLoginHelper.getMainResourcePath(), DesignerLoginUtils.renderMap()) .build(); add(modernUIPane, BorderLayout.CENTER); diff --git a/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideBridge.java b/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideBridge.java index 9b9c561e7c..e45323a0ed 100644 --- a/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideBridge.java +++ b/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideBridge.java @@ -6,26 +6,31 @@ import com.fr.design.dialog.FineJOptionPane; import com.fr.design.i18n.Toolkit; import com.fr.design.login.DesignerLoginHelper; import com.fr.design.login.DesignerLoginSource; -import com.teamdev.jxbrowser.chromium.Browser; -import com.teamdev.jxbrowser.chromium.JSObject; +import com.teamdev.jxbrowser.js.JsAccessible; + import javax.swing.JOptionPane; import javax.swing.SwingUtilities; /** + * 设计器登录指南面板的js-java桥 + * * @author Lanlan * @version 10.0 * Created by Lanlan on 2021/5/21 */ +@JsAccessible public class DesignerGuideBridge { - public static DesignerGuideBridge getBridge(Browser browser) { - return new DesignerGuideBridge(browser); + /** + * 获取 js-java bridge + * + * @return bridge + */ + public static DesignerGuideBridge getBridge() { + return new DesignerGuideBridge(); } - private JSObject window; - - private DesignerGuideBridge(Browser browser) { - this.window = browser.executeJavaScriptAndReturnValue("window").asObject(); + private DesignerGuideBridge() { } @JSBridge @@ -40,31 +45,28 @@ public class DesignerGuideBridge { DesignerLoginHelper.showLoginDialog(DesignerLoginSource.GUIDE); checkDoNotRemind(doNotRemind); } else { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - String[] options = new String[]{ - com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Login_Quit"), - com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Login_Return_Login") - }; - int rv = FineJOptionPane.showConfirmDialog( - DesignerGuideHelper.getDialog(), - com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Login_Quit_Tip"), - com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"), - JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE, - null, - options, - options[1] - ); - if (rv == JOptionPane.YES_OPTION) { - DesignerGuideHelper.closeWindow(); - checkDoNotRemind(doNotRemind); - } else if (rv == JOptionPane.NO_OPTION) { - DesignerLoginHelper.showLoginDialog(DesignerLoginSource.GUIDE); - DesignerGuideHelper.closeWindow(); - checkDoNotRemind(doNotRemind); - } + SwingUtilities.invokeLater(() -> { + String[] options = new String[]{ + Toolkit.i18nText("Fine-Designer_Login_Quit"), + Toolkit.i18nText("Fine-Designer_Login_Return_Login") + }; + int rv = FineJOptionPane.showConfirmDialog( + DesignerGuideHelper.getDialog(), + Toolkit.i18nText("Fine-Designer_Login_Quit_Tip"), + Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + null, + options, + options[1] + ); + if (rv == JOptionPane.YES_OPTION) { + DesignerGuideHelper.closeWindow(); + checkDoNotRemind(doNotRemind); + } else if (rv == JOptionPane.NO_OPTION) { + DesignerLoginHelper.showLoginDialog(DesignerLoginSource.GUIDE); + DesignerGuideHelper.closeWindow(); + checkDoNotRemind(doNotRemind); } }); } diff --git a/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideHelper.java b/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideHelper.java index 3e4c0dae51..05956da7c3 100644 --- a/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideHelper.java +++ b/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuideHelper.java @@ -10,9 +10,12 @@ import com.fr.design.os.impl.SupportOSImpl; import com.fr.design.update.push.DesignerPushUpdateManager; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; + import javax.swing.WindowConstants; /** + * 设计器登录指南帮助类 + * * @author Lanlan * @version 10.0 * Created by Lanlan on 2021/5/21 diff --git a/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuidePane.java b/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuidePane.java index 64bd316ef5..9e2277b1fb 100644 --- a/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuidePane.java +++ b/designer-base/src/main/java/com/fr/design/login/guide/DesignerGuidePane.java @@ -1,20 +1,22 @@ package com.fr.design.login.guide; import com.fr.design.dialog.BasicPane; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.login.guide.utils.DesignerGuideUtils; -import com.fr.design.ui.ModernUIPane; -import com.teamdev.jxbrowser.chromium.JSValue; -import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; -import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; + import java.awt.BorderLayout; /** + * 设计器登录帆软通行证的指南面板 + * * @author Lanlan * @version 10.0 * Created by Lanlan on 2021/5/21 */ public class DesignerGuidePane extends BasicPane { + private static final String DESIGNER_GUIDE_HELPER = "DesignerGuideHelper"; + @Override protected String title4PopupWindow() { return "DESIGNER_GUIDE"; @@ -22,14 +24,8 @@ public class DesignerGuidePane extends BasicPane { public DesignerGuidePane() { setLayout(new BorderLayout()); - ModernUIPane modernUIPane = new ModernUIPane.Builder<>() - .prepare(new ScriptContextAdapter() { - @Override - public void onScriptContextCreated(ScriptContextEvent event) { - JSValue window = event.getBrowser().executeJavaScriptAndReturnValue("window"); - window.asObject().setProperty("DesignerGuideHelper", DesignerGuideBridge.getBridge(event.getBrowser())); - } - }) + JxUIPane modernUIPane = new JxUIPane.Builder<>() + .bindWindow(DESIGNER_GUIDE_HELPER, w -> DesignerGuideBridge.getBridge()) .withEMB(DesignerGuideHelper.getMainResourcePath(), DesignerGuideUtils.renderMap()) .build(); add(modernUIPane, BorderLayout.CENTER); diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java index 8cfd263421..174655127c 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java @@ -54,6 +54,7 @@ import com.fr.plugin.injectable.PluginModule; import com.fr.plugin.manage.PluginFilter; import com.fr.plugin.observer.PluginEvent; import com.fr.plugin.observer.PluginEventListener; +import com.fr.report.lock.LockInfoOperator; import com.fr.stable.CoreConstants; import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; @@ -61,7 +62,6 @@ import com.fr.stable.project.ProjectConstants; import com.fr.third.org.apache.commons.io.FilenameUtils; import com.fr.workspace.WorkContext; -import com.fr.report.lock.LockInfoOperator; import javax.swing.BorderFactory; import javax.swing.JDialog; import javax.swing.JOptionPane; @@ -325,7 +325,7 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt if (VcsHelper.getInstance().needInit()) { vcsAction = new VcsAction(); - if (!WorkContext.getCurrent().isCluster()) { + if (!isLegacyOnCluster()) { vcsAction.setName(Toolkit.i18nText("Fine-Design_Vcs_Title")); } else { vcsAction.setName(Toolkit.i18nText("Fine-Design_Vcs_NotSupportRemote")); @@ -509,7 +509,7 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt private void fireVcsActionChange(boolean enable) { if (!DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable() || VcsHelper.getInstance().isUnSelectedTemplate() - || WorkContext.getCurrent().isCluster()) { + || isLegacyOnCluster()) { setEnabled(false); return; } @@ -537,7 +537,7 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } private boolean pathSupportVcsAction(String path) { - if (FileExtension.CPT.matchExtension(path) || FileExtension.FRM.matchExtension(path)) { + if (FileExtension.CPT.matchExtension(path) || FileExtension.FRM.matchExtension(path) || FileExtension.VIS.matchExtension(path)) { return true; } return false; @@ -810,6 +810,11 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } } + private boolean isLegacyOnCluster() { + // 老模式且为集群,用于代替之前的只判断集群逻辑 + return WorkContext.getCurrent().isCluster() && VcsHelper.getInstance().isLegacyMode(); + } + private String doCheck (String userInput, String suffix) { String errorMsg = StringUtils.EMPTY; if (selectedOperation.duplicated(userInput, suffix, true)) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java index fcfa4292fa..d850e8e814 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/NorthRegionContainerPane.java @@ -157,7 +157,8 @@ public class NorthRegionContainerPane extends JPanel { if (!DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().isEnabled()) { ad.createAlphaFinePane().setVisible(false); } - northEastPane.add(ad.createGuideEntryPane()); + /// 新手引导功能,暂时屏蔽 + // northEastPane.add(ad.createGuideEntryPane()); northEastPane.add(ad.createNotificationCenterPane()); OSSupportCenter.buildAction(new OSBasedAction() { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/mobile/processor/AbstractMobileStyleDefinePaneCreator.java b/designer-base/src/main/java/com/fr/design/mainframe/mobile/processor/AbstractMobileStyleDefinePaneCreator.java new file mode 100644 index 0000000000..203a83f13c --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/mobile/processor/AbstractMobileStyleDefinePaneCreator.java @@ -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; + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/mobile/processor/MobileStyleDefinePaneCreator.java b/designer-base/src/main/java/com/fr/design/mainframe/mobile/processor/MobileStyleDefinePaneCreator.java new file mode 100644 index 0000000000..3b5e2cb861 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/mobile/processor/MobileStyleDefinePaneCreator.java @@ -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; + + /** + *

    创建通用属性样式界面,可替换{@link com.fr.design.mainframe.mobile.ui.MobileStyleDefinePane} + *

    每种样式的通用属性面板是一样的 + * + * @param widget 控件 + * @param customDefinePane 自定义面板 + * @param mobileStyle 移动端样式 + * @return + */ + @Nullable BasicBeanPane createBaseBeanPane(Widget widget, Class customDefinePane, Class mobileStyle); + + /** + * 替换通用属性面板,注册额外属性 + * + * @return 属性类 + */ + @Nullable Class classForCommonExtraStyle(Widget widget); +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStyleDefinePane.java b/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStyleDefinePane.java index 6c03df00d5..268672fb30 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStyleDefinePane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStyleDefinePane.java @@ -53,22 +53,28 @@ public class MobileStyleDefinePane extends BasicBeanPane { private UISpinner borderRadius; private NewColorSelectBox iconColor; private MobileStyleFontConfigPane fontConfigPane; + private MobileStyle mobileStyle; MobileStyleDefinePane(Widget widget, Class customBeanPaneClass, Class mobileStyleClazz) { this.widget = widget; this.customBeanPane = Reflect.on(customBeanPaneClass).create(widget).get(); this.mobileStyleClazz = mobileStyleClazz; + initMobileStyle(widget); init(); } + private void initMobileStyle(Widget widget) { + mobileStyle = widget.getMobileStyle() != null ? widget.getMobileStyle() : Reflect.on(mobileStyleClazz).create().get(); + } + @Override public void populateBean(MobileStyle ob) { this.customBeanPane.populateBean(ob); customCombo.setSelectedIndex(ob.isCommonCustom() ? 1 : 0); - if(ob.getCommonBackground() != null) { - colorSelectBox.setSelectObject(((ColorBackground)ob.getCommonBackground()).getColor()); + if (ob.getCommonBackground() != null) { + colorSelectBox.setSelectObject(((ColorBackground) ob.getCommonBackground()).getColor()); } borderType.setSelectedLineStyle(ob.getCommonBorderType()); if (ob.getCommonBorderColor() != null) { @@ -85,7 +91,6 @@ public class MobileStyleDefinePane extends BasicBeanPane { @Override public MobileStyle updateBean() { - MobileStyle mobileStyle = Reflect.on(mobileStyleClazz).create().get(); this.widget.setMobileStyle(mobileStyle); this.customBeanPane.updateBean(); mobileStyle.setCommonCustom(customCombo.getSelectedIndex() == 1); diff --git a/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStylePane.java b/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStylePane.java index 3573427ca9..aafc5d4e2b 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStylePane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/mobile/ui/MobileStylePane.java @@ -5,17 +5,32 @@ import com.fr.design.beans.BasicBeanPane; import com.fr.design.dialog.BasicPane; import com.fr.design.fun.MobileWidgetStyleProvider; import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.mobile.processor.MobileStyleDefinePaneCreator; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.Listener; import com.fr.form.ui.Widget; import com.fr.form.ui.container.WScaleLayout; +import com.fr.form.ui.mobile.MobileCommonExtraStyle; import com.fr.form.ui.mobile.MobileStyle; +import com.fr.form.ui.mobile.StyleClassMap; import com.fr.form.ui.widget.CRBoundsWidget; import com.fr.log.FineLoggerFactory; +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.observer.PluginEventType; import com.fr.stable.ArrayUtils; -import javax.swing.*; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Dimension; import java.util.HashMap; import java.util.Map; @@ -28,9 +43,11 @@ public class MobileStylePane extends BasicPane { private JList styleList; private Map> map = new HashMap<>(); + private boolean checkFlag = true; + public MobileStylePane(Widget widget) { - if(widget instanceof WScaleLayout) { - this.widget = ((CRBoundsWidget)((WScaleLayout) widget).getBoundsWidget()).getWidget(); + if (widget instanceof WScaleLayout) { + this.widget = ((CRBoundsWidget) ((WScaleLayout) widget).getBoundsWidget()).getWidget(); } else { this.widget = widget; } @@ -63,13 +80,18 @@ public class MobileStylePane extends BasicPane { } private void init() { + initComponent(); + initPluginListener(); + } + + private void initComponent() { this.setLayout(FRGUIPaneFactory.createBorderLayout()); listModel = new DefaultListModel<>(); card = new CardLayout(); right = FRGUIPaneFactory.createCardLayout_S_Pane(); right.setLayout(card); MobileWidgetStyleProvider[] styleProviders = getMobileWidgetStyleProviders(); - for(MobileWidgetStyleProvider styleProvider: styleProviders) { + for (MobileWidgetStyleProvider styleProvider : styleProviders) { this.addProvider2View(styleProvider); } this.addWestList(); @@ -107,7 +129,15 @@ public class MobileStylePane extends BasicPane { listModel.addElement(displayName); try { + MobileStyleDefinePaneCreator processor = ExtraDesignClassManager.getInstance().getSingle(MobileStyleDefinePaneCreator.XML_TAG); BasicBeanPane mobileStyleBasicBeanPane = new MobileStyleDefinePane(widget, appearanceClazz, mobileStyleClazz); + if (checkFlag && processor != null && processor.createBaseBeanPane(widget, appearanceClazz, mobileStyleClazz) != null) { + mobileStyleBasicBeanPane = processor.createBaseBeanPane(widget, appearanceClazz, mobileStyleClazz); + Class extraStyle = processor.classForCommonExtraStyle(widget); + if (extraStyle != null) { + StyleClassMap.putCommonStyle(extraStyle.getName(), extraStyle.getName()); + } + } right.add(displayName, mobileStyleBasicBeanPane); map.put(displayName, mobileStyleBasicBeanPane); } catch (Exception e) { @@ -133,4 +163,33 @@ public class MobileStylePane extends BasicPane { styleProviders = ArrayUtils.insert(0, styleProviders, defaultMobileWidgetStyleProvider); return styleProviders; } + + private void initPluginListener() { + EventDispatcher.listen(PluginEventType.AfterRun, new Listener() { + @Override + public void on(Event event, PluginContext pluginContext) { + if (pluginContext.getRuntime().contain(MobileStyleDefinePaneCreator.XML_TAG)) { + checkFlag = true; + refreshDockingView(); + } + } + }); + EventDispatcher.listen(PluginEventType.BeforeStop, new Listener() { + @Override + public void on(Event event, PluginContext pluginContext) { + if (pluginContext.getRuntime().contain(MobileStyleDefinePaneCreator.XML_TAG)) { + checkFlag = false; + refreshDockingView(); + } + } + }); + } + + private void refreshDockingView() { + removeAll(); + initComponent(); + populate(widget.getMobileStyle()); + this.updateUI(); + this.repaint(); + } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeBlock.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeBlock.java index ea649033ea..7ff83c7b19 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeBlock.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeBlock.java @@ -11,6 +11,7 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.theme.dialog.TemplateThemeProfileDialog; import com.fr.general.IOUtils; +import com.fr.general.locale.image.I18nImage; import com.fr.stable.Constants; import com.fr.stable.StringUtils; @@ -26,7 +27,6 @@ import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; -import java.awt.Toolkit; import java.awt.Window; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -209,7 +209,8 @@ public class TemplateThemeBlock extends JPanel { } private static class ThumbnailPane extends JPanel { - private static final Image LOADING_IMAGE = Toolkit.getDefaultToolkit().createImage(ThumbnailPane.class.getResource("/com/fr/design/images/mainframe/loading.gif")); + private static final String LOADING_IMAGE_URL = "/com/fr/design/images/mainframe/loading/loading.gif"; + private static final Image LOADING_IMAGE = I18nImage.getImage(LOADING_IMAGE_URL); private Image thumbnail = null; @Override diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java index f61bba67a8..8551f735b9 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/VcsConfigManager.java @@ -16,6 +16,10 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { private boolean useInterval = true; private int saveInterval = 60; + private boolean useAutoSave = false; + + private int autoSaveInterval = 15; + public static VcsConfigManager getInstance() { return instance; } @@ -48,6 +52,23 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { return saveInterval; } + public int getAutoSaveInterval() { + return autoSaveInterval; + } + + public void setAutoSaveInterval(int autoSaveInterval) { + this.autoSaveInterval = autoSaveInterval; + } + + public boolean isUseAutoSave() { + return useAutoSave; + } + + public void setUseAutoSave(boolean useAutoSave) { + this.useAutoSave = useAutoSave; + } + + public void setSaveInterval(int saveInterval) { this.saveInterval = saveInterval; } @@ -59,6 +80,8 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { this.setSaveInterval(reader.getAttrAsInt("saveInterval", 60)); this.setUseInterval(reader.getAttrAsBoolean("useInterval", true)); this.setVcsEnable(reader.getAttrAsBoolean("vcsEnable", true)); + this.setAutoSaveInterval(reader.getAttrAsInt("autoSaveInterval", 15)); + this.setUseAutoSave(reader.getAttrAsBoolean("useAutoSave", false)); } } @@ -69,6 +92,8 @@ public class VcsConfigManager implements XMLReadable, XMLWriter { writer.attr("saveInterval", this.getSaveInterval()); writer.attr("useInterval", this.isUseInterval()); writer.attr("vcsEnable", this.isVcsEnable()); + writer.attr("autoSaveInterval", this.getAutoSaveInterval()); + writer.attr("useAutoSave", this.isUseAutoSave()); writer.end(); } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java index 980ccb5a3a..b245eff66c 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java @@ -2,6 +2,7 @@ package com.fr.design.mainframe.vcs.common; import com.fr.concurrent.NamedThreadFactory; +import com.fr.config.ConfigEvent; import com.fr.design.DesignerEnvManager; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.file.TemplateTreePane; @@ -12,6 +13,9 @@ import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.JTemplateActionListener; import com.fr.design.mainframe.vcs.VcsConfigManager; import com.fr.design.mainframe.vcs.ui.FileVersionTable; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.ListenerAdaptor; import com.fr.general.IOUtils; import com.fr.log.FineLoggerFactory; import com.fr.plugin.context.PluginContext; @@ -25,11 +29,14 @@ import com.fr.workspace.server.vcs.filesystem.VcsFileSystem; import com.fr.workspace.server.vcs.git.config.GcConfig; import javax.swing.Icon; +import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import java.awt.Color; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * Created by XiaXiang on 2019/4/17. @@ -55,10 +62,65 @@ public class VcsHelper implements JTemplateActionListener { private final static String SERVICE_NAME_MOVE = "moveVcs"; private static final VcsHelper INSTANCE = new VcsHelper(); + private static ScheduledExecutorService saveSchedule; + + private volatile boolean legacyMode; + public static VcsHelper getInstance() { return INSTANCE; } + private VcsHelper() { + VcsOperator op = WorkContext.getCurrent().get(VcsOperator.class); + // 开了设计器启动页面时一开始取不到VcsOperator,通过下面的切换环境事件再取,这边判断下 + if (op != null) { + legacyMode = op.isLegacyMode(); + } + EventDispatcher.listen(ConfigEvent.READY, new ListenerAdaptor() { + @Override + protected void on(Event event) { + try { + legacyMode = WorkContext.getCurrent().get(VcsOperator.class).isLegacyMode(); + FineLoggerFactory.getLogger().info("[VcsHelper] legacyMode:{}", legacyMode); + } catch (Exception e) { + //保险起见走老逻辑 + legacyMode = true; + FineLoggerFactory.getLogger().error("[VcsHelper] get legacy failed", e.getMessage()); + } + } + }); + } + + /** + * 开始自动保存任务 + * + * @param interval 时间间隔 + */ + public void startAutoSave(int interval) { + stopAutoSave(); + saveSchedule = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("VcsAutoSaveSchedule")); + saveSchedule.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + FineLoggerFactory.getLogger().info("[VcsV2] start to run auto save schedule"); + JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (DesignerEnvManager.getEnvManager().getVcsConfigManager().isVcsEnable() && JTemplate.isValid(template)) { + fireAutoSaveVcs(template); + } + } + }, interval, interval, TimeUnit.MINUTES); + } + + /** + * 停止任务 + */ + public void stopAutoSave() { + if (saveSchedule != null && !saveSchedule.isShutdown()) { + saveSchedule.shutdown(); + } + } + + private int containsFolderCounts() { TemplateFileTree fileTree = TemplateTreePane.getInstance().getTemplateFileTree(); if (fileTree.getSelectionPaths() == null) { @@ -133,24 +195,21 @@ public class VcsHelper implements JTemplateActionListener { fireVcs.execute(new Runnable() { @Override public void run() { - String fileName = getEditingFilename(); VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class); VcsEntity entity = operator.getFileVersionByIndex(fileName, 0); + boolean replace = needDeleteVersion(entity); int latestFileVersion = 0; if (entity != null) { latestFileVersion = entity.getVersion(); } if (jt.getEditingFILE() instanceof VcsCacheFileNodeFile) { - operator.saveVersionFromCache(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1); + operator.saveVersionFromCache(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1, replace); String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); - FileVersionTable.getInstance().updateModel(1, WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst(VCS_FILE_SLASH, StringUtils.EMPTY))); + List updatedList = WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst(VCS_FILE_SLASH, StringUtils.EMPTY)); + SwingUtilities.invokeLater(() -> FileVersionTable.getInstance().updateModel(1, updatedList)); } else { - operator.saveVersion(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1); - } - VcsEntity oldEntity = WorkContext.getCurrent().get(VcsOperator.class).getFileVersionByIndexAndUsername(fileName, getCurrentUsername(), 1); - if (needDeleteVersion(oldEntity)) { - operator.deleteVersion(oldEntity.getFilename(), oldEntity.getVersion()); + operator.saveVersion(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1, replace); } if (GcConfig.getInstance().isGcEnable()) { operator.gc(); @@ -158,7 +217,9 @@ public class VcsHelper implements JTemplateActionListener { } }); - fireVcs.shutdown(); + if (!fireVcs.isShutdown()) { + fireVcs.shutdown(); + } } /** @@ -188,12 +249,85 @@ public class VcsHelper implements JTemplateActionListener { moveVcs.shutdown(); } + /** + * 判断是否为老模式 + * @return 是否为老模式 + */ + public boolean isLegacyMode() { + return legacyMode; + } + + /** + * 更新当前的legacyMode状态 + *

  • 目前用在迁移结束后更新模式为新模式
  • + * + */ + public void updateLegacyMode() { + this.legacyMode = !legacyMode; + } @Override public void templateOpened(JTemplate jt) { + try { + if (checkAutoSaveSupport()) { + startAutoSave(VcsConfigManager.getInstance().getAutoSaveInterval()); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } } + /** + * 响应版本管理自动保存 + * + *
  • 直接用template的file来保存的话相当于拿源文件来保存模板,这样用户做的改动会丢失
  • + *
  • 因此需要自己实现一下自动保存的逻辑,将当前模板的数据导出,再拿这个Byte[]去做我们需要的保存处理
  • + *
  • 保存后需要触发清理逻辑
  • + * + * @param jt 模板 + */ + public void fireAutoSaveVcs(final JTemplate jt) { + String fileName = getEditingFilename(); + VcsOperator operator = WorkContext.getCurrent().get(VcsOperator.class); + VcsEntity entity = operator.getFileVersionByIndex(fileName, 0); + boolean replace = needDeleteVersion(entity); + int latestFileVersion = 0; + if (entity != null) { + latestFileVersion = entity.getVersion(); + } + if (JTemplate.isValid(jt)) { + doSave(jt, fileName, latestFileVersion, replace, operator); + } + } + + private void doSave(JTemplate jt, String fileName, int latestFileVersion, boolean replace, VcsOperator operator) { + if (jt.getEditingFILE() instanceof VcsCacheFileNodeFile) { + operator.saveVersionFromCache(getCurrentUsername(), fileName, StringUtils.EMPTY, latestFileVersion + 1, replace); + String path = DesignerFrameFileDealerPane.getInstance().getSelectedOperation().getFilePath(); + List updatedList = WorkContext.getCurrent().get(VcsOperator.class).getVersions(path.replaceFirst(VCS_FILE_SLASH, StringUtils.EMPTY)); + SwingUtilities.invokeLater(() -> FileVersionTable.getInstance().updateModel(1, updatedList)); + } else { + autoSave(jt, getCurrentUsername(), fileName, latestFileVersion + 1, replace, operator); + } + if (GcConfig.getInstance().isGcEnable()) { + operator.gc(); + } + } + + private void autoSave(JTemplate jt, String currentUsername, String fileName, int nowVersion, boolean replace, VcsOperator operator) { + try { + if (JTemplate.isValid(jt)) { + operator.autoSave(currentUsername, fileName, StringUtils.EMPTY, nowVersion, jt.exportData(), replace); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + } + + + + /** * 模板保存时 处理. * @@ -210,6 +344,40 @@ public class VcsHelper implements JTemplateActionListener { @Override public void templateClosed(JTemplate jt) { + try { + if (checkAutoSaveSupport()) { + stopAutoSave(); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + + } + + /** + * 判断是否支持V2功能 + * + * @return 支持返回true + */ + public boolean checkV2FunctionSupport() { + return !VcsHelper.getInstance().isLegacyMode() && (WorkContext.getCurrent().isLocal() || WorkContext.getCurrent().isRoot()); + } + /** + * 判断是否支持迁移功能 + * + * @return 支持返回true + */ + public boolean checkMoveFunctionSupport() { + return VcsHelper.getInstance().isLegacyMode() && (WorkContext.getCurrent().isLocal() || WorkContext.getCurrent().isRoot()); + } + + /** + * 是否支持自动保存 + * + * @return 支持返回true + */ + public boolean checkAutoSaveSupport() { + return VcsConfigManager.getInstance().isUseAutoSave() && !VcsHelper.getInstance().isLegacyMode(); } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsMovePanel.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsMovePanel.java new file mode 100644 index 0000000000..af450d8250 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/ui/VcsMovePanel.java @@ -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() { + @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 { + + 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 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){} + } + +} diff --git a/designer-base/src/main/java/com/fr/design/ui/ModernUIConstants.java b/designer-base/src/main/java/com/fr/design/ui/ModernUIConstants.java index 8f7bf07034..9edd5bd57c 100644 --- a/designer-base/src/main/java/com/fr/design/ui/ModernUIConstants.java +++ b/designer-base/src/main/java/com/fr/design/ui/ModernUIConstants.java @@ -3,12 +3,28 @@ package com.fr.design.ui; import com.fr.general.IOUtils; /** + * 新式面板常量 + * * @author richie * @version 10.0 * Created by richie on 2019-03-05 */ public class ModernUIConstants { + public static final String WINDOW = "window"; + public static final String DOT = "."; + public static final String EMB_TAG = "emb"; + /** + * 从emb:dynamic 改为 emb://dynamic + * 7之后协议会自动在":"后加上斜杠,导致识别错误 + */ + public static final String COMPONENT_TAG = "emb://dynamic"; + + public static final String SCHEME_HEADER = "://"; + + public static final String DEFAULT_NAMESPACE = "Pool"; + public static final String DEFAULT_VARIABLE = "data"; + public static final String DEFAULT_EXPRESSION = "update()"; public static final String SCRIPT_INIT_NAME_SPACE = IOUtils.readResourceAsString("/com/fr/design/ui/InitNameSpace.js"); public static final String HTML_TPL = IOUtils.readResourceAsString("/com/fr/design/ui/tpl.html"); diff --git a/designer-base/src/main/java/com/fr/design/ui/ModernUIPane.java b/designer-base/src/main/java/com/fr/design/ui/ModernUIPane.java index 5a0ead41e7..f3aceb8476 100644 --- a/designer-base/src/main/java/com/fr/design/ui/ModernUIPane.java +++ b/designer-base/src/main/java/com/fr/design/ui/ModernUIPane.java @@ -34,7 +34,10 @@ import java.util.Map; * @version 10.0 * Created by richie on 2019-03-04 * 用于加载html5的Swing容器,可以在设计选项设置中打开调试窗口,示例可查看:com.fr.design.ui.ModernUIPaneTest + * @see {@link com.fr.design.jxbrowser.JxUIPane} + * @deprecated 主要用于jxbrowser6,将在下个版本删除 */ +@Deprecated public class ModernUIPane extends BasicPane { private Browser browser; @@ -119,6 +122,7 @@ public class ModernUIPane extends BasicPane { /** * 转向一个新的地址,相当于重新加载 + * * @param url 新的地址 */ public void redirect(String url) { @@ -127,6 +131,7 @@ public class ModernUIPane extends BasicPane { /** * 转向一个新的地址,相当于重新加载 + * * @param url 新的地址 * @param map 初始化参数 */ @@ -161,7 +166,7 @@ public class ModernUIPane extends BasicPane { public void disposeBrowser() { - if(browser != null) { + if (browser != null) { browser.dispose(); browser = null; } @@ -191,6 +196,12 @@ public class ModernUIPane extends BasicPane { return null; } + + /** + * ModernUIPane 建造者 + * + * @param + */ public static class Builder implements BuilderDiff { private ModernUIPane pane; @@ -219,6 +230,7 @@ public class ModernUIPane extends BasicPane { /** * 加载jar包中的资源 + * * @param path 资源路径 */ public Builder withEMB(final String path) { @@ -229,6 +241,7 @@ public class ModernUIPane extends BasicPane { /** * 加载jar包中的资源 + * * @param path 资源路径 */ public Builder withEMB(final String path, Map map) { @@ -239,6 +252,7 @@ public class ModernUIPane extends BasicPane { /** * 加载url指向的资源 + * * @param url 文件的地址 */ public Builder withURL(final String url) { @@ -249,6 +263,7 @@ public class ModernUIPane extends BasicPane { /** * 加载url指向的资源 + * * @param url 文件的地址 */ public Builder withURL(final String url, Map map) { @@ -259,6 +274,7 @@ public class ModernUIPane extends BasicPane { /** * 加载Atom组件 + * * @param component Atom组件 */ public Builder withComponent(AssembleComponent component) { @@ -269,6 +285,7 @@ public class ModernUIPane extends BasicPane { /** * 加载Atom组件 + * * @param component Atom组件 */ public Builder withComponent(AssembleComponent component, Map map) { @@ -277,9 +294,9 @@ public class ModernUIPane extends BasicPane { return this; } - /** * 加载html文本内容 + * * @param html 要加载html文本内容 */ public Builder withHTML(String html) { @@ -290,6 +307,7 @@ public class ModernUIPane extends BasicPane { /** * 设置该前端页面做数据交换所使用的对象 + * * @param namespace 对象名 */ public Builder namespace(String namespace) { @@ -299,6 +317,7 @@ public class ModernUIPane extends BasicPane { /** * java端往js端传数据时使用的变量名字 + * * @param name 变量的名字 */ public Builder variable(String name) { @@ -308,6 +327,7 @@ public class ModernUIPane extends BasicPane { /** * js端往java端传数据时执行的函数表达式 + * * @param expression 函数表达式 */ public Builder expression(String expression) { diff --git a/designer-base/src/main/java/com/fr/design/ui/compatible/BuilderDiff.java b/designer-base/src/main/java/com/fr/design/ui/compatible/BuilderDiff.java index 9e6168514f..99daa4555a 100644 --- a/designer-base/src/main/java/com/fr/design/ui/compatible/BuilderDiff.java +++ b/designer-base/src/main/java/com/fr/design/ui/compatible/BuilderDiff.java @@ -1,5 +1,6 @@ package com.fr.design.ui.compatible; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.ui.ModernUIPane; import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; import com.teamdev.jxbrowser.chromium.events.LoadListener; @@ -10,17 +11,45 @@ import com.teamdev.jxbrowser.event.Observer; * 封装jxbrwoser v6/v7的构建方式的差异 * * @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在下个版本弃用 */ +@Deprecated public interface BuilderDiff { + /** + * v6准备工作 + * + * @param contextListener 上下文监听器 + * @return 构造器 + */ ModernUIPane.Builder prepareForV6(ScriptContextListener contextListener); + /** + * v6准备工作 + * + * @param loadListener 加载监听器 + * @return 构造器 + */ ModernUIPane.Builder prepareForV6(LoadListener loadListener); + /** + * v7准备工作 + * + * @param callback 注入js回调器 + * @return 构造器 + */ ModernUIPane.Builder prepareForV7(InjectJsCallback callback); + /** + * v7准备工作 + * + * @param event 事件 + * @param listener 监听器 + * @return 构造器 + */ ModernUIPane.Builder prepareForV7(Class event, Observer listener); } diff --git a/designer-base/src/main/java/com/fr/design/ui/compatible/ModernUIPaneFactory.java b/designer-base/src/main/java/com/fr/design/ui/compatible/ModernUIPaneFactory.java index 68b8950f7c..31774e90c9 100644 --- a/designer-base/src/main/java/com/fr/design/ui/compatible/ModernUIPaneFactory.java +++ b/designer-base/src/main/java/com/fr/design/ui/compatible/ModernUIPaneFactory.java @@ -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 参数 + * @return 构造器 + */ public static ModernUIPane.Builder 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; } } diff --git a/designer-base/src/main/java/com/fr/design/ui/compatible/NewModernUIPane.java b/designer-base/src/main/java/com/fr/design/ui/compatible/NewModernUIPane.java deleted file mode 100644 index 59df782cde..0000000000 --- a/designer-base/src/main/java/com/fr/design/ui/compatible/NewModernUIPane.java +++ /dev/null @@ -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 extends ModernUIPane { - - 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 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 extends ModernUIPane.Builder { - - private NewModernUIPane pane = new NewModernUIPane<>(); - - public Builder() { - super((ModernUIPane)null); - } - - public NewModernUIPane.Builder prepare(InjectJsCallback callback) { - pane.browser.set(InjectJsCallback.class, callback); - return this; - } - - /** - * 加载jar包中的资源 - * - * @param path 资源路径 - */ - @Override - public NewModernUIPane.Builder 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 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 withURL(final String url, Map 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 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 withComponent(AssembleComponent component, Map 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 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 namespace(String namespace) { - pane.namespace = namespace; - return this; - } - - /** - * java端往js端传数据时使用的变量名字 - * - * @param name 变量的名字 - */ - @Override - public NewModernUIPane.Builder variable(String name) { - pane.variable = name; - return this; - } - - /** - * js端往java端传数据时执行的函数表达式 - * - * @param expression 函数表达式 - */ - @Override - public NewModernUIPane.Builder expression(String expression) { - pane.expression = expression; - return this; - } - - @Override - public NewModernUIPane.Builder prepareForV6(ScriptContextListener contextListener) { - // do nothing - return this; - } - - @Override - public NewModernUIPane.Builder prepareForV6(LoadListener loadListener) { - // do nothing - return this; - } - - @Override - public NewModernUIPane.Builder prepareForV7(InjectJsCallback callback) { - return prepare(callback); - } - - @Override - public ModernUIPane.Builder prepareForV7(Class event, Observer listener) { - - pane.browser.navigation().on(event, listener); - - return this; - } - - @Override - public NewModernUIPane build() { - return pane; - } - } -} diff --git a/designer-base/src/main/java/com/fr/design/ui/compatible/NxComplexInterceptRequestCallback.java b/designer-base/src/main/java/com/fr/design/ui/compatible/NxComplexInterceptRequestCallback.java deleted file mode 100644 index eb85495d40..0000000000 --- a/designer-base/src/main/java/com/fr/design/ui/compatible/NxComplexInterceptRequestCallback.java +++ /dev/null @@ -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 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 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(""); - } - } - 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(""); - } - } - result = result.replaceAll("##script##", scriptText.toString()); - if (map != null) { - for (Map.Entry entry : map.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - result = result.replaceAll("\\$\\{" + key + "}", value); - } - } - return result; - } -} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/ui/compatible/NxInterceptRequestCallback.java b/designer-base/src/main/java/com/fr/design/ui/compatible/NxInterceptRequestCallback.java deleted file mode 100644 index 32a8f8b610..0000000000 --- a/designer-base/src/main/java/com/fr/design/ui/compatible/NxInterceptRequestCallback.java +++ /dev/null @@ -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 map; - - public NxInterceptRequestCallback() { - } - - public NxInterceptRequestCallback(Map map) { - this.map = map; - } - - @Override - public Response on(Params params) { - UrlRequest urlRequest = params.urlRequest(); - String path = urlRequest.url(); - if (path.startsWith("file:")) { - Optional 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 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 map) { - this.map = map; - } -} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java index e37877a4bb..1db0832c65 100644 --- a/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateDialog.java @@ -1,7 +1,7 @@ package com.fr.design.update.push; import com.fr.design.dialog.UIDialog; -import com.fr.design.ui.ModernUIPane; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.utils.BrowseUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.intelli.record.FocusPoint; @@ -14,20 +14,24 @@ import com.fr.web.struct.browser.RequestClient; import com.fr.web.struct.category.ScriptPath; import com.fr.web.struct.category.StylePath; import com.fr.web.struct.impl.FineUI; +import com.teamdev.jxbrowser.js.JsAccessible; import javax.swing.JPanel; -import javax.swing.SwingUtilities; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Frame; /** - * Created by plough on 2019/4/10. + * 设计器推送更新对话框 + * + * @author plough + * @since 10.0 + * Created on 2019/4/10. */ -class DesignerPushUpdateDialog extends UIDialog { +public class DesignerPushUpdateDialog extends UIDialog { public static final Dimension DEFAULT = new Dimension(640, 360); - private ModernUIPane jsPane; + private JxUIPane jsPane; private DesignerPushUpdateDialog(Frame parent) { super(parent); @@ -35,23 +39,23 @@ class DesignerPushUpdateDialog extends UIDialog { initComponents(); } - static void createAndShow(final Frame parent, final DesignerUpdateInfo updateInfo) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - DesignerPushUpdateDialog dialog = new DesignerPushUpdateDialog(parent); - dialog.populate(updateInfo); - dialog.showDialog(); - } - }); - + /** + * 创建并展示窗口 + * + * @param parent 父窗体 + * @param updateInfo 要展示的更新数据 + */ + public static void createAndShow(final Frame parent, final DesignerUpdateInfo updateInfo) { + DesignerPushUpdateDialog dialog = new DesignerPushUpdateDialog(parent); + dialog.populate(updateInfo); + dialog.showDialog(); } private void initComponents() { JPanel contentPane = (JPanel) getContentPane(); contentPane.setLayout(new BorderLayout()); - jsPane = new ModernUIPane.Builder() + jsPane = new JxUIPane.Builder() .withComponent(new AssembleComponent() { @Override public ScriptPath script(RequestClient req) { @@ -67,7 +71,7 @@ class DesignerPushUpdateDialog extends UIDialog { public Atom[] refer() { return new Atom[]{FineUI.KEY}; } - }).namespace("Pool").build(); + }).build(); contentPane.add(jsPane); } @@ -101,6 +105,7 @@ class DesignerPushUpdateDialog extends UIDialog { setVisible(true); } + @JsAccessible public class Model { private String version; private String content; @@ -163,6 +168,7 @@ class DesignerPushUpdateDialog extends UIDialog { exit(); } + @JsAccessible public String i18nText(String key) { return com.fr.design.i18n.Toolkit.i18nText(key); } @@ -180,9 +186,11 @@ class DesignerPushUpdateDialog extends UIDialog { private enum OperateType { CLOSE_WINDOW(0), UPDATE(1), REMIND_NEXT_TIME(2), SKIP(3); private int index; + OperateType(int index) { this.index = index; } + private String toText() { return String.valueOf(this.index); } diff --git a/designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java index 3f7c23cd30..47fbca1716 100644 --- a/designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java +++ b/designer-base/src/main/java/com/fr/design/update/push/DesignerUpdateInfo.java @@ -12,9 +12,13 @@ import com.fr.stable.StringUtils; import java.security.InvalidParameterException; /** - * Created by plough on 2019/4/8. + * 设计器更新信息bean + * + * @author plough + * @since 10.0 + * Created on 2019/4/8. */ -class DesignerUpdateInfo { +public class DesignerUpdateInfo { private static final String KEY_VERSION = "version"; private static final String KEY_CONTENT = "content"; private static final String KEY_BACKGROUND_URL = "background"; @@ -32,7 +36,7 @@ class DesignerUpdateInfo { private final String backgroundUrl; // 推送背景图片 url private final String moreInfoUrl; // 更多新特性 - DesignerUpdateInfo(String currentVersion, String latestVersion, String lastIgnoredVersion, JSONObject pushData) { + public DesignerUpdateInfo(String currentVersion, String latestVersion, String lastIgnoredVersion, JSONObject pushData) { this.currentVersion = currentVersion; this.latestVersion = latestVersion; this.latestFullVersion = initLatestFullVersion(); diff --git a/designer-base/src/main/java/com/fr/design/upm/UpmBridge.java b/designer-base/src/main/java/com/fr/design/upm/UpmBridge.java index 01369116d7..34ae1ac002 100644 --- a/designer-base/src/main/java/com/fr/design/upm/UpmBridge.java +++ b/designer-base/src/main/java/com/fr/design/upm/UpmBridge.java @@ -40,12 +40,13 @@ import com.teamdev.jxbrowser.chromium.Browser; import com.teamdev.jxbrowser.chromium.JSArray; import com.teamdev.jxbrowser.chromium.JSFunction; import com.teamdev.jxbrowser.chromium.JSObject; +import com.teamdev.jxbrowser.chromium.JSValue; -import java.awt.Desktop; import javax.swing.JFileChooser; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.filechooser.FileNameExtensionFilter; +import java.awt.Desktop; import java.io.File; import java.net.URI; import java.util.ArrayList; @@ -59,7 +60,9 @@ import java.util.concurrent.RunnableFuture; * @version 10.0 * Created by richie on 2019-04-12 * 桥接Java和JavaScript的类 + * @deprecated 用于jxbrowser6,下版本删除 */ +@Deprecated public class UpmBridge { public static UpmBridge getBridge(Browser browser) { @@ -78,6 +81,7 @@ public class UpmBridge { /** * 更新插件管理中心资源文件,这个方法仅仅是为了语义上的作用(更新) + * * @param callback 安装完成后的回调函数 */ @JSBridge @@ -96,6 +100,7 @@ public class UpmBridge { /** * 下载并安装插件管理中心的资源文件 + * * @param callback 安装完成后的回调函数 */ @JSBridge @@ -126,6 +131,7 @@ public class UpmBridge { /** * 获取upm的版本信息 + * * @return 版本信息 */ @JSBridge @@ -291,6 +297,18 @@ public class UpmBridge { PluginOperateUtils.setPluginActive(pluginID, jsCallback); } + /** + * 批量修改选中的插件的活跃状态 + * + * @param pluginIDs 要处理的插件ID + * @param callback 回调函数 + */ + @JSBridge + public void setAllPluginActive(JSArray pluginIDs, final JSFunction callback) { + JSCallback jsCallback = new JSCallback(UpmBrowserExecutor.create(window, callback)); + PluginOperateUtils.setPluginActive(pluginIDs, jsCallback); + } + /** * 选择文件对话框 * @@ -315,7 +333,7 @@ public class UpmBridge { @Override public String call() { FileChooserProvider fileChooserProvider = FileChooserFactory.createFileChooser( - FileChooserArgs.newBuilder(). + FileChooserArgs.newBuilder(). setFileSelectionMode(FileSelectionMode.FILE). setFilter(des, filter).build()); int result = fileChooserProvider.showDialog(UpmFinder.getDialog()); @@ -468,6 +486,7 @@ public class UpmBridge { /** * 使用系统浏览器打开网页 + * * @param url 要打开的网页 */ @JSBridge diff --git a/designer-base/src/main/java/com/fr/design/upm/NewUpmBridge.java b/designer-base/src/main/java/com/fr/design/upm/UpmBridgeV7.java similarity index 98% rename from designer-base/src/main/java/com/fr/design/upm/NewUpmBridge.java rename to designer-base/src/main/java/com/fr/design/upm/UpmBridgeV7.java index 6b9f1f85fc..a7714621b7 100644 --- a/designer-base/src/main/java/com/fr/design/upm/NewUpmBridge.java +++ b/designer-base/src/main/java/com/fr/design/upm/UpmBridgeV7.java @@ -45,15 +45,16 @@ import java.util.concurrent.RunnableFuture; * Created by richie on 2019-04-12 * 桥接Java和JavaScript的类 */ -public class NewUpmBridge extends UpmBridge { +@JsAccessible +public class UpmBridgeV7 extends UpmBridge { - public static NewUpmBridge getBridge(JsObject jsObject) { - return new NewUpmBridge(jsObject); + public static UpmBridgeV7 getBridge(JsObject jsObject) { + return new UpmBridgeV7(jsObject); } private JsObject jsObject; - private NewUpmBridge(JsObject jsObject) { + private UpmBridgeV7(JsObject jsObject) { this.jsObject = jsObject; } diff --git a/designer-base/src/main/java/com/fr/design/upm/UpmFinder.java b/designer-base/src/main/java/com/fr/design/upm/UpmFinder.java index 576c5a9b96..ede9b45d81 100644 --- a/designer-base/src/main/java/com/fr/design/upm/UpmFinder.java +++ b/designer-base/src/main/java/com/fr/design/upm/UpmFinder.java @@ -8,6 +8,7 @@ import com.fr.design.i18n.Toolkit; import com.fr.design.login.utils.DesignerLoginUtils; import com.fr.design.mainframe.DesignerContext; import com.fr.design.plugin.DesignerPluginContext; +import com.fr.design.ui.ModernUIConstants; import com.fr.design.update.ui.dialog.UpdateMainDialog; import com.fr.event.Event; import com.fr.event.EventDispatcher; @@ -57,7 +58,8 @@ public class UpmFinder { } public static String getMainResourcePath() { - return "file:///" + StableUtils.pathJoin(installHome, MAIN_RESOURCE_PATH); + return ModernUIConstants.EMB_TAG + ModernUIConstants.SCHEME_HEADER + + StableUtils.pathJoin(installHome, MAIN_RESOURCE_PATH); } public static UIDialog getDialog() { diff --git a/designer-base/src/main/java/com/fr/design/upm/UpmShowPane.java b/designer-base/src/main/java/com/fr/design/upm/UpmShowPane.java index 23a712508e..3035835cf1 100644 --- a/designer-base/src/main/java/com/fr/design/upm/UpmShowPane.java +++ b/designer-base/src/main/java/com/fr/design/upm/UpmShowPane.java @@ -1,19 +1,13 @@ package com.fr.design.upm; import com.fr.design.dialog.BasicPane; -import com.fr.design.ui.ModernUIPane; -import com.fr.design.ui.compatible.ModernUIPaneFactory; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.upm.event.DownloadEvent; import com.fr.event.Event; import com.fr.event.EventDispatcher; import com.fr.event.Listener; -import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; -import com.teamdev.jxbrowser.chromium.JSValue; -import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; -import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; -import com.teamdev.jxbrowser.js.JsObject; -import java.awt.*; +import java.awt.BorderLayout; /** * @author richie @@ -23,7 +17,9 @@ import java.awt.*; */ public class UpmShowPane extends BasicPane { - private ModernUIPane modernUIPane; + public static final String PLUGIN_HELPER = "PluginHelper"; + + private final JxUIPane jxUIPane; @Override protected String title4PopupWindow() { @@ -32,28 +28,22 @@ public class UpmShowPane extends BasicPane { UpmShowPane() { setLayout(new BorderLayout()); - modernUIPane = new ModernUIPane.Builder<>() - .prepare(new ScriptContextAdapter() { - @Override - public void onScriptContextCreated(ScriptContextEvent event) { - JSValue window = event.getBrowser().executeJavaScriptAndReturnValue("window"); - window.asObject().setProperty("PluginHelper", UpmBridge.getBridge(event.getBrowser())); - } - }) + jxUIPane = new JxUIPane.Builder<>() + .bindWindow(PLUGIN_HELPER, UpmBridgeV7::getBridge) .withURL(UpmFinder.getMainResourcePath(), UpmUtils.renderMap()) .build(); EventDispatcher.listen(DownloadEvent.UPDATE, new Listener() { @Override public void on(Event event, String param) { - modernUIPane.redirect(UpmFinder.getMainResourcePath(), UpmUtils.renderMap()); + jxUIPane.redirect(UpmFinder.getMainResourcePath(), UpmUtils.renderMap()); } }); EventDispatcher.listen(DownloadEvent.UPDATE, new Listener() { @Override public void on(Event event, String param) { - modernUIPane.redirect(UpmFinder.getMainResourcePath(), UpmUtils.renderMap()); + jxUIPane.redirect(UpmFinder.getMainResourcePath(), UpmUtils.renderMap()); } }); - add(modernUIPane, BorderLayout.CENTER); + add(jxUIPane, BorderLayout.CENTER); } } diff --git a/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java b/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java index 4779f91916..bfba512a20 100644 --- a/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java @@ -66,8 +66,8 @@ import java.util.concurrent.TimeoutException; * Some util method of Designer */ public class DesignUtils { - private static int port = DesignerPort.getInstance().getMessagePort(); + private static Integer port; private static boolean started = false; @@ -80,6 +80,9 @@ public class DesignUtils { } public synchronized static int getPort() { + if (port == null) { + setPort(DesignerPort.getInstance().getMessagePort()); + } return port; } @@ -93,7 +96,6 @@ public class DesignUtils { return started; } - /** * 判断设计器端口是否被其他程序占用 * 尝试去通信,无回应就是其他程序占用端口,否则需要继续判断是否为设计器进程未关闭 @@ -103,7 +105,7 @@ public class DesignUtils { public static boolean isPortOccupied() { ExecutorService executor = null; Future future = null; - try (Socket socket = new Socket("localhost", port); + try (Socket socket = new Socket("localhost", getPort()); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)))) { writer.println("check"); @@ -167,7 +169,7 @@ public class DesignUtils { if (lines == null || lines.length == 0) { return; } - try (Socket socket = new Socket("localhost", port)) { + try (Socket socket = new Socket("localhost", getPort())) { clientSend(lines, socket); } catch (Exception ignore) { @@ -190,7 +192,7 @@ public class DesignUtils { try { serverSocket = new ServerSocket(startPort); } catch (IOException e1) { - FineLoggerFactory.getLogger().error("Cannot create server socket on " + port); + FineLoggerFactory.getLogger().error("Cannot create server socket on " + getPort()); } while (true) { try { @@ -221,7 +223,7 @@ public class DesignUtils { @Override public void run() { DesignerStartupContext context = DesignerStartupContext.getInstance(); - + // 如果在启动页展示中 if (DesignerStartupUtil.openTemplateIfOnWaiting(f)) { return; @@ -231,7 +233,7 @@ public class DesignUtils { // 之前就有这样的问题 return; } - + // 打开模板 DesignerContext.getDesignerFrame().openTemplate(new FileFILE(f)); } @@ -250,7 +252,7 @@ public class DesignUtils { reader.close(); socket.close(); } else { - FineLoggerFactory.getLogger().error("Cannot create server socket on " + port); + FineLoggerFactory.getLogger().error("Cannot create server socket on " + getPort()); break; } } catch (IOException ignored) { @@ -484,6 +486,7 @@ public class DesignUtils { /** * 获取设计器可用字体 + * * @return */ public static String[] getAvailableFontFamilyNames4Report() { diff --git a/designer-base/src/main/java/com/fr/env/RemoteEnvPane.java b/designer-base/src/main/java/com/fr/env/RemoteEnvPane.java index 31bdaa979b..7be733b2b5 100644 --- a/designer-base/src/main/java/com/fr/env/RemoteEnvPane.java +++ b/designer-base/src/main/java/com/fr/env/RemoteEnvPane.java @@ -5,6 +5,7 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.beans.BasicBeanPane; import com.fr.design.border.UITitledBorder; import com.fr.design.env.RemoteDesignerWorkspaceInfo; +import com.fr.design.env.processor.RemoteDesignerWorkspaceInfoProcessor; import com.fr.design.fun.DesignerEnvProcessor; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icheckbox.UICheckBox; @@ -579,8 +580,14 @@ public class RemoteEnvPane extends BasicBeanPane { private void tryConnectRemoteEnv() { final RemoteDesignerWorkspaceInfo remoteEnv = updateBean(); - final WorkspaceConnectionInfo connection = remoteEnv.getConnection(); - + WorkspaceConnectionInfo originalConnection = remoteEnv.getConnection(); + final WorkspaceConnectionInfo connection; + RemoteDesignerWorkspaceInfoProcessor processor = ExtraDesignClassManager.getInstance().getSingle(RemoteDesignerWorkspaceInfoProcessor.XML_TAG); + if (processor != null) { + connection = processor.customUserName(originalConnection); + } else { + connection = originalConnection; + } final SwingWorker worker = new SwingWorker() { @Override diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index 48dfecd7b3..f7ede9f1a0 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -163,6 +163,7 @@ public class EnvDetectorDialog extends JDialog { } }; detectButton.setForeground(Color.WHITE); + detectButton.setToolTipText(buttonStatus.getDesc()); detectButton.addActionListener(event -> { if (buttonStatus.isNotExecuting()) { startDetecting(); @@ -284,6 +285,7 @@ public class EnvDetectorDialog extends JDialog { UIUtil.invokeLaterIfNeeded(() -> { // 刷新按钮 detectButton.setText(buttonStatus.getDesc()); + detectButton.setToolTipText(detectButton.getText()); // 刷新面板 refreshBody(); }); @@ -293,6 +295,7 @@ public class EnvDetectorDialog extends JDialog { // 刷新按钮 detectButton.setText(buttonStatus.getDesc()); + detectButton.setToolTipText(detectButton.getText()); if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { this.resultSummaryPane = new JPanel(); this.resultSummaryPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); @@ -358,6 +361,7 @@ public class EnvDetectorDialog extends JDialog { } EnvDetectorItem item = items.get(i); tablePanel.updateCell(row, 2, new UILabel(item.getDescription())); + tablePanel.updateCellToolTip(row, 2, item.getDescription()); DetectorResult result = item.getResult(); int detectRow = currentDetectIndex + 1; diff --git a/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg.png b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg.png index 9e731c557b..a302e257b2 100644 Binary files a/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg.png and b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_en.png b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_en.png new file mode 100644 index 0000000000..a302e257b2 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_en.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_zh.png b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_zh.png new file mode 100644 index 0000000000..55419d0a68 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_zh.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_zh_TW.png b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_zh_TW.png new file mode 100644 index 0000000000..6f9aa1036f Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget/picture_widget_designer_bg_zh_TW.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading.gif b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading.gif new file mode 100644 index 0000000000..ce2172ba41 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_en.gif b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_en.gif new file mode 100644 index 0000000000..26870d749c Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_en.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_zh.gif b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_zh.gif new file mode 100644 index 0000000000..c77b4fe100 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_zh.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_zh_TW.gif b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_zh_TW.gif new file mode 100644 index 0000000000..c78edd7a9c Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/mainframe/loading/loading_zh_TW.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/login/guide.css b/designer-base/src/main/resources/com/fr/design/login/guide.css deleted file mode 100644 index 12af5f6c3b..0000000000 --- a/designer-base/src/main/resources/com/fr/design/login/guide.css +++ /dev/null @@ -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; -} - diff --git a/designer-base/src/main/resources/com/fr/design/login/guide.html b/designer-base/src/main/resources/com/fr/design/login/guide.html index aada842a10..bafec74c30 100644 --- a/designer-base/src/main/resources/com/fr/design/login/guide.html +++ b/designer-base/src/main/resources/com/fr/design/login/guide.html @@ -15,7 +15,7 @@ - + diff --git a/designer-chart/src/main/resources/com/fr/design/images/en_us_emptydata.png b/designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata.png similarity index 100% rename from designer-chart/src/main/resources/com/fr/design/images/en_us_emptydata.png rename to designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata.png diff --git a/designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_en.png b/designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_en.png new file mode 100644 index 0000000000..46597ea7a1 Binary files /dev/null and b/designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_en.png differ diff --git a/designer-chart/src/main/resources/com/fr/design/images/zh_cn_emptydata.png b/designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_zh.png similarity index 100% rename from designer-chart/src/main/resources/com/fr/design/images/zh_cn_emptydata.png rename to designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_zh.png diff --git a/designer-chart/src/main/resources/com/fr/design/images/zh_tw_emptydata.png b/designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_zh_TW.png similarity index 100% rename from designer-chart/src/main/resources/com/fr/design/images/zh_tw_emptydata.png rename to designer-chart/src/main/resources/com/fr/design/images/emptydata/emptydata_zh_TW.png diff --git a/designer-chart/src/main/resources/com/fr/van/chart/background/background.png b/designer-chart/src/main/resources/com/fr/van/chart/background/background.png new file mode 100644 index 0000000000..33f81790b9 Binary files /dev/null and b/designer-chart/src/main/resources/com/fr/van/chart/background/background.png differ diff --git a/designer-chart/src/main/resources/com/fr/van/chart/background/background_en.png b/designer-chart/src/main/resources/com/fr/van/chart/background/background_en.png new file mode 100644 index 0000000000..33f81790b9 Binary files /dev/null and b/designer-chart/src/main/resources/com/fr/van/chart/background/background_en.png differ diff --git a/designer-chart/src/main/resources/com/fr/van/chart/background.png b/designer-chart/src/main/resources/com/fr/van/chart/background/background_zh.png similarity index 100% rename from designer-chart/src/main/resources/com/fr/van/chart/background.png rename to designer-chart/src/main/resources/com/fr/van/chart/background/background_zh.png diff --git a/designer-chart/src/main/resources/com/fr/van/chart/background/background_zh_TW.png b/designer-chart/src/main/resources/com/fr/van/chart/background/background_zh_TW.png new file mode 100644 index 0000000000..794af8550c Binary files /dev/null and b/designer-chart/src/main/resources/com/fr/van/chart/background/background_zh_TW.png differ diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XAutoChartCreator.java b/designer-form/src/main/java/com/fr/design/designer/creator/XAutoChartCreator.java index 4c5b669b48..7d172d6c7a 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XAutoChartCreator.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XAutoChartCreator.java @@ -12,13 +12,14 @@ import com.fr.design.mainframe.EditingMouseListener; import com.fr.design.mainframe.FormDesigner; import com.fr.design.module.DesignModuleFactory; import com.fr.form.ui.ChartAutoEditor; -import com.fr.general.IOUtils; +import com.fr.general.locale.image.I18nImage; import com.fr.stable.Constants; import com.fr.stable.bridge.StableFactory; import javax.swing.JComponent; import javax.swing.JPanel; -import java.awt.*; +import java.awt.Dimension; +import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; @@ -38,6 +39,8 @@ public class XAutoChartCreator extends XChartEditor { super(editor, size); } + private static final String AUTO_CHART_IMAGE_PATH = "com/fr/design/form/images/autochartpreview/auto_chart_preview.png"; + /** * 返回组件默认名 * @@ -103,7 +106,7 @@ public class XAutoChartCreator extends XChartEditor { @Override public void paintForeground(Graphics2D g) { - BufferedImage bufferedImage = IOUtils.readImage("com/fr/design/form/images/auto_chart_preview.png"); + BufferedImage bufferedImage = I18nImage.getImage(AUTO_CHART_IMAGE_PATH); GraphHelper.paintImage( g, this.getWidth(), this.getHeight(), bufferedImage, Constants.IMAGE_ADJUST, diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XNumberEditor.java b/designer-form/src/main/java/com/fr/design/designer/creator/XNumberEditor.java index fc359976e2..12bed66f01 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XNumberEditor.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XNumberEditor.java @@ -102,4 +102,8 @@ public class XNumberEditor extends XWrapperedFieldEditor { return "number_field_16.png"; } +// @Override +// public WidgetPropertyUIProvider[] getWidgetPropertyUIProviders() { +// return new WidgetPropertyUIProvider[]{new NumberEditorMobilePropertyUI(this)}; +// } } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XPassword.java b/designer-form/src/main/java/com/fr/design/designer/creator/XPassword.java index 4194e4be03..f3d6214bfc 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XPassword.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XPassword.java @@ -68,4 +68,8 @@ public class XPassword extends XWrapperedFieldEditor { (CRPropertyDescriptor[]) ArrayUtils.addAll(sup, new CRPropertyDescriptor[]{regex, waterMark}); } +// @Override +// public WidgetPropertyUIProvider[] getWidgetPropertyUIProviders() { +// return new WidgetPropertyUIProvider[]{new PasswordMobilePropertyUI(this)}; +// } } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XPicture.java b/designer-form/src/main/java/com/fr/design/designer/creator/XPicture.java index 7dd23bd4ce..32bf212037 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XPicture.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XPicture.java @@ -9,6 +9,7 @@ import com.fr.design.mainframe.widget.renderer.PictureRenderer; import com.fr.design.mainframe.widget.renderer.UrlLinkRenderer; import com.fr.form.ui.PictureWidget; import com.fr.general.IOUtils; +import com.fr.general.locale.image.I18nImage; import com.fr.stable.ArrayUtils; import java.awt.Dimension; import java.awt.Image; @@ -59,7 +60,7 @@ public class XPicture extends XWidgetCreator { imgPanel.setBackgroundImage((Image) value); imgPanel.setImageDisplayMode(pictureWidget.getShowType()); } else { - imgPanel.setBackgroundImage(IOUtils.readImage("com/fr/design/images/form/designer/widget/picture_widget_designer_bg.png")); + imgPanel.setBackgroundImage(I18nImage.getImage("/com/fr/design/images/form/designer/widget/picture_widget_designer_bg.png")); imgPanel.setImageDisplayMode(0); } this.editor.add(imgPanel, "Center"); diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XTextArea.java b/designer-form/src/main/java/com/fr/design/designer/creator/XTextArea.java index 845fe43d9e..e5702c81f2 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XTextArea.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XTextArea.java @@ -25,8 +25,9 @@ import java.beans.IntrospectionException; /** * @author richer * @since 6.5.3 + * Created on 2016/3/29 */ -public class XTextArea extends XFieldEditor { +public class XTextArea extends XWrapperedFieldEditor { public XTextArea(TextArea widget, Dimension initSize) { super(widget, initSize); @@ -81,4 +82,10 @@ public class XTextArea extends XFieldEditor { protected String getIconName() { return "text_area_16.png"; } + +// @Override +// public WidgetPropertyUIProvider[] getWidgetPropertyUIProviders() { +// return new WidgetPropertyUIProvider[]{new TextAreaMobilePropertyUI(this)}; +// } + } diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XTextEditor.java b/designer-form/src/main/java/com/fr/design/designer/creator/XTextEditor.java index 00daae7c7d..a1aca00ed0 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XTextEditor.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XTextEditor.java @@ -6,7 +6,7 @@ package com.fr.design.designer.creator; import com.fr.base.BaseUtils; import com.fr.base.ScreenResolution; import com.fr.base.Style; -import com.fr.design.designer.properties.mobile.ScanCodeMobilePropertyUI; +import com.fr.design.designer.properties.mobile.TextEditorMobilePropertyUI; import com.fr.design.form.util.XCreatorConstants; import com.fr.design.fun.WidgetPropertyUIProvider; import com.fr.design.mainframe.widget.editors.RegexEditor; @@ -86,6 +86,6 @@ public class XTextEditor extends XWrapperedFieldEditor { @Override public WidgetPropertyUIProvider[] getWidgetPropertyUIProviders() { - return new WidgetPropertyUIProvider[] {new ScanCodeMobilePropertyUI(this)}; + return new WidgetPropertyUIProvider[]{new TextEditorMobilePropertyUI(this)}; } } diff --git a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/NumberEditorMobilePropertyUI.java b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/NumberEditorMobilePropertyUI.java new file mode 100644 index 0000000000..67a8569f72 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/NumberEditorMobilePropertyUI.java @@ -0,0 +1,37 @@ +package com.fr.design.designer.properties.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.dialog.BasicPane; +import com.fr.design.fun.impl.AbstractWidgetPropertyUIProvider; +import com.fr.design.gui.itable.AbstractPropertyTable; +import com.fr.design.widget.ui.designer.mobile.NumberEditorMobileDefinePane; + +/** + * 数字控件属性面板注册 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class NumberEditorMobilePropertyUI extends AbstractWidgetPropertyUIProvider { + private XCreator xCreator; + + public NumberEditorMobilePropertyUI(XCreator xCreator) { + this.xCreator = xCreator; + } + + @Override + public AbstractPropertyTable createWidgetAttrTable() { + return null; + } + + @Override + public BasicPane createWidgetAttrPane() { + return new NumberEditorMobileDefinePane(xCreator); + } + + @Override + public String tableTitle() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Mobile_Attr"); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/PasswordMobilePropertyUI.java b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/PasswordMobilePropertyUI.java new file mode 100644 index 0000000000..2c3b3b2270 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/PasswordMobilePropertyUI.java @@ -0,0 +1,37 @@ +package com.fr.design.designer.properties.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.dialog.BasicPane; +import com.fr.design.fun.impl.AbstractWidgetPropertyUIProvider; +import com.fr.design.gui.itable.AbstractPropertyTable; +import com.fr.design.widget.ui.designer.mobile.PasswordMobileDefinePane; + +/** + * 密码控件移动端属性注册 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class PasswordMobilePropertyUI extends AbstractWidgetPropertyUIProvider { + private XCreator xCreator; + + public PasswordMobilePropertyUI(XCreator xCreator) { + this.xCreator = xCreator; + } + + @Override + public AbstractPropertyTable createWidgetAttrTable() { + return null; + } + + @Override + public BasicPane createWidgetAttrPane() { + return new PasswordMobileDefinePane(xCreator); + } + + @Override + public String tableTitle() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Mobile_Attr"); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/TextAreaMobilePropertyUI.java b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/TextAreaMobilePropertyUI.java new file mode 100644 index 0000000000..1c24e06bce --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/TextAreaMobilePropertyUI.java @@ -0,0 +1,37 @@ +package com.fr.design.designer.properties.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.dialog.BasicPane; +import com.fr.design.fun.impl.AbstractWidgetPropertyUIProvider; +import com.fr.design.gui.itable.AbstractPropertyTable; +import com.fr.design.widget.ui.designer.mobile.TextAreaAdvancedDefinePane; + +/** + * 文本域控件移动端属性 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/29 + */ +public class TextAreaMobilePropertyUI extends AbstractWidgetPropertyUIProvider { + private XCreator xCreator; + + public TextAreaMobilePropertyUI(XCreator xCreator) { + this.xCreator = xCreator; + } + + @Override + public AbstractPropertyTable createWidgetAttrTable() { + return null; + } + + @Override + public BasicPane createWidgetAttrPane() { + return new TextAreaAdvancedDefinePane(xCreator); + } + + @Override + public String tableTitle() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Mobile_Attr"); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/ScanCodeMobilePropertyUI.java b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/TextEditorMobilePropertyUI.java similarity index 65% rename from designer-form/src/main/java/com/fr/design/designer/properties/mobile/ScanCodeMobilePropertyUI.java rename to designer-form/src/main/java/com/fr/design/designer/properties/mobile/TextEditorMobilePropertyUI.java index 85011ee2b9..531e2c4fc8 100644 --- a/designer-form/src/main/java/com/fr/design/designer/properties/mobile/ScanCodeMobilePropertyUI.java +++ b/designer-form/src/main/java/com/fr/design/designer/properties/mobile/TextEditorMobilePropertyUI.java @@ -5,13 +5,20 @@ import com.fr.design.designer.creator.XTextEditor; import com.fr.design.dialog.BasicPane; import com.fr.design.fun.impl.AbstractWidgetPropertyUIProvider; import com.fr.design.gui.itable.AbstractPropertyTable; -import com.fr.design.widget.ui.designer.mobile.ScanCodeMobileDefinePane; +import com.fr.design.widget.ui.designer.mobile.TextEditorMobileDefinePane; -public class ScanCodeMobilePropertyUI extends AbstractWidgetPropertyUIProvider { +/** + * 文本控件 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class TextEditorMobilePropertyUI extends AbstractWidgetPropertyUIProvider { private XCreator xCreator; - public ScanCodeMobilePropertyUI(XTextEditor xTextEditor) { + public TextEditorMobilePropertyUI(XTextEditor xTextEditor) { this.xCreator = xTextEditor; } @@ -22,7 +29,7 @@ public class ScanCodeMobilePropertyUI extends AbstractWidgetPropertyUIProvider { @Override public BasicPane createWidgetAttrPane() { - return new ScanCodeMobileDefinePane(xCreator); + return new TextEditorMobileDefinePane(xCreator); } @Override diff --git a/designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/MiniComponentShopPane.java b/designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/MiniComponentShopPane.java index f9d3849f78..ccd13cc4a4 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/MiniComponentShopPane.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/share/ui/online/mini/MiniComponentShopPane.java @@ -1,19 +1,12 @@ package com.fr.design.mainframe.share.ui.online.mini; +import com.fr.design.jxbrowser.JxUIPane; import com.fr.design.mainframe.share.ui.online.mini.bridge.ComposedNativeBridges; import com.fr.design.mainframe.share.util.OnlineShopUtils; -import com.fr.design.ui.ModernUIPane; -import com.fr.design.ui.compatible.ModernUIPaneFactory; import com.fr.design.upm.event.CertificateEvent; import com.fr.event.Event; import com.fr.event.EventDispatcher; import com.fr.event.Listener; -import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; -import com.teamdev.jxbrowser.chromium.JSObject; -import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; -import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; -import com.teamdev.jxbrowser.js.JsObject; -import org.jetbrains.annotations.Nullable; import javax.swing.JPanel; import java.awt.BorderLayout; @@ -24,34 +17,16 @@ import java.awt.BorderLayout; * Created by Starryi on 2021/12/20 */ public class MiniComponentShopPane extends JPanel { - private final ModernUIPane modernUIPane; + private static final String SHOP_HELPER = "ShopHelper"; + private final JxUIPane modernUIPane; private final Listener loginListener; private final Listener logoutListener; public MiniComponentShopPane() { setLayout(new BorderLayout()); - modernUIPane = ModernUIPaneFactory.modernUIPaneBuilder() + modernUIPane = new JxUIPane.Builder<>() .withURL(OnlineShopUtils.getWebMiniShopPath()) - .prepareForV6(new ScriptContextAdapter() { - @Override - public void onScriptContextCreated(ScriptContextEvent event) { - super.onScriptContextCreated(event); - JSObject window = event.getBrowser().executeJavaScriptAndReturnValue("window").asObject(); - window.setProperty("ShopHelper", new ComposedNativeBridges(window)); - } - }) - .prepareForV7(new InjectJsCallback() { - @Nullable - @Override - public Response on(Params params) { - // 7.x - JsObject window = params.frame().executeJavaScript("window"); - if (window != null) { - window.putProperty("ShopHelper", new ComposedNativeBridges(window)); - } - return InjectJsCallback.Response.proceed(); - } - }) + .bindWindow(SHOP_HELPER, ComposedNativeBridges::new) .build(); add(modernUIPane, BorderLayout.CENTER); diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BaseTextEditorMobileDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BaseTextEditorMobileDefinePane.java new file mode 100644 index 0000000000..57faab6a72 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/BaseTextEditorMobileDefinePane.java @@ -0,0 +1,94 @@ +package com.fr.design.widget.ui.designer.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.foldablepane.UIExpandablePane; +import com.fr.design.gui.frpane.AttributeChangeListener; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.widget.ui.designer.mobile.component.MobileTextEditSettingPane; +import com.fr.form.ui.TextEditor; + +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.FlowLayout; + +/** + *

    文本类组件移动端高级属性的定义面板,基础扩展可以直接继承此面板 + *

    往内部添加其他配置 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class BaseTextEditorMobileDefinePane extends MobileWidgetDefinePane { + + private XCreator xCreator; + protected MobileTextEditSettingPane textSettingPane; + + public BaseTextEditorMobileDefinePane(XCreator xCreator) { + this.xCreator = xCreator; + } + + @Override + public void initPropertyGroups(Object source) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + JPanel container = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEADING, 0, 5); + addPropertyPanesToContainer(container); + this.add(new UIExpandablePane(Toolkit.i18nText("Fine-Design_Report_Advanced"), 280, 20, container), BorderLayout.NORTH); + this.repaint(); + } + + /** + * 添加其他属性面板 + * + * @param container 展开容器 + */ + protected void addPropertyPanesToContainer(JPanel container) { + initSettingPane(container); + } + + /** + * 初始化文本类基础面板 + * + * @param container + */ + protected void initSettingPane(JPanel container) { + textSettingPane = new MobileTextEditSettingPane(); + container.add(textSettingPane); + } + + @Override + public void populate(FormDesigner designer) { + TextEditor textEditor = (TextEditor) xCreator.toData(); + textSettingPane.populateBean(textEditor.getMobileTextEditAttr()); + this.bindListeners2Widgets(); + } + + protected void bindListeners2Widgets() { + reInitAllListeners(); + AttributeChangeListener changeListener = new AttributeChangeListener() { + @Override + public void attributeChange() { + update(); + } + }; + this.addAttributeChangeListener(changeListener); + } + + private void reInitAllListeners() { + initListener(this); + } + + @Override + public void update() { + TextEditor textEditor = (TextEditor) xCreator.toData(); + textSettingPane.updateBean(textEditor.getMobileTextEditAttr()); + DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); + } + + public XCreator getxCreator() { + return xCreator; + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ElementCaseDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ElementCaseDefinePane.java index bdd5b7d351..b9c84e5be7 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ElementCaseDefinePane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ElementCaseDefinePane.java @@ -22,7 +22,6 @@ import com.fr.design.mainframe.WidgetPropertyPane; import com.fr.design.mainframe.mobile.ui.MobileCollapsedStyleExpandPane; import com.fr.design.mainframe.mobile.ui.MobileComboBoxDialogEditor; import com.fr.form.ui.ElementCaseEditor; - import com.fr.form.ui.mobile.MobileCollapsedStyle; import com.fr.stable.StringUtils; @@ -30,7 +29,9 @@ import javax.swing.BorderFactory; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingConstants; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; /** * 报表块-移动端属性面板 @@ -123,7 +124,6 @@ public class ElementCaseDefinePane extends MobileWidgetDefinePane { panelWrapper.add(panel, BorderLayout.NORTH); UIExpandablePane folderPane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Fit"), 280, 20, panelWrapper); this.add(folderPane, BorderLayout.NORTH); - this.bingListeners2Widgets(); this.setGlobalNames(); this.repaint(); } @@ -165,7 +165,6 @@ public class ElementCaseDefinePane extends MobileWidgetDefinePane { @Override public void populate(FormDesigner designer) { this.designer = designer; - this.addAttributeChangeListener(changeListener); ElementCaseEditor elementCaseEditor = (ElementCaseEditor) xCreator.toData(); this.hComboBox.setSelectedItem(new Item(elementCaseEditor.getHorziontalAttr().description(), elementCaseEditor.getHorziontalAttr())); this.vComboBox.setSelectedItem(new Item(elementCaseEditor.getVerticalAttr().description(), elementCaseEditor.getVerticalAttr())); @@ -178,6 +177,8 @@ public class ElementCaseDefinePane extends MobileWidgetDefinePane { this.mobileCollapsedStyleEditor.setStyle(elementCaseEditor.getMobileCollapsedStyle()); fix(elementCaseEditor.getMobileCollapsedStyle()); this.mobileCollapsedStyleEditor.setSelected(elementCaseEditor.getMobileCollapsedStyle().isCollapsedWork()); + this.bingListeners2Widgets(); + this.addAttributeChangeListener(changeListener); } /** @@ -191,7 +192,6 @@ public class ElementCaseDefinePane extends MobileWidgetDefinePane { @Override public void update() { - DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); // 触发设计器保存按钮亮起来 String globalName = this.getGlobalName(); switch (globalName) { case "hComboBox": @@ -220,6 +220,7 @@ public class ElementCaseDefinePane extends MobileWidgetDefinePane { style.setCollapsedWork(this.mobileCollapsedStyleEditor.isSelectedCustom() && !FormDesignerUtils.isInAbsoluteLayout(xCreator)); ((ElementCaseEditor) xCreator.toData()).setMobileCollapsedStyle(style); } + DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); // 触发设计器保存按钮亮起来 } private void setGlobalNames() { diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/NumberEditorMobileDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/NumberEditorMobileDefinePane.java new file mode 100644 index 0000000000..75c6dbefc0 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/NumberEditorMobileDefinePane.java @@ -0,0 +1,33 @@ +package com.fr.design.widget.ui.designer.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.FormDesigner; +import com.fr.form.ui.NumberEditor; + +/** + * 数字控件 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class NumberEditorMobileDefinePane extends BaseTextEditorMobileDefinePane { + public NumberEditorMobileDefinePane(XCreator xCreator) { + super(xCreator); + } + + @Override + public void populate(FormDesigner designer) { + NumberEditor numberEditor = (NumberEditor) getxCreator().toData(); + textSettingPane.populateBean(numberEditor.getMobileTextEditAttr()); + this.bindListeners2Widgets(); + } + + @Override + public void update() { + NumberEditor numberEditor = (NumberEditor) getxCreator().toData(); + textSettingPane.updateBean(numberEditor.getMobileTextEditAttr()); + DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/PasswordMobileDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/PasswordMobileDefinePane.java new file mode 100644 index 0000000000..68c878b459 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/PasswordMobileDefinePane.java @@ -0,0 +1,25 @@ +package com.fr.design.widget.ui.designer.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.widget.ui.designer.mobile.component.MobilePasswordEditSettingPane; + +import javax.swing.JPanel; + +/** + * 密码控件移动端高级属性 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class PasswordMobileDefinePane extends BaseTextEditorMobileDefinePane { + public PasswordMobileDefinePane(XCreator xCreator) { + super(xCreator); + } + + @Override + protected void initSettingPane(JPanel container) { + textSettingPane = new MobilePasswordEditSettingPane(); + container.add(textSettingPane); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ScanCodeMobileDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ScanCodeMobileDefinePane.java deleted file mode 100644 index 71c35574a3..0000000000 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/ScanCodeMobileDefinePane.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.fr.design.widget.ui.designer.mobile; - -import com.fr.base.mobile.MobileScanCodeAttr; -import com.fr.design.designer.creator.XCreator; -import com.fr.design.foldablepane.UIExpandablePane; -import com.fr.design.gui.frpane.AttributeChangeListener; -import com.fr.design.i18n.Toolkit; -import com.fr.design.layout.FRGUIPaneFactory; -import com.fr.design.mainframe.DesignerContext; -import com.fr.design.mainframe.FormDesigner; -import com.fr.design.widget.ui.designer.mobile.component.MobileTextFieldInputSettingPane; -import com.fr.form.ui.TextEditor; - -import java.awt.BorderLayout; - - -public class ScanCodeMobileDefinePane extends MobileWidgetDefinePane { - - private XCreator xCreator; - private MobileTextFieldInputSettingPane settingPane; - - public ScanCodeMobileDefinePane(XCreator xCreator) { - this.xCreator = xCreator; - } - - @Override - public void initPropertyGroups(Object source) { - this.setLayout(FRGUIPaneFactory.createBorderLayout()); - settingPane = new MobileTextFieldInputSettingPane(); - this.add(new UIExpandablePane(Toolkit.i18nText("Fine-Design_Report_Advanced"), 280, 20, settingPane), BorderLayout.NORTH); - this.repaint(); - } - - private void bindListeners2Widgets() { - reInitAllListeners(); - AttributeChangeListener changeListener = new AttributeChangeListener() { - @Override - public void attributeChange() { - update(); - } - }; - this.addAttributeChangeListener(changeListener); - } - - private void reInitAllListeners() { - initListener(this); - } - - @Override - public void populate(FormDesigner designer) { - MobileScanCodeAttr mobileScanCodeAttr = ((TextEditor) xCreator.toData()).getMobileScanCodeAttr(); - settingPane.populateBean(mobileScanCodeAttr); - this.bindListeners2Widgets(); - } - - @Override - public void update() { - MobileScanCodeAttr mobileScanCodeAttr = ((TextEditor) xCreator.toData()).getMobileScanCodeAttr(); - settingPane.updateBean(mobileScanCodeAttr); - DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); - } - -} diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TextAreaAdvancedDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TextAreaAdvancedDefinePane.java new file mode 100644 index 0000000000..ccc3194f9f --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TextAreaAdvancedDefinePane.java @@ -0,0 +1,25 @@ +package com.fr.design.widget.ui.designer.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.widget.ui.designer.mobile.component.MobileTextAreaSettingPane; + +import javax.swing.JPanel; + +/** + * 文本域控件 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/29 + */ +public class TextAreaAdvancedDefinePane extends BaseTextEditorMobileDefinePane { + public TextAreaAdvancedDefinePane(XCreator xCreator) { + super(xCreator); + } + + @Override + protected void initSettingPane(JPanel container) { + textSettingPane = new MobileTextAreaSettingPane(); + container.add(textSettingPane); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TextEditorMobileDefinePane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TextEditorMobileDefinePane.java new file mode 100644 index 0000000000..834c3fb51b --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/TextEditorMobileDefinePane.java @@ -0,0 +1,55 @@ +package com.fr.design.widget.ui.designer.mobile; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.widget.ui.designer.mobile.component.MobileTextFieldInputSettingPane; +import com.fr.form.ui.TextEditor; + +import javax.swing.JPanel; + +/** + * 文本控件移动端属性 + * 输入方式: + * 输入框属性: + * + * @author hades + * @since 11.0 + * Created on 2018/11/27 + */ +public class TextEditorMobileDefinePane extends BaseTextEditorMobileDefinePane { + /** + * 输入方式 + */ + private MobileTextFieldInputSettingPane inputSettingPane; + + public TextEditorMobileDefinePane(XCreator xCreator) { + super(xCreator); + } + + @Override + protected void initSettingPane(JPanel container) { + inputSettingPane = new MobileTextFieldInputSettingPane(); +// textSettingPane = new MobileTextEditSettingPane(); + container.add(inputSettingPane); +// container.add(textSettingPane); + } + + + @Override + public void populate(FormDesigner designer) { + TextEditor textEditor = (TextEditor) getxCreator().toData(); + inputSettingPane.populateBean(textEditor.getMobileScanCodeAttr()); +// textSettingPane.populateBean(textEditor.getMobileTextEditAttr()); + this.bindListeners2Widgets(); + } + + @Override + public void update() { + TextEditor textEditor = (TextEditor) getxCreator().toData(); + inputSettingPane.updateBean(textEditor.getMobileScanCodeAttr()); +// textSettingPane.updateBean(textEditor.getMobileTextEditAttr()); + DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); + } + +} diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobilePasswordEditSettingPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobilePasswordEditSettingPane.java new file mode 100644 index 0000000000..e90bf577e0 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobilePasswordEditSettingPane.java @@ -0,0 +1,38 @@ +package com.fr.design.widget.ui.designer.mobile.component; + +import com.fr.base.mobile.MobileTextEditAttr; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.i18n.Toolkit; + +import java.awt.BorderLayout; + +/** + * 密码控件编辑属性设置面板 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/30 + */ +public class MobilePasswordEditSettingPane extends MobileTextEditSettingPane { + // 显示密码 + private UICheckBox showPassword; + + public MobilePasswordEditSettingPane() { + super(); + showPassword = new UICheckBox(Toolkit.i18nText("Fine-Design_Mobile_Show_Password"), false); + this.add(showPassword, BorderLayout.NORTH); + } + + @Override + public void populateBean(MobileTextEditAttr ob) { + super.populateBean(ob); + // 要兼容处理一下,为null 的话赋默认值,默认开启 + this.showPassword.setSelected(ob.isShowPassword() == null || ob.isShowPassword()); + } + + @Override + public void updateBean(MobileTextEditAttr ob) { + super.updateBean(ob); + ob.setShowPassword(showPassword.isSelected()); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextAreaSettingPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextAreaSettingPane.java new file mode 100644 index 0000000000..bfcb185b70 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextAreaSettingPane.java @@ -0,0 +1,43 @@ +package com.fr.design.widget.ui.designer.mobile.component; + +import com.fr.base.mobile.MobileTextEditAttr; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.i18n.Toolkit; + +import java.awt.BorderLayout; + +/** + * 文本域控件 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/29 + */ +public class MobileTextAreaSettingPane extends MobileTextEditSettingPane { + // 显示字数统计 + private UICheckBox showWordCount; + + public MobileTextAreaSettingPane() { + super(); + showWordCount = new UICheckBox(Toolkit.i18nText("Fine-Design_Mobile_Show_Word_Count"), false); + this.add(showWordCount, BorderLayout.NORTH); + } + + @Override + protected boolean getClearDefaultState() { + return false; + } + + @Override + public void populateBean(MobileTextEditAttr ob) { + super.populateBean(ob); + // 要兼容处理一下,为null 的话赋默认值,默认不开启 + this.showWordCount.setSelected(ob.isShowWordCount() != null && ob.isShowWordCount()); + } + + @Override + public void updateBean(MobileTextEditAttr ob) { + super.updateBean(ob); + ob.setShowWordCount(showWordCount.isSelected()); + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextEditSettingPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextEditSettingPane.java new file mode 100644 index 0000000000..9b2758a2d2 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextEditSettingPane.java @@ -0,0 +1,59 @@ +package com.fr.design.widget.ui.designer.mobile.component; + +import com.fr.base.mobile.MobileTextEditAttr; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.VerticalFlowLayout; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; + +/** + * 文本类基础设置 + * + * @author Coral.Chen + * @since 11.0 + * Created on 2023/3/29 + */ +public class MobileTextEditSettingPane extends BasicBeanPane { + // 允许一键清空 + protected UICheckBox allowOneClickClear; + + public MobileTextEditSettingPane() { + initLayout(); + allowOneClickClear = new UICheckBox(Toolkit.i18nText("Fine-Design_Mobile_Allow_One_Click_Clear"), getClearDefaultState()); + this.add(allowOneClickClear, BorderLayout.NORTH); + } + + protected boolean getClearDefaultState() { + return true; + } + + protected void initLayout() { + VerticalFlowLayout verticalFlowLayout = new VerticalFlowLayout(FlowLayout.LEADING, 0, 5); + verticalFlowLayout.setAlignLeft(true); + this.setLayout(verticalFlowLayout); + } + + @Override + public void populateBean(MobileTextEditAttr ob) { + allowOneClickClear.setSelected(ob.isAllowOneClickClear() == null || ob.isAllowOneClickClear()); + } + + @Override + public MobileTextEditAttr updateBean() { + // do nothing + return null; + } + + @Override + public void updateBean(MobileTextEditAttr ob) { + ob.setAllowOneClickClear(allowOneClickClear.isSelected()); + } + + @Override + protected String title4PopupWindow() { + return null; + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextFieldInputSettingPane.java b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextFieldInputSettingPane.java index fb9daa30f3..b7661f9f04 100644 --- a/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextFieldInputSettingPane.java +++ b/designer-form/src/main/java/com/fr/design/widget/ui/designer/mobile/component/MobileTextFieldInputSettingPane.java @@ -2,16 +2,21 @@ package com.fr.design.widget.ui.designer.mobile.component; import com.fr.base.mobile.MobileScanCodeAttr; import com.fr.design.beans.BasicBeanPane; +import com.fr.design.constants.LayoutConstants; import com.fr.design.gui.ibutton.UIRadioButton; import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; import com.fr.design.layout.VerticalFlowLayout; import javax.swing.ButtonGroup; import javax.swing.JPanel; import java.awt.BorderLayout; -import java.awt.FlowLayout; +import java.awt.Component; +import java.awt.Dimension; /** * @author hades @@ -31,7 +36,6 @@ public class MobileTextFieldInputSettingPane extends BasicBeanPane shortCuts = new ArrayList(); // shortCuts.add(new NewWorkBookXAction()); shortCuts.add(new NewWorkBookAction()); - shortCuts.add(new NewPolyReportAction()); try { if (DesignModuleFactory.getNewFormAction() != null) { shortCuts.add((ShortCut) DesignModuleFactory.getNewFormAction().newInstance()); @@ -221,6 +220,7 @@ public class MainDesigner extends BaseDesigner { } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } + shortCuts.add(new NewPolyReportAction()); return shortCuts.toArray(new ShortCut[0]); } diff --git a/designer-realize/src/main/java/com/fr/start/SplashContext.java b/designer-realize/src/main/java/com/fr/start/SplashContext.java index 7fc909c5f3..59b5a27828 100644 --- a/designer-realize/src/main/java/com/fr/start/SplashContext.java +++ b/designer-realize/src/main/java/com/fr/start/SplashContext.java @@ -2,14 +2,12 @@ package com.fr.start; import com.fr.concurrent.NamedThreadFactory; import com.fr.design.i18n.Toolkit; -import com.fr.design.locale.impl.SplashMark; import com.fr.design.mainframe.bbs.BBSConstants; import com.fr.event.Event; import com.fr.event.EventDispatcher; import com.fr.event.Listener; import com.fr.general.GeneralContext; -import com.fr.general.locale.LocaleCenter; -import com.fr.general.locale.LocaleMark; +import com.fr.general.locale.image.I18nImage; import com.fr.module.ModuleEvent; import com.fr.stable.StringUtils; @@ -44,6 +42,11 @@ public class SplashContext { private String guest = StringUtils.EMPTY; private boolean hasShowThanks = false; + /** + * 正常图片路径 + */ + private static final String SPLASH_IMAGE_PATH = "/com/fr/design/images/splash.png"; + private ScheduledExecutorService scheduler = Executors .newScheduledThreadPool(1, new NamedThreadFactory("SplashContext")); @@ -174,7 +177,6 @@ public class SplashContext { } private static String getSplashPath() { - LocaleMark localeMark = LocaleCenter.getMark(SplashMark.class); - return localeMark.getValue(); + return I18nImage.getImagePath(SPLASH_IMAGE_PATH); } } \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/start/common/SplashPane.java b/designer-realize/src/main/java/com/fr/start/common/SplashPane.java index 2bbb1b1527..a6c6c92bc3 100644 --- a/designer-realize/src/main/java/com/fr/start/common/SplashPane.java +++ b/designer-realize/src/main/java/com/fr/start/common/SplashPane.java @@ -3,9 +3,7 @@ package com.fr.start.common; import com.bulenkov.iconloader.IconLoader; import com.bulenkov.iconloader.util.JBUI; import com.fr.base.GraphHelper; -import com.fr.design.locale.impl.SplashMark; -import com.fr.general.locale.LocaleCenter; -import com.fr.general.locale.LocaleMark; +import com.fr.general.locale.image.I18nImage; import com.fr.stable.GraphDrawHelper; import com.fr.stable.GraphicsConfig; import com.fr.stable.StringUtils; @@ -51,13 +49,17 @@ public class SplashPane extends JPanel { private String thanksLog = StringUtils.EMPTY; private String moduleText = StringUtils.EMPTY; + /** + * 正常图片路径 + */ + private static final String SPLASH_PATH = "/com/fr/design/images/splash.png"; + private static int uiScale(int i) { return (int) (i * JBUI_INIT_SCALE); } private static String getSplashPath() { - LocaleMark localeMark = LocaleCenter.getMark(SplashMark.class); - return localeMark.getValue(); + return I18nImage.getImagePath(SPLASH_PATH); } private NotNullLazyValue fontValue = new NotNullLazyValue() { diff --git a/designer-realize/src/main/java/com/fr/start/common/SplashPane4WinAndJDK11.java b/designer-realize/src/main/java/com/fr/start/common/SplashPane4WinAndJDK11.java index 02aa605339..45aa334713 100644 --- a/designer-realize/src/main/java/com/fr/start/common/SplashPane4WinAndJDK11.java +++ b/designer-realize/src/main/java/com/fr/start/common/SplashPane4WinAndJDK11.java @@ -2,16 +2,14 @@ package com.fr.start.common; import com.bulenkov.iconloader.IconLoader; import com.fr.base.BaseUtils; -import com.fr.design.locale.impl.DoubleSplashMark; -import com.fr.design.locale.impl.SplashMark; import com.fr.general.IOUtils; -import com.fr.general.locale.LocaleCenter; +import com.fr.general.locale.image.I18nImage; -import java.awt.RenderingHints; import javax.swing.Icon; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.RenderingHints; import java.awt.image.BufferedImage; /** @@ -30,13 +28,23 @@ public class SplashPane4WinAndJDK11 extends SplashPane{ */ private boolean isDouble = false; + /** + * 正常图片路径 + */ + private static final String SPLASH_PATH = "/com/fr/design/images/splash.png"; + + /** + * 两倍图片路径 + */ + private static final String DOUBLE_SPLASH_PATH = "/com/fr/design/images/splash@2x.png"; + public SplashPane4WinAndJDK11() { this.imagePath = getSplashPath4WinAndJdk11(); } private String getSplashPath4WinAndJdk11() { - String path = (String) LocaleCenter.getMark(SplashMark.class).getValue(); - String pathOfDouble = (String) LocaleCenter.getMark(DoubleSplashMark.class).getValue(); + String path = I18nImage.getImagePath(SPLASH_PATH); + String pathOfDouble = I18nImage.getImagePath(DOUBLE_SPLASH_PATH); // 为图片加上"@2x" // 某些定制jar里面没有两倍图,判断一下,如果文件不存在,就返回一倍图的path if (IOUtils.readResource(pathOfDouble) != null) { diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash.png b/designer-realize/src/main/resources/com/fr/design/images/splash.png index 0535306e30..9e3cac4197 100644 Binary files a/designer-realize/src/main/resources/com/fr/design/images/splash.png and b/designer-realize/src/main/resources/com/fr/design/images/splash.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash@2x.png b/designer-realize/src/main/resources/com/fr/design/images/splash@2x.png index a284a5585e..8e90c76e33 100644 Binary files a/designer-realize/src/main/resources/com/fr/design/images/splash@2x.png and b/designer-realize/src/main/resources/com/fr/design/images/splash@2x.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash_en@2x.png b/designer-realize/src/main/resources/com/fr/design/images/splash@2x_en.png similarity index 100% rename from designer-realize/src/main/resources/com/fr/design/images/splash_en@2x.png rename to designer-realize/src/main/resources/com/fr/design/images/splash@2x_en.png diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash@2x_zh.png b/designer-realize/src/main/resources/com/fr/design/images/splash@2x_zh.png new file mode 100644 index 0000000000..a284a5585e Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/images/splash@2x_zh.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash@2x_zh_TW.png b/designer-realize/src/main/resources/com/fr/design/images/splash@2x_zh_TW.png new file mode 100644 index 0000000000..2a67ffa3ce Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/images/splash@2x_zh_TW.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash_zh.png b/designer-realize/src/main/resources/com/fr/design/images/splash_zh.png new file mode 100644 index 0000000000..0535306e30 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/images/splash_zh.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/images/splash_zh_TW.png b/designer-realize/src/main/resources/com/fr/design/images/splash_zh_TW.png new file mode 100644 index 0000000000..9d435160ce Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/images/splash_zh_TW.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open.png new file mode 100644 index 0000000000..f2eb7b2ee7 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_en.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_en.png new file mode 100644 index 0000000000..f2eb7b2ee7 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_en.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_zh.png similarity index 100% rename from designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open.png rename to designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_zh.png diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_zh_TW.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_zh_TW.png new file mode 100644 index 0000000000..d65a178d49 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/open/open_zh_TW.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind.png new file mode 100644 index 0000000000..9e91409643 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_en.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_en.png new file mode 100644 index 0000000000..9e91409643 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_en.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_zh.png similarity index 100% rename from designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind.png rename to designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_zh.png diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_zh_TW.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_zh_TW.png new file mode 100644 index 0000000000..d9192b9ce9 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/remind/remind_zh_TW.png differ