From 9194a041bb3bd98f4e6ffb04a82ff20fee3ed0a1 Mon Sep 17 00:00:00 2001 From: "fly.li" Date: Mon, 27 Sep 2021 16:54:55 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/actions/UpdateAction.java | 11 + .../design/actions/help/TutorialAction.java | 2 +- .../com/fr/design/border/UITitledBorder.java | 19 +- .../fr/design/border/UITitledMatteBorder.java | 32 ++ .../fr/design/cell/CellStylePreviewPane.java | 31 +- .../fr/design/data/datapane/ChoosePane.java | 48 +- .../connect/ConnectionComboBoxPanel.java | 5 +- .../connect/ItemEditableComboBoxPanel.java | 63 ++- .../fr/design/file/MutilTempalteTabPane.java | 3 +- .../gui/controlpane/JListControlPane.java | 4 +- .../design/gui/controlpane/UIControlPane.java | 2 - .../controlpane/UIListGroupControlPane.java | 8 +- .../com/fr/design/gui/ilist/JNameEdList.java | 23 +- .../com/fr/design/gui/ispinner/UISpinner.java | 6 + .../design/gui/itextfield/EditTextField.java | 7 +- .../style/TranslucentBorderSpecialPane.java | 17 +- .../javascript/ExportJavaScriptPane.java | 4 + .../authority/DSColumnAuthorityChecker.java | 20 + .../authority/ElementAuthorityChecker.java | 45 ++ .../authority/FormulaAuthorityChecker.java | 27 + .../authority/JTemplateAuthorityChecker.java | 184 +++++++ ...ameDatabaseConnectionAuthorityChecker.java | 22 + .../NameTableDataAuthorityChecker.java | 19 + .../mainframe/theme/FormThemeProfilePane.java | 76 +-- .../theme/ReportThemeProfilePane.java | 45 +- .../theme/TemplateThemeEditorPane.java | 315 +++++++++++ .../theme/TemplateThemeListPane.java | 2 +- .../theme/TemplateThemePreviewPane.java | 8 +- .../theme/TemplateThemeProfilePane.java | 519 ++++-------------- .../dialog/TemplateThemeProfileDialog.java | 2 +- .../theme/edit/ComponentStyleEditPane.java | 30 +- .../theme/edit/ui/ColorListPane.java | 7 + .../mainframe/theme/edit/ui/LabelUtils.java | 19 + .../mainframe/theme/edit/ui/TabbedPane.java | 1 - .../ecpreview/ECReportPreviewPane.java | 6 +- .../theme/ui/AutoCheckTextField.java | 100 ++++ .../theme/ui/AutoCheckThemeNameTextField.java | 26 + .../mainframe/toolbar/ToolBarMenuDock.java | 15 +- .../java/com/fr/design/ui/ModernUIPane.java | 42 +- .../fr/design/ui/compatible/BuilderDiff.java | 2 + .../ui/compatible/ModernUIPaneFactory.java | 19 +- .../design/ui/compatible/NewModernUIPane.java | 57 +- .../push/DesignerPushUpdateManager.java | 2 +- .../update/ui/dialog/UpdateMainDialog.java | 2 +- .../gui/AdjustWorkBookDefaultStyleUtils.java | 46 ++ .../com/fr/design/utils/gui/GUICoreUtils.java | 8 +- .../java/com/fr/file/FILEChooserPane.java | 12 +- .../design/images/transparent_background.png | Bin 24823 -> 17319 bytes 48 files changed, 1378 insertions(+), 585 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/design/border/UITitledMatteBorder.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/authority/DSColumnAuthorityChecker.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/authority/ElementAuthorityChecker.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/authority/FormulaAuthorityChecker.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/authority/JTemplateAuthorityChecker.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/authority/NameDatabaseConnectionAuthorityChecker.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/authority/NameTableDataAuthorityChecker.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckTextField.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckThemeNameTextField.java create mode 100644 designer-base/src/main/java/com/fr/design/utils/gui/AdjustWorkBookDefaultStyleUtils.java diff --git a/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java b/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java index 5c12ff29c3..048f233373 100644 --- a/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/UpdateAction.java @@ -345,6 +345,17 @@ public abstract class UpdateAction extends ShortCut implements Action { return (JComponent) object; } + public JComponent createToolBarComponentByName(String componentName) { + Object object = this.getValue(componentName); + if (!(object instanceof AbstractButton)) { + UIButton button = null; + button = new UIButton(); + object = initButton(button, componentName); + } + + return (JComponent) object; + } + protected JComponent initButton(UIButton button, String name) { // 添加一个名字作为自动化测试用 button.setName(getName()); diff --git a/designer-base/src/main/java/com/fr/design/actions/help/TutorialAction.java b/designer-base/src/main/java/com/fr/design/actions/help/TutorialAction.java index 3e08b13991..51a390c82d 100644 --- a/designer-base/src/main/java/com/fr/design/actions/help/TutorialAction.java +++ b/designer-base/src/main/java/com/fr/design/actions/help/TutorialAction.java @@ -28,7 +28,7 @@ public class TutorialAction extends AbstractDesignerSSO { @Override public String getJumpUrl() { - return CloudCenter.getInstance().acquireUrlByKind(createDocKey()); + return CloudCenter.getInstance().acquireUrlByKind(createDocKey(), "http://help.finereport.com"); } // 生成帮助文档 sitecenter key, help.zh_CN.10 diff --git a/designer-base/src/main/java/com/fr/design/border/UITitledBorder.java b/designer-base/src/main/java/com/fr/design/border/UITitledBorder.java index 3987844cf8..875ef00833 100644 --- a/designer-base/src/main/java/com/fr/design/border/UITitledBorder.java +++ b/designer-base/src/main/java/com/fr/design/border/UITitledBorder.java @@ -22,24 +22,7 @@ public class UITitledBorder extends TitledBorder { } private UITitledBorder(String title) { - super( - BorderFactory.createCompoundBorder( - BorderFactory.createEmptyBorder( - 0, - 0, - 5, - 0), - new UIRoundedBorder( - UIConstants.TITLED_BORDER_COLOR, - 1, - 10) - ), - title, - TitledBorder.LEADING, - TitledBorder.TOP, - null, - new Color(1, 159, 222) - ); + this(title, 10); } /** diff --git a/designer-base/src/main/java/com/fr/design/border/UITitledMatteBorder.java b/designer-base/src/main/java/com/fr/design/border/UITitledMatteBorder.java new file mode 100644 index 0000000000..c46143e1e5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/border/UITitledMatteBorder.java @@ -0,0 +1,32 @@ +package com.fr.design.border; + +import com.fr.design.constants.UIConstants; + +import javax.swing.BorderFactory; +import javax.swing.border.TitledBorder; +import java.awt.Color; + +public class UITitledMatteBorder extends TitledBorder { + public static UITitledMatteBorder createTitledTopBorder(String title, Color color) { + return new UITitledMatteBorder(title, 1, 0, 0, 0, color); + } + + public static UITitledMatteBorder createTitledBorder(String title, Color color) { + return new UITitledMatteBorder(title, 1, 1, 1, 1, color); + } + + public static UITitledMatteBorder createTitledBorder(String title, int top, int left, int bottom, int right, Color color) { + return new UITitledMatteBorder(title, top, left, bottom, right, color); + } + + private UITitledMatteBorder(String title, int top, int left, int bottom, int right, Color color) { + super( + BorderFactory.createMatteBorder(top, left, bottom, right, UIConstants.TITLED_BORDER_COLOR), + title, + TitledBorder.LEADING, + TitledBorder.TOP, + null, + color + ); + } +} diff --git a/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java b/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java index 393f217ce7..c317d0e5a3 100644 --- a/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java +++ b/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java @@ -6,6 +6,9 @@ import com.fr.base.Style; import com.fr.general.IOUtils; import javax.swing.JPanel; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; @@ -40,10 +43,21 @@ public class CellStylePreviewPane extends JPanel { @Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; - int resolution = ScreenResolution.getScreenResolution(); - int width = getWidth(); - int height = getHeight(); + paintTransparentBackground(g2d, style); + + paintCellStyle(g2d, style); + } + + private void paintTransparentBackground(Graphics2D g2d, Style style) { + Color fontColor = style.getFRFont().getForeground(); + float g = fontColor.getRed() * 0.299F + fontColor.getGreen() * 0.587F * fontColor.getBlue() * 0.114F; + float alpha = 1.0F; + if (g < 50) { + alpha = 0.2F; + } else if (g < 160){ + alpha = 0.5F; + } float scaleWidth = 1.0F * getWidth() / transparentBackgroundWidth; float scaleHeight = 1.0F * getHeight() / transparentBackgroundHeight; @@ -54,7 +68,18 @@ public class CellStylePreviewPane extends JPanel { } else { scaleHeight = scaleWidth = maxScale; } + + Composite oldComposite = g2d.getComposite(); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); g2d.drawImage(transparentBackgroundImage, 0, 0, (int) (transparentBackgroundWidth * scaleWidth), (int) (transparentBackgroundHeight * scaleHeight), null); + g2d.setComposite(oldComposite); + } + + private void paintCellStyle(Graphics2D g2d, Style style) { + int resolution = ScreenResolution.getScreenResolution(); + + int width = getWidth(); + int height = getHeight(); if (style == Style.DEFAULT_STYLE) { // 如果是默认的style,就只写"Report"上去 diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/ChoosePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/ChoosePane.java index b5bc39e4eb..f656105265 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/ChoosePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/ChoosePane.java @@ -25,6 +25,7 @@ import com.fr.design.gui.icombobox.UIComboBoxEditor; import com.fr.design.gui.icombobox.UIComboBoxRenderer; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; @@ -39,7 +40,16 @@ import com.fr.stable.StringUtils; import com.fr.workspace.WorkContext; import com.fr.workspace.server.connection.DBConnectAuth; -import javax.swing.*; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.util.Collections; +import java.util.concurrent.CancellationException; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTree; +import javax.swing.SwingWorker; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.PopupMenuEvent; @@ -50,7 +60,6 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import java.awt.*; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; @@ -70,6 +79,9 @@ import java.util.concurrent.FutureTask; * @since 2012-7-11下午4:49:39 */ public class ChoosePane extends BasicBeanPane implements Refreshable, Previewable, Prepare4DataSourceChange { + + private static final List PENDING_CONTENT = new ArrayList<>(Collections.singletonList(Toolkit.i18nText("Fine-Design_Basic_Loading") + "...")); + private static final double COLUMN_SIZE = 24; /** @@ -91,6 +103,8 @@ public class ChoosePane extends BasicBeanPane implements Refresha private SwingWorker populateWorker; + private SwingWorker, Void> initWorker; + private PopupMenuListener popupMenuListener = new PopupMenuListener() { @Override @@ -213,10 +227,34 @@ public class ChoosePane extends BasicBeanPane implements Refresha @SuppressWarnings("unchecked") protected void initDsNameComboBox() { + if (initWorker != null && !initWorker.isDone()) { + initWorker.cancel(true); + } dsNameComboBox.setRefreshingModel(true); - FilterableComboBoxModel dsNameComboBoxModel = new FilterableComboBoxModel(getHasAuthConnections()); - dsNameComboBox.setModel(dsNameComboBoxModel); - dsNameComboBox.setRefreshingModel(false); + dsNameComboBox.setModel(new FilterableComboBoxModel(PENDING_CONTENT)); + initWorker = new SwingWorker, Void>() { + + @Override + protected List doInBackground() throws Exception { + return getHasAuthConnections(); + } + + @Override + protected void done() { + try { + FilterableComboBoxModel dsNameComboBoxModel = new FilterableComboBoxModel(get()); + String selected = dsNameComboBox.getSelectedItem(); + dsNameComboBox.setModel(dsNameComboBoxModel); + dsNameComboBox.setSelectedItem(selected); + dsNameComboBox.setRefreshingModel(false); + } catch (Exception e) { + if (!(e instanceof CancellationException)) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + } + }; + initWorker.execute(); } protected void initComponentsLayout(PreviewLabel previewLabel, int labelSize) { diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java index 310558f168..3b5aebed9e 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java @@ -80,12 +80,13 @@ public class ConnectionComboBoxPanel extends ItemEditableComboBoxPanel { * 刷新ComboBox.items */ protected Iterator items() { - nameList = new ArrayList(); - ConnectionConfig mgr = ConnectionConfig.getInstance(); Iterator nameIt = mgr.getConnections().keySet().iterator(); Collection noAuthConnections = WorkContext.getCurrent().get(DBConnectAuth.class).getNoAuthConnections(); + + nameList = new ArrayList<>(); + if (noAuthConnections == null) { return nameList.iterator(); } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java index f06d1405b8..d0738e57b8 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/connect/ItemEditableComboBoxPanel.java @@ -3,12 +3,19 @@ package com.fr.design.data.datapane.connect; import com.fr.base.BaseUtils; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icombobox.UIComboBox; +import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; -import javax.swing.*; -import java.awt.*; +import com.fr.log.FineLoggerFactory; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.Iterator; +import java.util.concurrent.CancellationException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JPanel; +import javax.swing.SwingWorker; public abstract class ItemEditableComboBoxPanel extends JPanel { /** @@ -16,6 +23,8 @@ public abstract class ItemEditableComboBoxPanel extends JPanel { */ private static final long serialVersionUID = 1L; + private static final String PENDING = Toolkit.i18nText("Fine-Design_Basic_Loading") + "..."; + protected static final Object EMPTY = new Object() { public String toString() { return ""; @@ -26,6 +35,8 @@ public abstract class ItemEditableComboBoxPanel extends JPanel { protected UIButton editButton; protected UIButton refreshButton; + private SwingWorker, Void> refreshWorker; + public ItemEditableComboBoxPanel() { super(); @@ -75,6 +86,11 @@ public abstract class ItemEditableComboBoxPanel extends JPanel { * 刷新itemComboBox的内容 */ protected void refreshItems() { + + if (refreshWorker != null && !refreshWorker.isDone()) { + refreshWorker.cancel(true); + } + // 记录原来选中的Item,重新加载后需要再次选中 Object lastSelectedItem = itemComboBox.getSelectedItem(); @@ -83,18 +99,43 @@ public abstract class ItemEditableComboBoxPanel extends JPanel { // 先加EMPTY,再加items model.addElement(EMPTY); + model.addElement(PENDING); - java.util.Iterator itemIt = items(); - while(itemIt.hasNext()) { - model.addElement(itemIt.next()); + // 存在两种场景之前只考虑了填充场景 有populate会填充下 把这边的填充逻辑删了 所以没有问题 + // 如果是纯通过刷新按钮 没有populate 需要手动设置下上次选中的内容 + if (lastSelectedItem != null) { + model.setSelectedItem(lastSelectedItem); } - // 再次选中之前选中的Item - int idx = model.getIndexOf(lastSelectedItem); - if(idx < 0) { - idx = 0; - } - itemComboBox.setSelectedIndex(idx); + refreshWorker = new SwingWorker, Void>() { + @Override + protected Iterator doInBackground() throws Exception { + return items(); + } + + @Override + protected void done() { + try { + Iterator itemIt = get(); + model.removeElement(PENDING); + while(itemIt.hasNext()) { + model.addElement(itemIt.next()); + } + // 如果加载成功 但是下拉框是可见的 下拉框高度是会固定为原始高度 不会因为填充了更多下拉项而变化 + // 需要重新设置下拉框高度 但值一样时相关事件不会生效 所以先加再减下 + if (itemComboBox.isPopupVisible()) { + itemComboBox.setMaximumRowCount(itemComboBox.getMaximumRowCount() + 1); + itemComboBox.setMaximumRowCount(itemComboBox.getMaximumRowCount() - 1); + } + } catch (Exception e) { + if (!(e instanceof CancellationException)) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + } + }; + refreshWorker.execute(); } /* diff --git a/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java b/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java index 6c3323600c..b7a564713b 100644 --- a/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java +++ b/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java @@ -4,6 +4,7 @@ package com.fr.design.file; import com.fr.base.BaseUtils; import com.fr.base.GraphHelper; import com.fr.base.vcs.DesignerMode; +import com.fr.design.base.mode.DesignModeContext; import com.fr.design.constants.UIConstants; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.gui.imenu.UIMenuItem; @@ -763,7 +764,7 @@ public class MutilTempalteTabPane extends JComponent { /** * 关闭掉一个模板之后激活新的待显示模板 */ - private void activePrevTemplateAfterClose() { + public void activePrevTemplateAfterClose() { if (openedTemplate.isEmpty()) { //新建并激活模板 DesignerContext.getDesignerFrame().addAndActivateJTemplate(); diff --git a/designer-base/src/main/java/com/fr/design/gui/controlpane/JListControlPane.java b/designer-base/src/main/java/com/fr/design/gui/controlpane/JListControlPane.java index f119c55cd3..82cba758b7 100644 --- a/designer-base/src/main/java/com/fr/design/gui/controlpane/JListControlPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/controlpane/JListControlPane.java @@ -23,12 +23,12 @@ import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import java.util.Collection; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import java.util.Collection; public abstract class JListControlPane extends JControlPane implements ListControlPaneProvider { private static final String LIST_NAME = "JControl_List"; @@ -335,7 +335,7 @@ public abstract class JListControlPane extends JControlPane implements ListContr getHelper().checkButtonEnabled(); } - private class NameableListCellRenderer extends + protected class NameableListCellRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, diff --git a/designer-base/src/main/java/com/fr/design/gui/controlpane/UIControlPane.java b/designer-base/src/main/java/com/fr/design/gui/controlpane/UIControlPane.java index 648cb03f33..f7229136f5 100644 --- a/designer-base/src/main/java/com/fr/design/gui/controlpane/UIControlPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/controlpane/UIControlPane.java @@ -387,8 +387,6 @@ public abstract class UIControlPane extends JControlPane { contentPane.setBackground(originColor); contentPane.setLayout(new BorderLayout()); titleLabel = new UILabel(title); - Font font = new Font("SimSun", Font.PLAIN, 12); - titleLabel.setFont(font); contentPane.add(titleLabel, BorderLayout.WEST); contentPane.setBorder(new EmptyBorder(5, 14, 6, 0)); diff --git a/designer-base/src/main/java/com/fr/design/gui/controlpane/UIListGroupControlPane.java b/designer-base/src/main/java/com/fr/design/gui/controlpane/UIListGroupControlPane.java index 6ec5f496a6..7c40235304 100644 --- a/designer-base/src/main/java/com/fr/design/gui/controlpane/UIListGroupControlPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/controlpane/UIListGroupControlPane.java @@ -329,7 +329,7 @@ public abstract class UIListGroupControlPane extends UIControlPane implements Li width = Math.max(width, calculateUIListMaxCellWidth(uiList.getModel(), uiList.getFontMetrics(uiList.getFont()))); } iterator = nameEdListMap.entrySet().iterator(); - width += 40; + width += 30; while (iterator.hasNext()) { Map.Entry entry = iterator.next(); ListWrapperPane wrapperPane = entry.getValue(); @@ -349,7 +349,8 @@ public abstract class UIListGroupControlPane extends UIControlPane implements Li } else { text = element.toString(); } - width = Math.max(width, fontMetrics.stringWidth(text)); + //增加 10px 的左右间隔 + width = Math.max(width, fontMetrics.stringWidth(text) + 10); } } return width; @@ -539,7 +540,8 @@ public abstract class UIListGroupControlPane extends UIControlPane implements Li label.setBackground(Color.WHITE); label.setForeground(Color.decode("#333334")); label.setFont(label.getFont().deriveFont(11F)); - label.setPreferredSize(new Dimension(224, 26)); + //预留 10px 的纵向滚动条的宽度 + label.setPreferredSize(new Dimension(214, 26)); this.nameEdList = nameEdList; this.add(label, BorderLayout.NORTH); this.add(this.nameEdList, BorderLayout.CENTER); diff --git a/designer-base/src/main/java/com/fr/design/gui/ilist/JNameEdList.java b/designer-base/src/main/java/com/fr/design/gui/ilist/JNameEdList.java index 0efa74cec7..4c530c0fcc 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ilist/JNameEdList.java +++ b/designer-base/src/main/java/com/fr/design/gui/ilist/JNameEdList.java @@ -1,25 +1,22 @@ package com.fr.design.gui.ilist; import com.fr.design.gui.NameInspector; +import com.fr.design.gui.itextfield.UITextField; import com.fr.general.GeneralUtils; import com.fr.general.NameObject; -import com.fr.base.Utils; -import com.fr.design.gui.itextfield.UITextField; - import com.fr.stable.Nameable; import com.fr.stable.StringUtils; import com.fr.stable.core.PropertyChangeAdapter; -import javax.swing.*; +import javax.swing.ListModel; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; -import java.awt.*; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Vector; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; public class JNameEdList extends UIList implements CellEditorListener { private static final int ICON_WIDTH = 20; @@ -38,6 +35,8 @@ public class JNameEdList extends UIList implements CellEditorListener { */ private String oldName; + private boolean replaceEmptyName = true; + public JNameEdList(ListModel dataModel) { super(dataModel); } @@ -70,6 +69,10 @@ public class JNameEdList extends UIList implements CellEditorListener { return this.editable; } + public void setReplaceEmptyName(boolean replaceEmptyName) { + this.replaceEmptyName = replaceEmptyName; + } + public void setNameShouldNumber(boolean isNameShouldNumber) { this.isNameShouldNumber = isNameShouldNumber; } @@ -301,7 +304,7 @@ public class JNameEdList extends UIList implements CellEditorListener { ListCellEditor editor = getCellEditor(); if (editor != null && editorComp != null) { Object value = editor.getCellEditorValue(); - String name = StringUtils.isBlank(value.toString()) ? oldName : value.toString(); + String name = StringUtils.isBlank(value.toString()) && replaceEmptyName ? oldName : value.toString(); setNameAt(name, editingIndex); removeComp(); doAfterStopEditing(); diff --git a/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java b/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java index 22f6cb246e..4a722fc50d 100644 --- a/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java +++ b/designer-base/src/main/java/com/fr/design/gui/ispinner/UISpinner.java @@ -73,6 +73,12 @@ public class UISpinner extends JPanel implements UIObserver, GlobalNameObserver textField.setValue(defaultValue); } + public UISpinner(double minValue, double maxValue, double dierta, double defaultValue, boolean fillNegativeNumber) { + init(minValue, maxValue, dierta); + textField.setValue(defaultValue); + textField.canFillNegativeNumber(fillNegativeNumber); + } + protected void init(double minValue, double maxValue, double dierta) { this.minValue = minValue; this.maxValue = maxValue; diff --git a/designer-base/src/main/java/com/fr/design/gui/itextfield/EditTextField.java b/designer-base/src/main/java/com/fr/design/gui/itextfield/EditTextField.java index 9ed9068273..85474ca3ce 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itextfield/EditTextField.java +++ b/designer-base/src/main/java/com/fr/design/gui/itextfield/EditTextField.java @@ -1,12 +1,11 @@ package com.fr.design.gui.itextfield; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Toolkit; - import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.PlainDocument; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Toolkit; /** diff --git a/designer-base/src/main/java/com/fr/design/gui/style/TranslucentBorderSpecialPane.java b/designer-base/src/main/java/com/fr/design/gui/style/TranslucentBorderSpecialPane.java index 237a5659a7..0de98c3b5f 100644 --- a/designer-base/src/main/java/com/fr/design/gui/style/TranslucentBorderSpecialPane.java +++ b/designer-base/src/main/java/com/fr/design/gui/style/TranslucentBorderSpecialPane.java @@ -487,6 +487,7 @@ public class TranslucentBorderSpecialPane extends AbstractBorderPackerPane imple private int ninePointBottom = -1; private static final int MIN_NINE_POINT = 0; + private static final int MIN_GAP_PARALLEL_LINES = 1; private int imgWidth; private int imgHeight; @@ -757,8 +758,8 @@ public class TranslucentBorderSpecialPane extends AbstractBorderPackerPane imple private void onNinePointTopChanged(int value) { if (value < MIN_NINE_POINT) { value = MIN_NINE_POINT; - } else if (value >= imgHeight - ninePointBottom) { - value = imgHeight - ninePointBottom - MIN_NINE_POINT; + } else if (value >= imgHeight - ninePointBottom - MIN_GAP_PARALLEL_LINES) { + value = imgHeight - ninePointBottom - MIN_GAP_PARALLEL_LINES; } this.ninePointTop = value; repaint(); @@ -767,8 +768,8 @@ public class TranslucentBorderSpecialPane extends AbstractBorderPackerPane imple private void onNinePointBottomChanged(int value) { if (value < MIN_NINE_POINT) { value = MIN_NINE_POINT; - } else if (value >= imgHeight - ninePointTop) { - value = imgHeight - ninePointTop - MIN_NINE_POINT; + } else if (value >= imgHeight - ninePointTop - MIN_GAP_PARALLEL_LINES) { + value = imgHeight - ninePointTop - MIN_GAP_PARALLEL_LINES; } this.ninePointBottom = value; repaint(); @@ -777,8 +778,8 @@ public class TranslucentBorderSpecialPane extends AbstractBorderPackerPane imple private void onNinePointLeftChanged(int value) { if (value < MIN_NINE_POINT) { value = MIN_NINE_POINT; - } else if (value >= imgWidth - ninePointRight) { - value = imgWidth - ninePointRight - MIN_NINE_POINT; + } else if (value >= imgWidth - ninePointRight - MIN_GAP_PARALLEL_LINES) { + value = imgWidth - ninePointRight - MIN_GAP_PARALLEL_LINES; } this.ninePointLeft = value; repaint(); @@ -787,8 +788,8 @@ public class TranslucentBorderSpecialPane extends AbstractBorderPackerPane imple private void onNinePointRightChanged(int value) { if (value < MIN_NINE_POINT) { value = MIN_NINE_POINT; - } else if (value >= imgWidth - ninePointLeft) { - value = imgWidth - ninePointLeft - MIN_NINE_POINT; + } else if (value >= imgWidth - ninePointLeft - MIN_GAP_PARALLEL_LINES) { + value = imgWidth - ninePointLeft - MIN_GAP_PARALLEL_LINES; } this.ninePointRight = value; repaint(); diff --git a/designer-base/src/main/java/com/fr/design/javascript/ExportJavaScriptPane.java b/designer-base/src/main/java/com/fr/design/javascript/ExportJavaScriptPane.java index f3367206f5..460e838c51 100644 --- a/designer-base/src/main/java/com/fr/design/javascript/ExportJavaScriptPane.java +++ b/designer-base/src/main/java/com/fr/design/javascript/ExportJavaScriptPane.java @@ -2,6 +2,7 @@ package com.fr.design.javascript; import com.fr.base.BaseFormula; import com.fr.base.Parameter; +import com.fr.base.extension.FileExtension; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.DialogActionAdapter; @@ -417,6 +418,9 @@ public class ExportJavaScriptPane extends AbstractHyperLinkPane { + + @Override + @Nullable + Set getNoAuthDatasetNames(DSColumn dsColumn, Set authDatasetNames) { + if (!authDatasetNames.contains(dsColumn.getDSName())) { + return new HashSet<>(Arrays.asList(dsColumn.getDSName())); + } + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/authority/ElementAuthorityChecker.java b/designer-base/src/main/java/com/fr/design/mainframe/authority/ElementAuthorityChecker.java new file mode 100644 index 0000000000..67d0f1da36 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/authority/ElementAuthorityChecker.java @@ -0,0 +1,45 @@ +package com.fr.design.mainframe.authority; + +import org.jetbrains.annotations.Nullable; +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; + +import java.lang.reflect.Type; +import java.util.Set; + + +public abstract class ElementAuthorityChecker { + + + /** + * @Description 获取越权的数据连接 + * @param: t 待检查的对象 + * @param: authConnectionNames 有权限的数据连接名 + * @return 如果有返回名称,没有返回null + */ + @Nullable + Set getNoAuthConnectionNames(T t, Set authConnectionNames) { + return null; + } + + + /** + * @Description 获取越权的服务器数据集 + * @param: t 待检查的对象 + * @param: authDatasetNames 有权限的服务器数据集名 + * @return 如果有返回名称,没有返回null + */ + @Nullable + Set getNoAuthDatasetNames(T t, Set authDatasetNames) { + return null; + } + + /** + * @Description 要检查对象的className + * @return className + */ + String getCheckClassName() { + ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) this.getClass().getGenericSuperclass(); + Type type = parameterizedType.getActualTypeArguments()[0]; + return type.getTypeName(); + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/authority/FormulaAuthorityChecker.java b/designer-base/src/main/java/com/fr/design/mainframe/authority/FormulaAuthorityChecker.java new file mode 100644 index 0000000000..d108c18d41 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/authority/FormulaAuthorityChecker.java @@ -0,0 +1,27 @@ +package com.fr.design.mainframe.authority; + +import com.fr.base.Formula; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FormulaAuthorityChecker extends ElementAuthorityChecker { + private static final Pattern FORMULA_PATTERN = Pattern.compile("^=SQL\\(\"(.+?)\","); + + @Override + @Nullable + public Set getNoAuthConnectionNames(Formula formula, Set authConnectionNames) { + String content = formula.getContent(); + Matcher matcher = FORMULA_PATTERN.matcher(content); + if (matcher.find()) { + if (!authConnectionNames.contains(matcher.group(1))) { + return new HashSet<>(Arrays.asList(matcher.group(1))); + } + } + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/authority/JTemplateAuthorityChecker.java b/designer-base/src/main/java/com/fr/design/mainframe/authority/JTemplateAuthorityChecker.java new file mode 100644 index 0000000000..f8b2c8ce6b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/authority/JTemplateAuthorityChecker.java @@ -0,0 +1,184 @@ +package com.fr.design.mainframe.authority; + + +import com.fr.design.dialog.FineJOptionPane; + +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mod.ModClassFilter; +import com.fr.invoke.ClassHelper; + +import com.fr.log.FineLoggerFactory; +import com.fr.rpc.ExceptionHandler; +import com.fr.rpc.RPCInvokerExceptionInfo; +import com.fr.stable.Filter; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.authority.user.UserAuthority; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +import static javax.swing.JOptionPane.WARNING_MESSAGE; + + +public class JTemplateAuthorityChecker { + JTemplate jTemplate; + Set authConnectionNames; + Set authDatasetNames; + Map checkerMap = new HashMap<>(); + Set authFailConnectionNames = new HashSet<>(); + Set authFailDatasetNames = new HashSet<>(); + + + public JTemplateAuthorityChecker(JTemplate jTemplate) { + long s = System.currentTimeMillis(); + this.jTemplate = jTemplate; + this.initAuthNames(); + this.initChecker(); + FineLoggerFactory.getLogger().info("JTemplateAuthorityChecker init time consume:" + (System.currentTimeMillis() - s)); + } + + private void initAuthNames() { + UserAuthority templateAuthority = WorkContext.getCurrent().get(UserAuthority.class); + Map> authNamesMap = templateAuthority.getAuthServerDataSetAndConnectionNames(); + if (authNamesMap != null) { + //有权限的数据连接名称 + authConnectionNames = authNamesMap.get(UserAuthority.AUTH_CONNECTION_NAMES); + //有权限的数据集名称(模板数据集和服务器数据集) + authDatasetNames = authNamesMap.get(UserAuthority.AUTH_SERVER_DATASET_NAMES); + Iterator iterator = jTemplate.getTarget().getTableDataNameIterator(); + while (iterator.hasNext()) { + String datasetName = iterator.next(); + authDatasetNames.add(datasetName); + } + } + } + + private void initChecker() { + registerChecker(new NameDatabaseConnectionAuthorityChecker()); + registerChecker(new DSColumnAuthorityChecker()); + registerChecker(new FormulaAuthorityChecker()); + registerChecker(new NameTableDataAuthorityChecker()); + } + + private void registerChecker(ElementAuthorityChecker checker) { + checkerMap.put(checker.getCheckClassName(), checker); + } + + + public boolean isAuthority() { + long s = System.currentTimeMillis(); + //遍历模板对象,根据checkerMap.keySet()把感兴趣的对象找出来 + Map> targetObjects = ClassHelper.searchObject(jTemplate.getTarget(), checkerMap.keySet(), ClassFilter.getInstance()); + + //找到对应的checker,对对象进行检查 + for (String name : targetObjects.keySet()) { + ElementAuthorityChecker checker = checkerMap.get(name); + for (Object object : targetObjects.get(name)) { + if (authConnectionNames != null) { + Set noAuthName = checker.getNoAuthConnectionNames(object, authConnectionNames); + if (noAuthName != null) { + authFailConnectionNames.addAll(noAuthName); + } + } + if (authDatasetNames != null) { + Set noAuthName = checker.getNoAuthDatasetNames(object, authDatasetNames); + if (noAuthName != null) { + authFailDatasetNames.addAll(noAuthName); + } + } + } + } + + FineLoggerFactory.getLogger().info("JTemplateAuthorityChecker check time consume:" + (System.currentTimeMillis() - s)); + return authFailConnectionNames.size() == 0 && authFailDatasetNames.size() == 0; + } + + public void showAuthorityFailPromptDialog() { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(Toolkit.i18nText("Fine-Design-Basic_Save_Failure")); + stringBuffer.append("\n"); + stringBuffer.append(getPromptInfo(authFailDatasetNames, + Toolkit.i18nText("Fine-Design_Template_Authority_Check_Server_Dataset_Authority"))); + stringBuffer.append(getPromptInfo(authFailConnectionNames, + Toolkit.i18nText("Fine-Design_Template_Authority_Check_Data_Connection_Authority"))); + FineJOptionPane.showMessageDialog( + DesignerContext.getDesignerFrame(), + stringBuffer.toString(), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + } + + private String getPromptInfo(Set authFailNames, String message) { + StringBuffer stringBuffer = new StringBuffer(); + if (authFailNames.size() > 0) { + stringBuffer.append(Toolkit.i18nText("Fine-Design_Template_Authority_Check_Current_Operator_Miss")); + stringBuffer.append(authFailNames.size()); + stringBuffer.append(Toolkit.i18nText("Fine-Design_Report_Ge")); + stringBuffer.append(message); + stringBuffer.append("\n"); + stringBuffer.append(getNoAuthNameSequence(authFailNames)); + } + return stringBuffer.toString(); + } + + private String getNoAuthNameSequence(Set names) { + StringBuffer stringBuffer = new StringBuffer(); + int showMaxCount = 3; + int count = 0; + for (String name : names) { + if (count == showMaxCount) { + stringBuffer.append(Toolkit.i18nText("Fine-Design_Template_Authority_Check_Etc")); + break; + } + stringBuffer.append(name); + if (count != names.size() - 1 && count != showMaxCount - 1) { + stringBuffer.append(";"); + } + count++; + } + stringBuffer.append("\n"); + return stringBuffer.toString(); + } + + static class ClassFilter implements Filter { + + private static final Set FILTER_SET = new HashSet<>(); + private static final Set START_WITH_SET = new HashSet<>(); + private static final Filter INSTANCE = new ModClassFilter(); + + public static Filter getInstance() { + return INSTANCE; + } + + static { + FILTER_SET.add("java.awt.image.BufferedImage"); + FILTER_SET.add("sun.awt.AppContext"); + FILTER_SET.add("com.fr.poly.creator.ECBlockCreator"); + FILTER_SET.add("io.netty.channel.nio.SelectedSelectionKeySet"); + FILTER_SET.add("com.fr.form.ui.ElementCaseImage"); + FILTER_SET.add("this$0"); + START_WITH_SET.add("com.fr.design"); + } + + @Override + public boolean accept(String s) { + if (FILTER_SET.contains(s)) { + return true; + } + for (String start : START_WITH_SET) { + if (s.startsWith(start)) { + return true; + } + } + return false; + } + } +} + diff --git a/designer-base/src/main/java/com/fr/design/mainframe/authority/NameDatabaseConnectionAuthorityChecker.java b/designer-base/src/main/java/com/fr/design/mainframe/authority/NameDatabaseConnectionAuthorityChecker.java new file mode 100644 index 0000000000..96bc0b8c5d --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/authority/NameDatabaseConnectionAuthorityChecker.java @@ -0,0 +1,22 @@ +package com.fr.design.mainframe.authority; + +import com.fr.data.impl.NameDatabaseConnection; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +public class NameDatabaseConnectionAuthorityChecker extends ElementAuthorityChecker { + @Override + @Nullable + Set getNoAuthConnectionNames(NameDatabaseConnection nameDatabaseConnection, Set authConnectionNames) { + String name = nameDatabaseConnection.getName(); + if (!authConnectionNames.contains(name)) { + return new HashSet<>(Arrays.asList(name)); + } + return null; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/authority/NameTableDataAuthorityChecker.java b/designer-base/src/main/java/com/fr/design/mainframe/authority/NameTableDataAuthorityChecker.java new file mode 100644 index 0000000000..13e7300856 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/authority/NameTableDataAuthorityChecker.java @@ -0,0 +1,19 @@ +package com.fr.design.mainframe.authority; + +import com.fr.data.impl.NameTableData; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class NameTableDataAuthorityChecker extends ElementAuthorityChecker { + @Override + @Nullable + Set getNoAuthDatasetNames(NameTableData nameTableData, Set authDatasetNames) { + if (!authDatasetNames.contains(nameTableData.getName())) { + return new HashSet<>(Arrays.asList(nameTableData.getName())); + } + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/FormThemeProfilePane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/FormThemeProfilePane.java index f34f8a27f0..798bb73c59 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/FormThemeProfilePane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/FormThemeProfilePane.java @@ -36,46 +36,56 @@ public class FormThemeProfilePane extends TemplateThemeProfilePane { } @Override - protected JPanel createCustomEditorsPane() { - JPanel container = super.createCustomEditorsPane(); - formBodyStyleSettingPane = new FormBodyStyleEditPane(); - addCustomEditorPane(i18nText("Fine-Design_Predefined_Template_Background"), formBodyStyleSettingPane); - - addCustomEditorPane(i18nText("Fine-Design_Predefined_Cell_Style"), createCellStyleSettingPane()); - addCustomEditorPane(i18nText("Fine-Design_Predefined_Chart_Style"), createChartStyleSettingPane()); - - - componentStyleSettingPane = new ComponentStyleEditPane(); - addCustomEditorPane(i18nText("Fine-Design_Predefined_Component_Style"), componentStyleSettingPane); - return container; + public TemplateThemeEditorPane createThemeEditorPane() { + return new FormThemeEditorPane(config); } @Override - protected JPanel createChartStyleSettingPane() { - JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); - chartStyleSettingPane = new ChartStyleFormEditPane(); - container.add(chartStyleSettingPane); - return container; + public String title4PopupWindow() { + return Toolkit.i18nText("Fine-Design_Basic_Form_Theme_Profile_Dialog_Title"); } - @Override - public void populateBean4CustomEditors(FormTheme theme) { - super.populateBean4CustomEditors(theme); - formBodyStyleSettingPane.populateBean(theme.getBodyStyle()); - componentStyleSettingPane.populateBean(theme.getComponentStyle()); - } + private static class FormThemeEditorPane extends TemplateThemeEditorPane { - @Override - public void updateBean(FormTheme theme) { - ThemedFormBodyStyle formBodyStyle = formBodyStyleSettingPane.updateBean(); - theme.setBodyStyle(formBodyStyle); + private final FormBodyStyleEditPane formBodyStyleSettingPane; + private final ComponentStyleEditPane componentStyleSettingPane; - ThemedComponentStyle componentStyle = componentStyleSettingPane.updateBean(); - theme.setComponentStyle(componentStyle); - } + public FormThemeEditorPane(TemplateThemeConfig config) { + super(config); + formBodyStyleSettingPane = new FormBodyStyleEditPane(); + addCustomEditorPane(i18nText("Fine-Design_Predefined_Template_Background"), formBodyStyleSettingPane); - @Override - public String title4PopupWindow() { - return Toolkit.i18nText("Fine-Design_Basic_Form_Theme_Profile_Dialog_Title"); + addCustomEditorPane(i18nText("Fine-Design_Predefined_Cell_Style"), createCellStyleSettingPane()); + addCustomEditorPane(i18nText("Fine-Design_Predefined_Chart_Style"), createChartStyleSettingPane()); + + + componentStyleSettingPane = new ComponentStyleEditPane(); + addCustomEditorPane(i18nText("Fine-Design_Predefined_Component_Style"), componentStyleSettingPane); + } + + @Override + protected JPanel createChartStyleSettingPane() { + JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); + chartStyleSettingPane = new ChartStyleFormEditPane(); + container.add(chartStyleSettingPane); + return container; + } + + @Override + protected void populateBean4CustomEditors(FormTheme theme) { + super.populateBean4CustomEditors(theme); + formBodyStyleSettingPane.populateBean(theme.getBodyStyle()); + componentStyleSettingPane.populateBean(theme.getComponentStyle()); + } + + @Override + protected void updateBean4CustomEditors(FormTheme theme) { + super.updateBean4CustomEditors(theme); + ThemedFormBodyStyle formBodyStyle = formBodyStyleSettingPane.updateBean(); + theme.setBodyStyle(formBodyStyle); + + ThemedComponentStyle componentStyle = componentStyleSettingPane.updateBean(); + theme.setComponentStyle(componentStyle); + } } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/ReportThemeProfilePane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/ReportThemeProfilePane.java index 1158a8eb5d..f0f25f677f 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/ReportThemeProfilePane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/ReportThemeProfilePane.java @@ -25,28 +25,37 @@ public class ReportThemeProfilePane extends TemplateThemeProfilePane createThemeEditorPane() { + return new ReportThemeEditorPane(config); } @Override public String title4PopupWindow() { return Toolkit.i18nText("Fine-Design_Basic_Report_Theme_Profile_Dialog_Title"); } + + private static class ReportThemeEditorPane extends TemplateThemeEditorPane { + + private final ReportBodyStyleEditPane reportBodyStyleSettingPane; + + public ReportThemeEditorPane(TemplateThemeConfig config) { + super(config); + this.reportBodyStyleSettingPane = new ReportBodyStyleEditPane(); + addCustomEditorPane(Toolkit.i18nText("Fine-Design_Predefined_Template_Background"), reportBodyStyleSettingPane); + addCustomEditorPane(Toolkit.i18nText("Fine-Design_Predefined_Cell_Style"), createCellStyleSettingPane()); + addCustomEditorPane(Toolkit.i18nText("Fine-Design_Predefined_Chart_Style"), createChartStyleSettingPane()); + } + + @Override + public void populateBean4CustomEditors(ReportTheme theme) { + super.populateBean4CustomEditors(theme); + reportBodyStyleSettingPane.populateBean(theme.getBodyStyle()); + } + + @Override + public void updateBean4CustomEditors(ReportTheme theme) { + super.updateBean4CustomEditors(theme); + theme.setBodyStyle(this.reportBodyStyleSettingPane.updateBean()); + } + } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java new file mode 100644 index 0000000000..a5301e9565 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeEditorPane.java @@ -0,0 +1,315 @@ +package com.fr.design.mainframe.theme; + +import com.fr.base.theme.FineColorFlushUtils; +import com.fr.base.theme.FineColorManager; +import com.fr.base.theme.TemplateTheme; +import com.fr.base.theme.TemplateThemeConfig; +import com.fr.base.theme.settings.ThemedCellStyleList; +import com.fr.base.theme.settings.ThemedColorScheme; +import com.fr.design.designer.IntervalConstants; +import com.fr.design.gui.frpane.AbstractAttrNoScrollPane; +import com.fr.design.gui.frpane.AttributeChangeListener; +import com.fr.design.gui.frpane.UITabbedPane; +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.mainframe.theme.edit.CellStyleListEditPane; +import com.fr.design.mainframe.theme.edit.ChartStyleEditPane; +import com.fr.design.mainframe.theme.edit.ui.ColorListExtendedPane; +import com.fr.design.mainframe.theme.edit.ui.ColorListPane; +import com.fr.design.mainframe.theme.edit.ui.LabelUtils; +import com.fr.design.mainframe.theme.ui.AutoCheckTextField; +import com.fr.design.mainframe.theme.ui.AutoCheckThemeNameTextField; +import com.fr.design.mainframe.theme.ui.BorderUtils; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +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.Dimension; +import java.util.List; + +/** + * @author Starryi + * @version 1.0 + * Created by Starryi on 2021/8/13 + */ +public abstract class TemplateThemeEditorPane extends JPanel { + public static final int LEFT_TITLE_PANE_HEIGHT = 539; + + public static final int RIGHT_PANE_WIDTH = 362; + public static final int RIGHT_PANE_HEIGHT = LEFT_TITLE_PANE_HEIGHT; + + protected AutoCheckThemeNameTextField nameTextField; + private UILabel nameErrorLabel; + protected ColorListPane colorListPane; + private ColorListExtendedPane colorListExtendedPane; + protected CellStyleListEditPane cellStyleSettingPane; + protected ChartStyleEditPane chartStyleSettingPane; + + protected boolean isPopulating = false; + protected UITabbedPane uiTabbedPane; + + private final TemplateThemeConfig config; + + private boolean refreshingThemedColor = false; + private T theme; + + private AttributeChangeListener changeListener; + private AutoCheckTextField.CheckListener themeNameCheckListener; + + public TemplateThemeEditorPane(TemplateThemeConfig config) { + super(); + this.config = config; + theme = config.createNewTheme(); + initializePane(); + } + + private void initializePane() { + setLayout(new BorderLayout(0, 0)); + setPreferredSize(new Dimension(RIGHT_PANE_WIDTH, RIGHT_PANE_HEIGHT)); + JPanel nameEditPane = createNameEditPane(); + add(nameEditPane, BorderLayout.NORTH); + + JPanel settingPane = new JPanel(new BorderLayout(0, IntervalConstants.INTERVAL_L1)); + settingPane.add(createColorSchemeEditPane(), BorderLayout.NORTH); + settingPane.add(createCustomEditorsPane(), BorderLayout.CENTER); + + add(settingPane, BorderLayout.CENTER); + } + + private JPanel createNameEditPane() { + nameErrorLabel = LabelUtils.createLabel(StringUtils.EMPTY, Color.RED); + nameErrorLabel.setVisible(false); + + nameTextField = new AutoCheckThemeNameTextField<>(); + nameTextField.setThemeConfig(config); + nameTextField.setEditable(false); + nameTextField.setEnabled(false); + nameTextField.setPreferredSize(new Dimension(165, 20)); + nameTextField.setNameCheckListener(new AutoCheckTextField.CheckListener() { + @Override + public void onChecked(String error, boolean valid) { + nameErrorLabel.setVisible(StringUtils.isNotEmpty(error)); + nameErrorLabel.setText(error); + + themeNameCheckListener.onChecked(error, valid); + } + }); + + double p = TableLayout.PREFERRED; + + JPanel container = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{ + new Component[] { LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Name")), nameTextField }, + new Component[] { null, nameErrorLabel }, + }, new double[] { 20, 20}, new double[] { p, 165}, IntervalConstants.INTERVAL_L1, IntervalConstants.INTERVAL_W0); + + container.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); + return container; + } + + private JPanel createColorSchemeEditPane() { + colorListPane = new ColorListPane(); + colorListExtendedPane = new ColorListExtendedPane(); + + colorListExtendedPane.setBackground(null); + colorListExtendedPane.setOpaque(false); + JPanel extendedBackgroundContainer = FRGUIPaneFactory.createBorderLayout_S_Pane(); + extendedBackgroundContainer.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + extendedBackgroundContainer.setBackground(Color.WHITE); + extendedBackgroundContainer.add(colorListExtendedPane, BorderLayout.WEST); + JPanel extendedContainer = FRGUIPaneFactory.createBorderLayout_S_Pane(); + extendedContainer.add(extendedBackgroundContainer, BorderLayout.WEST); + + double p = TableLayout.PREFERRED; + double[] rowSize = new double[]{p, p, p}; + double[] columnSize = {p, p}; + + JPanel colorListContainerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + colorListContainerPane.add(colorListPane, BorderLayout.WEST); + + JPanel previewLabelPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + previewLabelPane.add(LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Preview_Label")), BorderLayout.NORTH); + + UILabel tipLabel = LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Edit_Tip"), new Color(153, 153, 153)); + tipLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); + + JPanel content = TableLayoutHelper.createGapTableLayoutPane(new JComponent[][]{ + {LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Edit_Label")), colorListContainerPane}, + {null, tipLabel}, + {previewLabelPane, extendedContainer}, + }, + rowSize, columnSize, 18, 7); + content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + JPanel borderContainer = new JPanel(new BorderLayout()); + borderContainer.setBorder(BorderUtils.createTitleBorder(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Title"))); + borderContainer.add(content); + + JPanel container = new JPanel(new BorderLayout()); + container.add(borderContainer, BorderLayout.CENTER); + + colorListPane.addColorChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (refreshingThemedColor) { + return; + } + List colors = colorListPane.update(); + refreshingThemedColor = true; + onColorSchemeChanged(colors); + refreshingThemedColor = false; + + fireAttributeChange(); + } + }); + + return container; + } + private void onColorSchemeChanged(List colors) { + colorListExtendedPane.populate(colors); + FineColorManager.FineColorReplaceByColorScheme replaceByColorScheme = new FineColorManager.FineColorReplaceByColorScheme(colors); + T theme = updateBean(); + FineColorFlushUtils.replaceCacheObject(theme, replaceByColorScheme); + FineColorManager.traverse(theme, replaceByColorScheme); + populateBean4CustomEditors(theme); + //图表渐变色 + chartStyleSettingPane.populateGradientBar(colors); + this.repaint(); + } + + protected JPanel createCustomEditorsPane() { + JPanel container = new JPanel(new BorderLayout()); + container.setBorder(BorderUtils.createTitleBorder(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Custom_Settings_Title"))); + + uiTabbedPane = new UITabbedPane(); + uiTabbedPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 1)); + container.add(uiTabbedPane, BorderLayout.CENTER); + + return container; + } + + public void addCustomEditorPane(String title, final Component component) { + AbstractAttrNoScrollPane settingPane = new NoBorderAbstractAttrNoScrollPane() { + @Override + protected JPanel createContentPane() { + JPanel contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + contentPane.add(component, BorderLayout.CENTER); + return contentPane; + } + }; + settingPane.addAttributeChangeListener(new AttributeChangeListener() { + @Override + public void attributeChange() { + fireAttributeChange(); + } + }); + uiTabbedPane.addTab(title, settingPane); + } + protected JPanel createCellStyleSettingPane() { + JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); + cellStyleSettingPane = new CellStyleListEditPane(); + cellStyleSettingPane.registerAttrChangeListener(new AttributeChangeListener() { + @Override + public void attributeChange() { + fireAttributeChange(); + } + }); + container.add(cellStyleSettingPane); + return container; + } + protected JPanel createChartStyleSettingPane() { + JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); + chartStyleSettingPane = new ChartStyleEditPane(); + container.add(chartStyleSettingPane); + return container; + } + + public void populateBean(T theme) { + this.theme = theme; + isPopulating = true; + + String name = theme.getName(); + setThemeNameEditable(StringUtils.isEmpty(name)); + + nameTextField.setText(name); + + colorListPane.populate(theme.getColorScheme().getColors()); + colorListExtendedPane.populate(colorListPane.update()); + + populateBean4CustomEditors(theme); + isPopulating = false; + } + + protected void populateBean4CustomEditors(T theme) { + cellStyleSettingPane.populateBean(theme.getCellStyleList()); + chartStyleSettingPane.populateBean(theme.getChartStyle()); + } + + public T updateBean() { + if (theme == null) { + theme = config.createNewTheme(); + } + + theme.setName(this.nameTextField.getText()); + + ThemedColorScheme colorScheme = theme.getColorScheme(); + colorScheme.setColors(this.colorListPane.update()); + theme.setColorScheme(colorScheme); + + updateBean4CustomEditors(theme); + + return theme; + } + + protected void updateBean4CustomEditors(T theme) { + ThemedCellStyleList cellStyleConfig = this.cellStyleSettingPane.updateBean(); + theme.setCellStyleList(cellStyleConfig); + + theme.setChartStyle(this.chartStyleSettingPane.updateBean()); + } + + public void setThemeNameEditable(boolean editable) { + nameTextField.setEditable(editable); + nameTextField.setEnabled(editable); + } + + public boolean checkNameValid() { + return nameTextField.checkValid(); + } + + public List getCurrentColorScheme() { + return colorListPane.update(); + } + + private void fireAttributeChange() { + if (!isPopulating && !refreshingThemedColor && changeListener != null) { + changeListener.attributeChange(); + } + } + + public void addAttributeChangeListener(AttributeChangeListener changeListener) { + this.changeListener = changeListener; + } + public void addThemeNameCheckListener(AutoCheckTextField.CheckListener checkListener) { + this.themeNameCheckListener = checkListener; + } + + private static abstract class NoBorderAbstractAttrNoScrollPane extends AbstractAttrNoScrollPane { + @Override + protected void initContentPane() { + super.initContentPane(); + if (leftContentPane != null) { + // 修正 AbstractAttrNoScrollPane 的默认行为 + leftContentPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + } + } + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeListPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeListPane.java index fca01386c4..768eb37242 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeListPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeListPane.java @@ -32,7 +32,7 @@ public class TemplateThemeListPane extends BasicPane { public static final int BLOCK_GAP = IntervalConstants.INTERVAL_L1; public static final int CONTENT_WIDTH = TemplateThemeBlock.CONTENT_WIDTH * BLOCK_COUNT_ROW_LINE + BLOCK_GAP * (BLOCK_COUNT_ROW_LINE - 1) + 10; public static final int BLOCK_ROWS_PER_PAGE = 3; - public static final int CONTENT_HEIGHT = TemplateThemeBlock.CONTENT_HEIGHT * BLOCK_ROWS_PER_PAGE + BLOCK_GAP * (BLOCK_ROWS_PER_PAGE + 1); + public static final int CONTENT_HEIGHT = Math.min(527, TemplateThemeBlock.CONTENT_HEIGHT * BLOCK_ROWS_PER_PAGE + BLOCK_GAP * (BLOCK_ROWS_PER_PAGE + 1)); public static final int ASYNC_FETCH_THEME_THREAD_COUNT = 10; private final AsyncThemeFetcher asyncThemeFetcher; diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemePreviewPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemePreviewPane.java index 19885763cc..c302cf1ad8 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemePreviewPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemePreviewPane.java @@ -48,9 +48,6 @@ public abstract class TemplateThemePreviewPane extends int height = getHeight(); try { - // 使用TYPE_INT_RGB和new Color(255, 255, 255, 1)设置有透明背景buffer image, - // 使得创建出来的透明像素是(255, 255, 255, 1),而不是(0, 0, 0, 0) - // 这样不支持透明通道缩略图的旧设计器打开新设计器创建的模版时,就不会创建出拥有黑色背景的缩略图 image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = image.createGraphics(); // 创建一个支持透明背景的buffer image @@ -58,8 +55,6 @@ public abstract class TemplateThemePreviewPane extends g2d.dispose(); g2d = image.createGraphics(); - g2d.fillRect(0, 0, width, height); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.clipRect(0, 0, width, height); @@ -80,6 +75,9 @@ public abstract class TemplateThemePreviewPane extends @Override public void paintComponent(Graphics g) { g.clearRect(0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT); + // 添加个默认白色,否则因为上面的clearRect导致无背景时会出现黑色 + g.setColor(Color.WHITE); + g.fillRect(0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT); paintBackground((Graphics2D) g); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeProfilePane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeProfilePane.java index d5103f8246..f3187acaab 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeProfilePane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/TemplateThemeProfilePane.java @@ -1,58 +1,39 @@ package com.fr.design.mainframe.theme; -import com.fr.base.theme.FineColorFlushUtils; -import com.fr.base.theme.FineColorManager; import com.fr.base.theme.TemplateTheme; import com.fr.base.theme.TemplateThemeConfig; import com.fr.base.theme.settings.ThemeThumbnail; -import com.fr.base.theme.settings.ThemedCellStyleList; -import com.fr.base.theme.settings.ThemedColorScheme; -import com.fr.design.designer.IntervalConstants; +import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.FineJOptionPane; -import com.fr.design.gui.frpane.AbstractAttrNoScrollPane; import com.fr.design.gui.frpane.AttributeChangeListener; -import com.fr.design.gui.frpane.UITabbedPane; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; -import com.fr.design.gui.itextfield.UITextField; 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.mainframe.theme.dialog.TemplateThemeProfileDialog; -import com.fr.design.mainframe.theme.edit.CellStyleListEditPane; -import com.fr.design.mainframe.theme.edit.ChartStyleEditPane; -import com.fr.design.mainframe.theme.edit.ui.ColorListExtendedPane; -import com.fr.design.mainframe.theme.edit.ui.ColorListPane; import com.fr.design.mainframe.theme.edit.ui.LabelUtils; +import com.fr.design.mainframe.theme.ui.AutoCheckTextField; +import com.fr.design.mainframe.theme.ui.AutoCheckThemeNameTextField; import com.fr.design.mainframe.theme.ui.BorderUtils; +import com.fr.design.mainframe.toast.DesignerToastMsgUtil; import com.fr.design.utils.gui.GUICoreUtils; -import com.fr.general.Inter; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; import com.fr.transaction.CallBackAdaptor; + import javax.swing.BorderFactory; -import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.util.List; import static com.fr.design.i18n.Toolkit.i18nText; @@ -61,71 +42,35 @@ import static com.fr.design.i18n.Toolkit.i18nText; * @version 1.0 * Created by Starryi on 2021/8/13 */ -public abstract class TemplateThemeProfilePane extends AbstractAttrNoScrollPane { +public abstract class TemplateThemeProfilePane extends BasicPane { public static final int TITLE_BORDER_FONT = 12; public static final int LEFT_TITLE_PANE_WIDTH = 627; public static final int LEFT_TITLE_PANE_HEIGHT = 539; public static final int PREVIEW_PANE_WIDTH = LEFT_TITLE_PANE_WIDTH - 10; public static final int PREVIEW_PANE_HEIGHT = LEFT_TITLE_PANE_HEIGHT - TITLE_BORDER_FONT - 16; - public static final int RIGHT_PANE_WIDTH = 362; - public static final int RIGHT_PANE_HEIGHT = LEFT_TITLE_PANE_HEIGHT; - public static final int COLOR_SCHEME_TITLE_PANE_WIDTH = 298; - public static final int COLOR_SCHEME_TITLE_PANE_HEIGHT = 174 + TITLE_BORDER_FONT / 2; - protected TemplateThemePreviewPane themePreviewPane; - - protected UITextField nameTextField; - private UILabel nameErrorLabel; - protected ColorListPane colorListPane; - private ColorListExtendedPane colorListExtendedPane; - protected CellStyleListEditPane cellStyleSettingPane; - protected ChartStyleEditPane chartStyleSettingPane; + protected TemplateThemeEditorPane themeEditorPane; protected boolean isPopulating = false; - protected UITabbedPane uiTabbedPane; + protected boolean isMutable = false; - private final TemplateThemeConfig config; + protected final TemplateThemeConfig config; private UIButton saveButton = new UIButton(); private UIButton saveAsButton = new UIButton(); - private boolean refreshingThemedColor = false; - - private boolean currentIsNewTheme; - private T theme; - public TemplateThemeProfilePane(TemplateThemeConfig config) { + super(); this.config = config; - theme = config.createNewTheme(); - } - @Override - protected JPanel createContentPane() { - JPanel container = new JPanel(new BorderLayout(5, 0)); - container.add(createLeftPane(), BorderLayout.CENTER); - container.add(createRightPane(), BorderLayout.EAST); - addAttributeChangeListener(new AttributeChangeListener() { - @Override - public void attributeChange() { - if (!isPopulating) { - valueChangeAction(); - } - } - }); - return container; - } - - @Override - protected void initContentPane() { - super.initContentPane(); - if (leftContentPane != null) { - // 修正 AbstractAttrNoScrollPane 的默认行为 - leftContentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 6)); - } + initializePane(); } - public void valueChangeAction() { - themePreviewPane.refresh(updateBean()); + private void initializePane() { + setLayout(new BorderLayout(5, 0)); + setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 6)); + add(createLeftPane(), BorderLayout.CENTER); + add(createRightPane(), BorderLayout.EAST); } @Override @@ -149,221 +94,55 @@ public abstract class TemplateThemeProfilePane extends return titleContainer; } private JPanel createRightPane() { - JPanel container = new JPanel(new BorderLayout(0, 0)); - container.setPreferredSize(new Dimension(RIGHT_PANE_WIDTH, RIGHT_PANE_HEIGHT)); - JPanel nameEditPane = createNameEditPane(); - container.add(nameEditPane, BorderLayout.NORTH); - - JPanel settingPane = new JPanel(new BorderLayout(0, IntervalConstants.INTERVAL_L1)); - container.add(settingPane, BorderLayout.CENTER); - settingPane.add(createColorSchemeEditPane(), BorderLayout.NORTH); - settingPane.add(createCustomEditorsPane(), BorderLayout.CENTER); - - uiTabbedPane.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - valueChangeAction(); - } - }); - - return container; - } - - private JPanel createNameEditPane() { - nameErrorLabel = LabelUtils.createLabel(StringUtils.EMPTY, Color.RED); - nameErrorLabel.setVisible(false); - - nameTextField = new UITextField(); - nameTextField.setEnabled(false); - nameTextField.setPreferredSize(new Dimension(165, 20)); - nameTextField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(currentIsNewTheme, false, currentIsNewTheme, true, nameTextField, nameErrorLabel, saveButton); - } - } - - @Override - public void removeUpdate(DocumentEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(currentIsNewTheme, false, currentIsNewTheme, true, nameTextField, nameErrorLabel, saveButton); - } - } - - @Override - public void changedUpdate(DocumentEvent e) { - - } - }); - nameTextField.addFocusListener(new FocusAdapter() { + themeEditorPane = createThemeEditorPane(); + themeEditorPane.addAttributeChangeListener(new AttributeChangeListener() { @Override - public void focusGained(FocusEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(currentIsNewTheme, false, currentIsNewTheme, true, nameTextField, nameErrorLabel, saveButton); - } - } - - @Override - public void focusLost(FocusEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(currentIsNewTheme, true, currentIsNewTheme, true, nameTextField, nameErrorLabel, saveButton); + public void attributeChange() { + if (isPopulating) { + return; } + themePreviewPane.refresh(updateBean()); + saveButton.setEnabled(themeEditorPane.checkNameValid() && isMutable); } }); - - double p = TableLayout.PREFERRED; - - JPanel container = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{ - new Component[] { LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Name")), nameTextField }, - new Component[] { null, nameErrorLabel }, - }, new double[] { 20, 20}, new double[] { p, 165}, IntervalConstants.INTERVAL_L1, IntervalConstants.INTERVAL_W0); - - container.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); - return container; - } - private JPanel createColorSchemeEditPane() { - colorListPane = new ColorListPane(); - colorListExtendedPane = new ColorListExtendedPane(); - - JPanel extendedContainer = FRGUIPaneFactory.createBorderLayout_S_Pane(); - colorListExtendedPane.setBackground(null); - colorListExtendedPane.setOpaque(false); - extendedContainer.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - extendedContainer.setBackground(Color.WHITE); - extendedContainer.add(colorListExtendedPane); - - double p = TableLayout.PREFERRED; - double[] rowSize = new double[]{p, p, p}; - double[] columnSize = {p, p}; - - JPanel colorListContainerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); - colorListContainerPane.add(colorListPane, BorderLayout.WEST); - - JPanel previewLabelPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); - previewLabelPane.add(LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Preview_Label")), BorderLayout.NORTH); - - UILabel tipLabel = LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Edit_Tip"), new Color(153, 153, 153)); - tipLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); - - JPanel content = TableLayoutHelper.createGapTableLayoutPane(new JComponent[][]{ - {LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Edit_Label")), colorListContainerPane}, - {null, tipLabel}, - {previewLabelPane, extendedContainer}, - }, - rowSize, columnSize, 18, 7); - content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - - JPanel borderContainer = new JPanel(new BorderLayout()); - borderContainer.setPreferredSize(new Dimension(COLOR_SCHEME_TITLE_PANE_WIDTH, COLOR_SCHEME_TITLE_PANE_HEIGHT)); - borderContainer.setBorder(BorderUtils.createTitleBorder(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Title"))); - borderContainer.add(content); - - JPanel container = new JPanel(new BorderLayout()); - container.add(borderContainer, BorderLayout.WEST); - - colorListPane.addColorChangeListener(new ChangeListener() { + themeEditorPane.addThemeNameCheckListener(new AutoCheckTextField.CheckListener() { @Override - public void stateChanged(ChangeEvent e) { - if (refreshingThemedColor) { + public void onChecked(String error, boolean valid) { + if (isPopulating) { return; } - List colors = colorListPane.update(); - refreshingThemedColor = true; - onColorSchemeChanged(colors); - refreshingThemedColor = false; + saveButton.setEnabled(valid && isMutable); } }); - return container; - } - protected JPanel createCustomEditorsPane() { - JPanel container = new JPanel(new BorderLayout()); - container.setBorder(BorderUtils.createTitleBorder(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Custom_Settings_Title"))); - - uiTabbedPane = new UITabbedPane(); - uiTabbedPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 1)); - container.add(uiTabbedPane, BorderLayout.NORTH); - - return container; - } - public void addCustomEditorPane(String title, JComponent component) { - component.setPreferredSize(new Dimension(317, 239)); - uiTabbedPane.addTab(title, component); - } - protected JPanel createCellStyleSettingPane() { - JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); - cellStyleSettingPane = new CellStyleListEditPane(); - cellStyleSettingPane.registerAttrChangeListener(new AttributeChangeListener() { - @Override - public void attributeChange() { - valueChangeAction(); - } - }); - container.add(cellStyleSettingPane); - return container; - } - protected JPanel createChartStyleSettingPane() { - JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); - chartStyleSettingPane = new ChartStyleEditPane(); - container.add(chartStyleSettingPane); - return container; + return themeEditorPane; } public abstract TemplateThemePreviewPane createThemePreviewPane(); + public abstract TemplateThemeEditorPane createThemeEditorPane(); - public void onColorSchemeChanged(List colors) { - colorListExtendedPane.populate(colors); - FineColorManager.FineColorReplaceByColorScheme replaceByColorScheme = new FineColorManager.FineColorReplaceByColorScheme(colors); - T theme = updateBean(); + public void populateBean(T theme) { + isPopulating = true; - FineColorFlushUtils.replaceCacheObject(theme, replaceByColorScheme); - FineColorManager.traverse(theme, replaceByColorScheme); - populateBean4CustomEditors(theme); - //图表渐变色 - chartStyleSettingPane.populateGradientBar(colors); + isMutable = theme.isMutable(); - themePreviewPane.refresh(theme); - this.repaint(); - } + themeEditorPane.populateBean(theme); - public void populateBean(T theme) { - this.theme = theme; - isPopulating = true; + themePreviewPane.refresh(theme); String name = theme.getName(); - currentIsNewTheme = config.cachedFetch(name) == null; - - nameTextField.setText(name); - nameTextField.setEnabled(StringUtils.isEmpty(name)); if (saveButton != null) { - saveButton.setEnabled(theme.isMutable() && !currentIsNewTheme); + saveButton.setEnabled(isMutable); } if (saveAsButton != null) { - saveAsButton.setEnabled(!currentIsNewTheme); + saveAsButton.setEnabled(StringUtils.isNotEmpty(name)); } - - colorListPane.populate(theme.getColorScheme().getColors()); - colorListExtendedPane.populate(colorListPane.update()); - - populateBean4CustomEditors(theme); - - themePreviewPane.refresh(theme); isPopulating = false; } - protected void populateBean4CustomEditors(T theme) { - cellStyleSettingPane.populateBean(theme.getCellStyleList()); - chartStyleSettingPane.populateBean(theme.getChartStyle()); - } - public T updateBean() { - if (theme == null) { - theme = config.createNewTheme(); - } - - theme.setName(this.nameTextField.getText()); + T theme = themeEditorPane.updateBean(); Image thumbnailImage = themePreviewPane.createThumbnailImage(); if (thumbnailImage != null) { @@ -372,24 +151,55 @@ public abstract class TemplateThemeProfilePane extends theme.setThumbnail(thumbnail); } - ThemedCellStyleList cellStyleConfig = this.cellStyleSettingPane.updateBean(); - theme.setCellStyleList(cellStyleConfig); - - ThemedColorScheme colorScheme = theme.getColorScheme(); - colorScheme.setColors(this.colorListPane.update()); - theme.setColorScheme(colorScheme); - - theme.setChartStyle(this.chartStyleSettingPane.updateBean()); - - updateBean(theme); return theme; } - public List getCurrentColorScheme() { - return colorListPane.update(); + public TemplateThemeEditorPane getThemeEditorPane() { + return themeEditorPane; } - protected abstract void updateBean(T theme); + public void save(CallBackAdaptor callBack) { + T theme; + try { + theme = (T) updateBean().clone(); + } catch (CloneNotSupportedException ex) { + FineLoggerFactory.getLogger().error(ex.getMessage(), ex); + return; + } + save(theme, callBack); + } + public void save(String name, CallBackAdaptor callBack) { + T theme = null; + try { + theme = (T) updateBean().clone(); + } catch (CloneNotSupportedException ex) { + FineLoggerFactory.getLogger().error(ex.getMessage(), ex); + return; + } + theme.setName(name); + save(theme, callBack); + } + private void save(T theme, CallBackAdaptor callBack) { + theme.setRemovable(true); + theme.setMutable(true); + config.addTheme(theme, true, new CallBackAdaptor() { + @Override + public void afterCommit() { + super.afterCommit(); + themeEditorPane.setThemeNameEditable(false); + callBack.afterCommit(); + } + + @Override + public void afterRollback() { + super.afterRollback(); + FineJOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(TemplateThemeProfilePane.this), + i18nText("Fine-Design_Basic_Template_Theme_Operation_Failed_Tip"), + i18nText("Fine-Design_Basic_Alert"), + JOptionPane.WARNING_MESSAGE); + } + }); + } public UIButton createSaveButton() { saveButton = new UIButton(); @@ -398,29 +208,15 @@ public abstract class TemplateThemeProfilePane extends saveButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - T theme = updateBean(); - boolean canBeSaved = checkThemeCanBeSavedAndUpdateUI(currentIsNewTheme, true, currentIsNewTheme, true, nameTextField, nameErrorLabel, saveButton); - if (canBeSaved && theme != null) { - theme.setName(nameTextField.getText()); - config.addTheme(theme, true, new CallBackAdaptor() { - @Override - public void afterCommit() { - super.afterCommit(); - currentIsNewTheme = false; - nameTextField.setEnabled(false); - saveAsButton.setEnabled(true); - } - - @Override - public void afterRollback() { - super.afterRollback(); - FineJOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(TemplateThemeProfilePane.this), - i18nText("Fine-Design_Basic_Template_Theme_Operation_Failed_Tip"), - i18nText("Fine-Design_Basic_Alert"), - JOptionPane.WARNING_MESSAGE); - } - }); - } + save(new CallBackAdaptor() { + @Override + public void afterCommit() { + super.afterCommit(); + saveButton.setEnabled(false); + saveAsButton.setEnabled(true); + DesignerToastMsgUtil.toastPrompt(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Profile_Pane_Save_Successfully")); + } + }); } }); return saveButton; @@ -441,7 +237,7 @@ public abstract class TemplateThemeProfilePane extends private class SaveAsDialog extends JDialog { private final TemplateThemeProfileDialog parent; - private UITextField nameTextField; + private AutoCheckThemeNameTextField nameTextField; private UILabel nameErrorLabel; private UIButton confirmButton; @@ -467,6 +263,18 @@ public abstract class TemplateThemeProfilePane extends } private void initializeComponents() { + nameTextField = new AutoCheckThemeNameTextField<>(); + nameTextField.setThemeConfig(config); + nameTextField.setPreferredSize(new Dimension(180, 20)); + nameTextField.setNameCheckListener(new AutoCheckTextField.CheckListener() { + @Override + public void onChecked(String error, boolean valid) { + nameErrorLabel.setVisible(StringUtils.isNotEmpty(error)); + nameErrorLabel.setText(error); + confirmButton.setEnabled(valid); + } + }); + nameErrorLabel = LabelUtils.createLabel(StringUtils.EMPTY, Color.RED); nameErrorLabel.setBorder(BorderFactory.createEmptyBorder(10, 20, 0, 0)); @@ -475,47 +283,16 @@ public abstract class TemplateThemeProfilePane extends confirmButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - saveAsNew(nameTextField.getText()); - } - }); - - nameTextField = new UITextField(); - nameTextField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(true, false, true, true, nameTextField, nameErrorLabel, confirmButton); - } - } - - @Override - public void removeUpdate(DocumentEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(true, false, true, true, nameTextField, nameErrorLabel, confirmButton); - } - } - - @Override - public void changedUpdate(DocumentEvent e) { - - } - }); - nameTextField.addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(true, false, true, true, nameTextField, nameErrorLabel, confirmButton); - } - } - - @Override - public void focusLost(FocusEvent e) { - if (isEnabled()) { - checkThemeCanBeSavedAndUpdateUI(true, true, true, true, nameTextField, nameErrorLabel, confirmButton); - } + save(nameTextField.getText(), new CallBackAdaptor() { + @Override + public void afterCommit() { + super.afterCommit(); + exit(); + parent.exit(); + } + }); } }); - nameTextField.setPreferredSize(new Dimension(180, 20)); } private JPanel createActionsContainer() { @@ -549,82 +326,8 @@ public abstract class TemplateThemeProfilePane extends return container; } - private void saveAsNew(String name) { - T newThemeObject = null; - try { - newThemeObject = (T) updateBean().clone(); - } catch (CloneNotSupportedException ex) { - FineLoggerFactory.getLogger().error(ex.getMessage(), ex); - return; - } - boolean canBeSaved = checkThemeCanBeSavedAndUpdateUI(true, true, true, true, nameTextField, nameErrorLabel, confirmButton); - if (canBeSaved && newThemeObject != null) { - newThemeObject.setName(name); - newThemeObject.setRemovable(true); - newThemeObject.setMutable(true); - config.addTheme(newThemeObject, true, new CallBackAdaptor() { - @Override - public void afterCommit() { - super.afterCommit(); - exit(); - parent.exit(); - } - - @Override - public void afterRollback() { - super.afterRollback(); - FineJOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(TemplateThemeProfilePane.this), - i18nText("Fine-Design_Basic_Template_Theme_Operation_Failed_Tip"), - i18nText("Fine-Design_Basic_Alert"), - JOptionPane.WARNING_MESSAGE); - } - }); - } - } - public void exit() { this.dispose(); } } - - private boolean isThemeNameEmpty(String name) { - return StringUtils.isEmpty(name); - } - private boolean isThemeNameDuplicated(String name) { - return config.cachedFetch(name) != null; - } - private boolean checkThemeCanBeSavedAndUpdateUI( - boolean checkEmpty, - boolean displayEmptyTip, - boolean checkDuplicated, - boolean displayDuplicatedTip, - UITextField textField, - UILabel errorLabel, UIButton... actionButtons) { - String name = textField.getText(); - - boolean valid = true; - errorLabel.setText(StringUtils.EMPTY); - if (checkEmpty && isThemeNameEmpty(name)) { - valid = false; - if (displayEmptyTip) { - errorLabel.setText(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Empty_Name_Error_Tip")); - } - } else if (checkDuplicated && isThemeNameDuplicated(name)) { - valid = false; - if (displayDuplicatedTip) { - errorLabel.setText(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Duplicated_Name_Error_Tip")); - } - } - - errorLabel.setVisible(!valid); - if (actionButtons != null && actionButtons.length > 0) { - for (UIButton button : actionButtons) { - if (button != null) { - button.setEnabled(valid); - } - } - } - - return valid; - } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeProfileDialog.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeProfileDialog.java index 3a6a9d3838..fe31bd13ac 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeProfileDialog.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/dialog/TemplateThemeProfileDialog.java @@ -51,7 +51,7 @@ public class TemplateThemeProfileDialog extends Templat public static List getEditingColorScheme() { if (currentVisibleProfilePane != null) { - return currentVisibleProfilePane.getCurrentColorScheme(); + return currentVisibleProfilePane.getThemeEditorPane().getCurrentColorScheme(); } return null; } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ComponentStyleEditPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ComponentStyleEditPane.java index 53e216b0d6..de62a195d4 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ComponentStyleEditPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ComponentStyleEditPane.java @@ -2,8 +2,7 @@ package com.fr.design.mainframe.theme.edit; import com.fr.base.theme.settings.ThemedComponentStyle; import com.fr.design.designer.IntervalConstants; -import com.fr.design.dialog.AttrScrollPane; -import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.style.ComponentBodyStylePane; import com.fr.design.gui.style.ComponentIntegralStylePane; import com.fr.design.gui.style.ComponentTitleStylePane; @@ -18,10 +17,9 @@ import com.fr.general.act.BorderPacker; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JPanel; -import javax.swing.JScrollPane; +import javax.swing.JTextArea; import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Dimension; /** * @author Starryi @@ -50,7 +48,8 @@ public class ComponentStyleEditPane extends JPanel { }, new JComponent[] { createTabContainer(componentTitleStylePane), createTabContainer(componentBodyStylePane), createTabContainer(componentIntegralStylePane) } ); - add(content, BorderLayout.NORTH); + content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + add(content, BorderLayout.CENTER); } public void populateBean(ThemedComponentStyle style) { @@ -115,21 +114,20 @@ public class ComponentStyleEditPane extends JPanel { } private JComponent createTabContainer(final JPanel component) { - JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); - container.setPreferredSize(new Dimension(container.getPreferredSize().width, 220)); + JPanel content = FRGUIPaneFactory.createBorderLayout_S_Pane(); JPanel tipLabelPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); tipLabelPane.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0)); - UILabel tipLabel = LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Component_Style_Tip"), new Color(153, 153, 153)); + JTextArea tipLabel = LabelUtils.createAutoWrapLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Component_Style_Tip"), new Color(153, 153, 153)); tipLabelPane.add(tipLabel); - container.add(tipLabelPane, BorderLayout.NORTH); - - container.add(new AttrScrollPane() { - @Override - protected JPanel createContentPane() { - return component; - } - }, BorderLayout.CENTER); + content.add(tipLabelPane, BorderLayout.NORTH); + content.add(component, BorderLayout.CENTER); + + UIScrollPane scrollPane = new UIScrollPane(content); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + + JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); + container.add(scrollPane, BorderLayout.CENTER); return container; diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListPane.java index adeca3d7a5..5c282033f0 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/ColorListPane.java @@ -40,6 +40,8 @@ public class ColorListPane extends JPanel implements UIObserver { private final EventListenerList colorChangeListenerList = new EventListenerList(); private UIObserverListener uiObserverListener; + private boolean isPopulating = false; + public ColorListPane() { this(DEFAULT_COLOR_COUNT, DEFAULT_COLOR_SIZE, DEFAULT_COLOR_GAP); } @@ -61,6 +63,9 @@ public class ColorListPane extends JPanel implements UIObserver { colorButton.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { + if (isPopulating) { + return; + } colorList.set(index, colorButton.getSelectObject()); fireColorStateChanged(); } @@ -88,6 +93,7 @@ public class ColorListPane extends JPanel implements UIObserver { } public void populate(List colors) { + isPopulating = true; colorList.clear(); for (int i = 0; i < colors.size(); i++) { Color color = colors.get(i); @@ -96,6 +102,7 @@ public class ColorListPane extends JPanel implements UIObserver { colorButtons.get(i).setSelectObject(color); } } + isPopulating = false; } public List update() { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/LabelUtils.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/LabelUtils.java index 2b099ed306..13769cfc64 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/LabelUtils.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/LabelUtils.java @@ -3,6 +3,9 @@ package com.fr.design.mainframe.theme.edit.ui; import com.fr.design.gui.ilable.UILabel; import com.fr.general.FRFont; +import javax.swing.BorderFactory; +import javax.swing.JTextArea; +import javax.swing.plaf.basic.BasicTextAreaUI; import java.awt.Color; import java.awt.Font; @@ -26,4 +29,20 @@ public class LabelUtils { uiLabel.setFont(newFont); return uiLabel; } + + public static JTextArea createAutoWrapLabel(String title, Color color) { + JTextArea tipLabel = new JTextArea(); + tipLabel.setUI(new BasicTextAreaUI()); + tipLabel.setForeground(color); + Font newFont = FRFont.getInstance(tipLabel.getFont().getFontName(), Font.PLAIN, 12); + tipLabel.setFont(newFont); + tipLabel.setBorder(BorderFactory.createEmptyBorder()); + tipLabel.setEnabled(false); + tipLabel.setText(title); + tipLabel.setLineWrap(true); + tipLabel.setWrapStyleWord(true); + tipLabel.setOpaque(false); + tipLabel.setBackground(null); + return tipLabel; + } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/TabbedPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/TabbedPane.java index 3f4667e111..c1c8371674 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/TabbedPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/ui/TabbedPane.java @@ -25,7 +25,6 @@ public class TabbedPane extends JPanel { public TabbedPane(String[] names, JComponent[] panes) { setLayout(FRGUIPaneFactory.createBorderLayout()); - setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); for (JComponent pane :panes) { pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/preview/ecpreview/ECReportPreviewPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/preview/ecpreview/ECReportPreviewPane.java index 231233196d..281624ef32 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/preview/ecpreview/ECReportPreviewPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/preview/ecpreview/ECReportPreviewPane.java @@ -64,8 +64,8 @@ public class ECReportPreviewPane extends UINoOpaquePanel implements ThemePreview northPane.add(bigTitleCell, BorderLayout.NORTH); CornerPreviewCell cornerCell = new CornerPreviewCell(new String[]{Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Profile_Pane_EC_Product"), Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Profile_Pane_EC_Data"), Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Profile_Pane_EC_Statistical_Dimension")}, - new Point2D[]{new Point(159, 71), new Point(226, 49)}); - cornerCell.setPreferredSize(new Dimension(226, 71)); + new Point2D[]{new Point(159, 71), new Point(225, 49)}); + cornerCell.setPreferredSize(new Dimension(225, 71)); headerCellList.add(cornerCell); northPane.add(cornerCell, BorderLayout.WEST); JPanel centerPane = FRGUIPaneFactory.createBorderLayout_NO_Opaque_Pane(); @@ -99,7 +99,7 @@ public class ECReportPreviewPane extends UINoOpaquePanel implements ThemePreview JPanel gridPane = new UINoOpaquePanel(new GridLayout(5, 1)); for (int i = 0; i < 5; i++) { PreviewCell cell = new PreviewCell(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Profile_Pane_EC_Apple_Juice")); - cell.setPreferredSize(new Dimension(114, 31)); + cell.setPreferredSize(new Dimension(113, 31)); headerCellList.add(cell); gridPane.add(cell); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckTextField.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckTextField.java new file mode 100644 index 0000000000..9ce5a3d5d3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckTextField.java @@ -0,0 +1,100 @@ +package com.fr.design.mainframe.theme.ui; + +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.stable.StringUtils; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +/** + * @author Starryi + * @version 1.0 + * Created by Starryi on 2021/9/18 + */ +public class AutoCheckTextField extends UITextField implements DocumentListener, FocusListener { + private DuplicateChecker duplicatedChecker; + private CheckListener checkListener; + + public AutoCheckTextField() { + getDocument().addDocumentListener(this); + addFocusListener(this); + } + + public void setDuplicatedChecker(DuplicateChecker checker) { + this.duplicatedChecker = checker; + } + public void setNameCheckListener(CheckListener checkListener) { + this.checkListener = checkListener; + } + + private boolean isEmpty(String name) { + return StringUtils.isEmpty(name); + } + private boolean isDuplicated(String name) { + if (duplicatedChecker != null) { + return duplicatedChecker.isDuplicated(name); + } + return false; + } + private boolean checkValid(boolean notifyEmptyTip) { + String name = getText(); + + String error = StringUtils.EMPTY; + boolean valid = true; + if (isEditable()) { + if (isEmpty(name)) { + valid = false; + if (notifyEmptyTip) { + error = Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Empty_Name_Error_Tip"); + } + } else if (isDuplicated(name)) { + valid = false; + error = Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Duplicated_Name_Error_Tip"); + } + } + + if (checkListener != null) { + checkListener.onChecked(error, valid); + } + + return valid; + } + public boolean checkValid() { + return checkValid(true); + } + + @Override + public void insertUpdate(DocumentEvent e) { + checkValid(false); + } + + @Override + public void removeUpdate(DocumentEvent e) { + checkValid(false); + } + + @Override + public void changedUpdate(DocumentEvent e) { + + } + + @Override + public void focusGained(FocusEvent e) { + checkValid(false); + } + + @Override + public void focusLost(FocusEvent e) { + checkValid(true); + } + + public interface CheckListener { + void onChecked(String error, boolean valid); + } + public interface DuplicateChecker { + boolean isDuplicated(String name); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckThemeNameTextField.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckThemeNameTextField.java new file mode 100644 index 0000000000..804c8493d2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/ui/AutoCheckThemeNameTextField.java @@ -0,0 +1,26 @@ +package com.fr.design.mainframe.theme.ui; + +import com.fr.base.theme.TemplateTheme; +import com.fr.base.theme.TemplateThemeConfig; + +/** + * @author Starryi + * @version 1.0 + * Created by Starryi on 2021/9/18 + */ +public class AutoCheckThemeNameTextField extends AutoCheckTextField { + private TemplateThemeConfig config; + + public AutoCheckThemeNameTextField() { + setDuplicatedChecker(new DuplicateChecker() { + @Override + public boolean isDuplicated(String name) { + return config != null && config.contains(name); + } + }); + } + + public void setThemeConfig(TemplateThemeConfig config) { + this.config = config; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java b/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java index d8e5b4a57e..cfc4483c95 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java @@ -40,6 +40,7 @@ import com.fr.design.actions.server.GlobalParameterAction; import com.fr.design.actions.server.GlobalTableDataAction; import com.fr.design.actions.server.PlatformManagerAction; import com.fr.design.actions.server.PluginManagerAction; +import com.fr.design.base.mode.DesignModeContext; import com.fr.design.file.NewTemplatePane; import com.fr.design.fun.MenuHandler; import com.fr.design.fun.OemProcessor; @@ -407,7 +408,9 @@ public abstract class ToolBarMenuDock { menuDef.addShortCut(new OpenRecentReportMenuDef()); - addCloseCurrentTemplateAction(menuDef); + if (!DesignModeContext.isDuchampMode()) { + addCloseCurrentTemplateAction(menuDef); + } scs = plus.shortcut4FileMenu(); if (!ArrayUtils.isEmpty(scs)) { @@ -491,10 +494,12 @@ public abstract class ToolBarMenuDock { if (!DesignerMode.isAuthorityEditing()) { addPluginManagerAction(menuDef); - menuDef.addShortCut( - new FunctionManagerAction(), - new GlobalParameterAction() - ); + menuDef.addShortCut(new FunctionManagerAction()); + + if (!DesignModeContext.isDuchampMode()) { + menuDef.addShortCut(new GlobalParameterAction()); + } + } 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 8c7ed5ac5a..09fa2bbc08 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 @@ -17,15 +17,17 @@ import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter; import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent; import com.teamdev.jxbrowser.chromium.events.ScriptContextListener; import com.teamdev.jxbrowser.chromium.swing.BrowserView; +import com.teamdev.jxbrowser.event.Observer; import javax.swing.JDialog; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; -import java.util.Map; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; +import java.util.Map; /** * @author richie @@ -157,6 +159,38 @@ public class ModernUIPane extends BasicPane { return null; } + public void disposeBrowser() { + + if(browser != null) { + browser.dispose(); + browser = null; + } + + } + + public void clearCache() { + if (browser != null) { + browser.getCacheStorage().clearCache(); + File file = new File(browser.getContext().getCacheDir()); + if (file.exists()) { + file.delete(); + } + } + } + + public void executeJavaScript(String javaScript) { + if (browser != null) { + browser.executeJavaScript(javaScript); + } + } + + public JSValue executeJavaScriptAndReturnValue(String javaScript) { + if (browser != null) { + return browser.executeJavaScriptAndReturnValue(javaScript); + } + return null; + } + public static class Builder implements BuilderDiff { private ModernUIPane pane; @@ -293,6 +327,12 @@ public class ModernUIPane extends BasicPane { return this; } + @Override + public Builder prepareForV7(Class event, Observer listener) { + // do nothing + return this; + } + public ModernUIPane build() { return pane; } 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 33c817c948..9e6168514f 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 @@ -4,6 +4,7 @@ import com.fr.design.ui.ModernUIPane; 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; /** * 封装jxbrwoser v6/v7的构建方式的差异 @@ -20,5 +21,6 @@ public interface BuilderDiff { ModernUIPane.Builder prepareForV7(InjectJsCallback callback); + 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 5ae56627e9..68b8950f7c 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 @@ -11,6 +11,17 @@ import com.fr.stable.os.OperatingSystem; public class ModernUIPaneFactory { public static ModernUIPane.Builder modernUIPaneBuilder() { + + if (isV7()) { + return new NewModernUIPane.Builder<>(); + } else { + return new ModernUIPane.Builder<>(); + } + + } + + public static boolean isV7() { + // 7.15的class不存在时 走老版本 boolean hasJxBrowserV7_15 = true; try { @@ -18,10 +29,8 @@ public class ModernUIPaneFactory { } catch (ClassNotFoundException e) { hasJxBrowserV7_15 = false; } - if (OperatingSystem.isWindows() && hasJxBrowserV7_15) { - return new NewModernUIPane.Builder<>(); - } else { - return new ModernUIPane.Builder<>(); - } + + return OperatingSystem.isWindows() && hasJxBrowserV7_15; + } } 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 index 33747d3f15..d755d4e05f 100644 --- 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 @@ -15,6 +15,7 @@ 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.Scheme; import com.teamdev.jxbrowser.view.swing.BrowserView; @@ -80,30 +81,33 @@ public class NewModernUIPane extends ModernUIPane { private void showDebuggerDialog() { JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(this)); - Engine engine = Engine.newInstance( - EngineOptions.newBuilder(RenderingMode.HARDWARE_ACCELERATED) - .addSwitch("--disable-google-traffic") - .remoteDebuggingPort(9222).build()); - Browser debugger = engine.newBrowser(); + + 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 options; + EngineOptions.Builder builder; if (scheme != null && requestCallback != null) { - options = EngineOptions.newBuilder(RenderingMode.HARDWARE_ACCELERATED).addSwitch("--disable-google-traffic").addScheme(scheme, requestCallback).build(); - } else { - options = EngineOptions.newBuilder(RenderingMode.HARDWARE_ACCELERATED).addSwitch("--disable-google-traffic").build(); + 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(options); + + Engine engine = Engine.newInstance(builder.build()); browser = engine.newBrowser(); // 初始化的时候,就把命名空间对象初始化好,确保window.a.b.c("a.b.c"为命名空间)对象都是初始化过的 @@ -161,6 +165,29 @@ public class NewModernUIPane extends ModernUIPane { return null; } + public void disposeBrowser() { + + if (browser != null) { + browser.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<>(); @@ -191,7 +218,7 @@ public class NewModernUIPane extends ModernUIPane { @Override public NewModernUIPane.Builder withURL(final String url) { pane.scheme = Scheme.of("file"); - pane.requestCallback = new NxComplexInterceptRequestCallback(null); + pane.requestCallback = new NxComplexInterceptRequestCallback(null); pane.browser.navigation().loadUrl(url); return this; } @@ -301,6 +328,14 @@ public class NewModernUIPane extends ModernUIPane { 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/update/push/DesignerPushUpdateManager.java b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java index 6214f1cfa9..425f164543 100644 --- a/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java +++ b/designer-base/src/main/java/com/fr/design/update/push/DesignerPushUpdateManager.java @@ -60,7 +60,7 @@ public class DesignerPushUpdateManager { private String getFullLatestVersion() { try { - String url = CloudCenter.getInstance().acquireUrlByKind("jar10.update"); + String url = CloudCenter.getInstance().acquireUrlByKind("jar11.update"); if(StringUtils.isBlank(url)){ return StringUtils.EMPTY; } diff --git a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java index 3c91ebf145..9a849ad4e9 100644 --- a/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java +++ b/designer-base/src/main/java/com/fr/design/update/ui/dialog/UpdateMainDialog.java @@ -350,7 +350,7 @@ public class UpdateMainDialog extends UIDialog { new SwingWorker() { @Override protected JSONObject doInBackground() throws Exception { - return new JSONObject(HttpToolbox.get(CloudCenter.getInstance().acquireUrlByKind("jar10.update"))); + return new JSONObject(HttpToolbox.get(CloudCenter.getInstance().acquireUrlByKind("jar11.update"))); } @Override diff --git a/designer-base/src/main/java/com/fr/design/utils/gui/AdjustWorkBookDefaultStyleUtils.java b/designer-base/src/main/java/com/fr/design/utils/gui/AdjustWorkBookDefaultStyleUtils.java new file mode 100644 index 0000000000..abae997e39 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/gui/AdjustWorkBookDefaultStyleUtils.java @@ -0,0 +1,46 @@ +package com.fr.design.utils.gui; + +import com.fr.base.Style; +import com.fr.base.background.ColorBackground; +import com.fr.design.base.mode.DesignModeContext; +import com.fr.report.cell.CellElement; +import com.fr.report.cell.FloatElement; + +import java.awt.Color; + +/** + * @author shine + * @version 10.0 + * Created by shine on 2021/9/6 + */ +public class AdjustWorkBookDefaultStyleUtils { + + private static final Color TEMPLATE_BACKGROUND = new Color(16, 11, 43); + private static final Color CELL_ELEMENT_BORDER = new Color(110, 110, 110); + + public static void adjustCellElement(CellElement cellElement) { + if (DesignModeContext.isDuchampMode()) { + Style style = cellElement.getStyle(); + style = style.deriveFRFont(style.getFRFont().applyForeground(Color.WHITE)); + style = style.deriveBorder(0, CELL_ELEMENT_BORDER, + 0, CELL_ELEMENT_BORDER, + 0, CELL_ELEMENT_BORDER, + 0, CELL_ELEMENT_BORDER); + cellElement.setStyle(style); + } + } + + public static void adjustFloatElement(FloatElement floatElement) { + if (DesignModeContext.isDuchampMode()) { + Style style = floatElement.getStyle(); + style = style.deriveBackground(ColorBackground.getInstance(TEMPLATE_BACKGROUND)); + style = style.deriveFRFont(style.getFRFont().applyForeground(Color.WHITE)); + floatElement.setStyle(style); + } + } + + public static Color adjustBack(Color color) { + return DesignModeContext.isDuchampMode() ? TEMPLATE_BACKGROUND : color; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/utils/gui/GUICoreUtils.java b/designer-base/src/main/java/com/fr/design/utils/gui/GUICoreUtils.java index a89e367d64..ac75786314 100644 --- a/designer-base/src/main/java/com/fr/design/utils/gui/GUICoreUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/gui/GUICoreUtils.java @@ -38,7 +38,6 @@ import com.fr.stable.Constants; import com.fr.stable.OperatingSystem; import com.fr.stable.StringUtils; -import java.awt.Container; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.BorderFactory; @@ -67,10 +66,13 @@ import javax.swing.tree.TreePath; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.GridLayout; import java.awt.Image; import java.awt.Point; @@ -82,8 +84,6 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -176,7 +176,7 @@ public final class GUICoreUtils { if (style.getBackground() instanceof ColorBackground) { textField.setBackground(((ColorBackground) style.getBackground()).getColor()); } else { - textField.setBackground(Color.WHITE); + textField.setBackground(AdjustWorkBookDefaultStyleUtils.adjustBack(Color.WHITE)); } } diff --git a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java index 2dfbda4f53..c21c82c75a 100644 --- a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java +++ b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java @@ -29,6 +29,9 @@ import com.fr.design.layout.TableLayoutHelper; import com.fr.design.mainframe.DesignerFrame; import com.fr.design.mainframe.JTemplate; import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.Listener; import com.fr.file.filetree.FileNode; import com.fr.file.filter.ChooseFileFilter; import com.fr.file.filter.FILEFilter; @@ -45,6 +48,8 @@ import com.fr.stable.os.windows.WindowsDetector; import com.fr.stable.project.ProjectConstants; import com.fr.workspace.WorkContext; +import com.fr.workspace.Workspace; +import com.fr.workspace.WorkspaceEvent; import javax.swing.AbstractAction; import javax.swing.AbstractListModel; import javax.swing.ActionMap; @@ -66,7 +71,6 @@ import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; @@ -1334,6 +1338,12 @@ public class FILEChooserPane extends BasicPane { LocationButtonPane.this.repaint(10); } }); + EventDispatcher.listen(WorkspaceEvent.AfterSwitch, new Listener() { + @Override + public void on(Event event, Workspace param) { + setPopDir(null); + } + }); } public void highLightButton(FILE dir) { diff --git a/designer-base/src/main/resources/com/fr/design/images/transparent_background.png b/designer-base/src/main/resources/com/fr/design/images/transparent_background.png index cfb233f46e5e5e3c5349a7639004806f54c1eee2..4f942536f8fe729157ba36a8bd37add0bf1f7ca5 100644 GIT binary patch literal 17319 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