diff --git a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java index b8951ea8b..011f92140 100644 --- a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java +++ b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java @@ -18,6 +18,7 @@ import com.fr.design.file.HistoryTemplateListPane; import com.fr.design.fun.DesignerPortProvider; import com.fr.design.i18n.Toolkit; import com.fr.design.locale.impl.ProductImproveMark; +import com.fr.design.mainframe.reuse.ComponentReuseNotificationInfo; import com.fr.design.mainframe.vcs.VcsConfigManager; import com.fr.design.notification.SnapChatConfig; import com.fr.design.port.DesignerPortContext; @@ -196,6 +197,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { // 开启内嵌web页面的调试窗口 private boolean openDebug = false; + private ComponentReuseNotificationInfo notificationInfo = ComponentReuseNotificationInfo.getInstance(); + /** * DesignerEnvManager. */ @@ -1561,13 +1564,15 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { readRecentColor(reader); } else if ("OpenDebug".equals(name)) { readOpenDebug(reader); + } else if (name.equals(ComponentReuseNotificationInfo.XML_TAG)) { + readComponentReuseNotificationInfo(reader); } else if (name.equals(DesignerPushUpdateConfigManager.XML_TAG)) { readDesignerPushUpdateAttr(reader); } else if (name.equals(vcsConfigManager.XML_TAG)) { readVcsAttr(reader); } else if (DesignerPort.XML_TAG.equals(name)) { readDesignerPort(reader); - }else if (name.equals(SnapChatConfig.XML_TAG)) { + } else if (name.equals(SnapChatConfig.XML_TAG)) { readSnapChatConfig(reader); } else { readLayout(reader, name); @@ -1575,6 +1580,11 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { } } + private void readComponentReuseNotificationInfo(XMLableReader reader){ + reader.readXMLObject(this.notificationInfo); + + } + private void readSnapChatConfig(XMLableReader reader) { reader.readXMLObject(this.snapChatConfig = SnapChatConfig.getInstance()); } @@ -1795,9 +1805,16 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writeVcsAttr(writer); writeDesignerPort(writer); writeSnapChatConfig(writer); + writeComponentReuseNotificationInfo(writer); writer.end(); } + private void writeComponentReuseNotificationInfo(XMLPrintWriter writer) { + if (this.notificationInfo != null) { + this.notificationInfo.writeXML(writer); + } + } + private void writeSnapChatConfig(XMLPrintWriter writer) { if (this.snapChatConfig != null) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/ComponentReuseNotifyUtil.java b/designer-base/src/main/java/com/fr/design/mainframe/ComponentReuseNotifyUtil.java new file mode 100644 index 000000000..8541ffa66 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/ComponentReuseNotifyUtil.java @@ -0,0 +1,39 @@ +package com.fr.design.mainframe; + +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.reuse.ComponentReuseNotificationInfo; +import com.fr.design.mainframe.toast.DesignerToastMsgUtil; +import com.fr.design.notification.SnapChat; +import com.fr.design.notification.SnapChatFactory; +import com.fr.design.notification.SnapChatKey; + +/** + * Created by kerry on 5/8/21 + */ +public class ComponentReuseNotifyUtil { + private static final String COMPONENT_SNAP_CHAT_KEY = "com.fr.component.share-components"; + + private ComponentReuseNotifyUtil() { + + } + + public static void enterWidgetLib() { + EastRegionContainerPane.getInstance().switchTabTo(EastRegionContainerPane.KEY_WIDGET_LIB); + enterWidgetLibExtraAction(); + } + + public static void enterWidgetLibExtraAction() { + if (ComponentReuseNotificationInfo.getInstance().isClickedWidgetLib()) { + return; + } + SnapChat snapChat = SnapChatFactory.createSnapChat(false, new SnapChatKey() { + @Override + public String calc() { + return COMPONENT_SNAP_CHAT_KEY; + } + }); + if (snapChat.hasRead()) { + DesignerToastMsgUtil.toastPrompt(Toolkit.i18nText("Fine-Design_Component_Reuse_Merge_Prompt")); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignOperationEvent.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignOperationEvent.java new file mode 100644 index 000000000..5357cefbd --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignOperationEvent.java @@ -0,0 +1,15 @@ +package com.fr.design.mainframe; + +import com.fr.event.Event; +import com.fr.event.Null; + +/** + * Created by kerry on 4/28/21 + */ +public enum DesignOperationEvent implements Event { + + CELL_STYLE_MODIFY, + + CELL_IMAGE_VALUE_MODIFY + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java index 3bf9848de..346902a0d 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/EastRegionContainerPane.java @@ -299,7 +299,12 @@ public class EastRegionContainerPane extends UIEastResizableContainer { // 组件库 PropertyItem widgetLib = new PropertyItem(KEY_WIDGET_LIB, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Widget_Library"), "widgetlib", new PropertyMode[]{PropertyMode.FORM}, - new PropertyMode[]{PropertyMode.FORM}); + new PropertyMode[]{PropertyMode.FORM}, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ComponentReuseNotifyUtil.enterWidgetLibExtraAction(); + } + }); // 权限编辑 PropertyItem authorityEdition = new PropertyItem(KEY_AUTHORITY_EDITION, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Permissions_Edition"), "authorityedit", new PropertyMode[]{PropertyMode.AUTHORITY_EDITION_DISABLED}, @@ -595,6 +600,7 @@ public class EastRegionContainerPane extends UIEastResizableContainer { if (propertyItem.isVisible() && propertyItem.isEnabled() && !propertyItem.isPoppedOut()) { propertyCard.show(rightPane, tabName); propertyItem.setTabButtonSelected(); + propertyItem.processSnapChat(); //从单元格菜单过来也要关闭弹出窗 hideCurrentPopupPane(); } @@ -713,19 +719,30 @@ public class EastRegionContainerPane extends UIEastResizableContainer { private String iconSuffix = ICON_SUFFIX_NORMAL; // normal, diabled, selected, 三者之一 private final Color selectedBtnBackground = new Color(0xF5F5F7); private Color originBtnBackground; + private ActionListener actionListener; public PropertyItem(String name, String title, String btnIconName, PropertyMode[] visibleModes, PropertyMode[] enableModes) { this(name, title, btnIconName, ICON_BASE_DIR, visibleModes, enableModes, null, null); } + public PropertyItem(String name, String title, String btnIconName, PropertyMode[] visibleModes, PropertyMode[] enableModes, ActionListener actionListener) { + this(name, title, btnIconName, ICON_BASE_DIR, visibleModes, enableModes, null, null, actionListener); + } + + public PropertyItem(String name, String title, String btnIconName, String iconBaseDir, PropertyMode[] visibleModes, PropertyMode[] enableModes, SnapChat snapChat, PromptWindow promptWindow) { + this(name, title, btnIconName, iconBaseDir, visibleModes, enableModes, snapChat, promptWindow, null); + } + + public PropertyItem(String name, String title, String btnIconName, String iconBaseDir, PropertyMode[] visibleModes, PropertyMode[] enableModes, SnapChat snapChat, PromptWindow promptWindow, ActionListener actionListener) { this.name = name; this.title = title; this.btnIconName = btnIconName; this.iconBaseDir = iconBaseDir; this.snapChat = snapChat; this.promptWindow = promptWindow; + this.actionListener = actionListener; initButton(); initPropertyPanel(); initModes(visibleModes, enableModes); @@ -956,17 +973,25 @@ public class EastRegionContainerPane extends UIEastResizableContainer { popupFixedPane(); } setTabButtonSelected(); - if (snapChat != null && !snapChat.hasRead()) { - snapChat.markRead(); - if (promptWindow != null) { - promptWindow.showWindow(); - } - } + processSnapChat(); } }); + if (actionListener != null) { + button.addActionListener(actionListener); + } button.setToolTipText(title); } + public void processSnapChat(){ + if (snapChat != null && !snapChat.hasRead()) { + snapChat.markRead(); + if (promptWindow != null) { + promptWindow.showWindow(); + } + } + } + + public UIButton getButton() { return button; } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/reuse/ComponentReuseNotificationInfo.java b/designer-base/src/main/java/com/fr/design/mainframe/reuse/ComponentReuseNotificationInfo.java new file mode 100644 index 000000000..d819c1876 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/reuse/ComponentReuseNotificationInfo.java @@ -0,0 +1,69 @@ +package com.fr.design.mainframe.reuse; + +import com.fr.stable.xml.XMLPrintWriter; +import com.fr.stable.xml.XMLable; +import com.fr.stable.xml.XMLableReader; + +/** + * Created by kerry on 5/8/21 + */ +public class ComponentReuseNotificationInfo implements XMLable { + public static final String XML_TAG = "ComponentReuseNotificationInfo"; + + private static final ComponentReuseNotificationInfo INSTANCE = new ComponentReuseNotificationInfo(); + + public static ComponentReuseNotificationInfo getInstance() { + return INSTANCE; + } + + private long lastNotifyTime = 0; + + private int notifiedNumber = 0; + + private boolean clickedWidgetLib = false; + + public long getLastNotifyTime() { + return lastNotifyTime; + } + + public void setLastNotifyTime(long lastNotifyTime) { + this.lastNotifyTime = lastNotifyTime; + } + + public int getNotifiedNumber() { + return notifiedNumber; + } + + public void setNotifiedNumber(int notifiedNumber) { + this.notifiedNumber = notifiedNumber; + } + + public boolean isClickedWidgetLib() { + return clickedWidgetLib; + } + + public void setClickedWidgetLib(boolean clickedWidgetLib) { + this.clickedWidgetLib = clickedWidgetLib; + } + + @Override + public void readXML(XMLableReader reader) { + this.setLastNotifyTime(reader.getAttrAsLong("lastNotifyTime", 0L)); + this.setNotifiedNumber(reader.getAttrAsInt("notifiedNumber", 0)); + this.setClickedWidgetLib(reader.getAttrAsBoolean("clickedWidgetLib", false)); + } + + @Override + public void writeXML(XMLPrintWriter writer) { + writer.startTAG("ComponentReuseNotificationInfo"); + writer.attr("lastNotifyTime", this.lastNotifyTime) + .attr("notifiedNumber", this.notifiedNumber) + .attr("clickedWidgetLib", this.clickedWidgetLib); + writer.end(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/toast/DesignerToastMsgUtil.java b/designer-base/src/main/java/com/fr/design/mainframe/toast/DesignerToastMsgUtil.java new file mode 100644 index 000000000..e77cf8b86 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/toast/DesignerToastMsgUtil.java @@ -0,0 +1,106 @@ +package com.fr.design.mainframe.toast; + +import com.fr.base.BaseUtils; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerContext; +import com.fr.stable.Constants; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; + +/** + * Created by kerry on 5/6/21 + */ +public class DesignerToastMsgUtil { + private static final int MIN_WIDTH = 134; + private static final int MAX_WIDTH = 454; + private static final Icon PROMPT_ICON = BaseUtils.readIcon("/com/fr/design/images/toast/toast_prompt.png"); + private static final Icon WARNING_ICON = BaseUtils.readIcon("/com/fr/design/images/toast/toast_warning.png"); + + private DesignerToastMsgUtil() { + + } + + + public static void toastPrompt(JPanel contendPane) { + toastPane(PROMPT_ICON, contendPane); + } + + public static void toastWarning(JPanel contendPane) { + toastPane(WARNING_ICON, contendPane); + + } + + public static void toastPrompt(String promptInfo) { + toastPrompt(toastPane(promptInfo)); + } + + public static void toastWarning(String warningInfo) { + toastWarning(toastPane(warningInfo)); + } + + private static JPanel toastPane(String text) { + UILabel promptLabel = new UILabel("" + text + ""); + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + int width = promptLabel.getPreferredSize().width; + if (width > MAX_WIDTH) { + Dimension dimension = calculatePreferSize(text, promptLabel.getFont(), width); + jPanel.setPreferredSize(dimension); + } + jPanel.add(promptLabel, BorderLayout.NORTH); + return jPanel; + } + + private static void toastPane(Icon icon, JPanel contendPane) { + JPanel pane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + UILabel uiLabel = new UILabel(icon); + uiLabel.setVerticalAlignment(SwingConstants.TOP); + uiLabel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0)); + pane.add(uiLabel, BorderLayout.WEST); + pane.add(contendPane, BorderLayout.CENTER); + pane.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15)); + contendPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + ToastMsgDialog dialog = new ToastMsgDialog(DesignerContext.getDesignerFrame(), pane); + dialog.setVisible(true); + } + + + private static Dimension calculatePreferSize(String text, Font font, int width) { + int limitWidth = Math.max(MIN_WIDTH, width); + limitWidth = Math.min(MAX_WIDTH, limitWidth); + return new Dimension(limitWidth, getHtmlHeight(text, limitWidth, font)); + } + + + private static int getHtmlHeight(String content, int width, Font font) { + StringBuffer limitDiv = new StringBuffer("
"); + return getHtmlContentDimension(content, limitDiv).height; + } + + + private static Dimension getHtmlContentDimension(String content, StringBuffer limitDiv) { + content = limitDiv.append(content).append("
").toString(); + JEditorPane editorPane = new JEditorPane(); + editorPane.setContentType("text/html"); + editorPane.setText(content); + return editorPane.getPreferredSize(); + } + + private static String getFontWrapStyle(Font font) { + double dpi96 = Constants.FR_PAINT_RESOLUTION; + double dpi72 = Constants.DEFAULT_FONT_PAINT_RESOLUTION; + return new StringBuilder() + .append(";font-size:").append(font.getSize() * dpi96 / dpi72) + .append("pt;font-family:").append(font.getFontName()) + .toString(); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/toast/ToastMsgDialog.java b/designer-base/src/main/java/com/fr/design/mainframe/toast/ToastMsgDialog.java new file mode 100644 index 000000000..e2cdaa35d --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/toast/ToastMsgDialog.java @@ -0,0 +1,154 @@ +package com.fr.design.mainframe.toast; + +import com.fr.concurrent.NamedThreadFactory; +import com.fr.design.dialog.UIDialog; +import com.fr.design.mainframe.DesignerContext; +import com.fr.module.ModuleContext; + +import javax.swing.JPanel; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Created by kerry on 4/29/21 + */ +public class ToastMsgDialog extends UIDialog { + private static final int MIN_HEIGHT = 36; + private static final String TOAST_MSG_TIMER = "TOAST_MSG_TIMER"; + private ScheduledExecutorService TIMER; + private int hide_height = 0; + private JPanel contentPane; + + public ToastMsgDialog(Frame parent, JPanel panel) { + super(parent); + setFocusable(false); + setAutoRequestFocus(false); + setUndecorated(true); + contentPane = panel; + initComponent(parent); + } + + private void initComponent(Frame parent) { + this.getContentPane().setLayout(null); + this.getContentPane().add(contentPane); + Dimension dimension = calculatePreferSize(); + hide_height = dimension.height; + setSize(new Dimension(dimension.width, 0)); + contentPane.setSize(dimension); + setLocationRelativeTo(DesignerContext.getDesignerFrame().getContentFrame()); + int positionY = DesignerContext.getDesignerFrame().getContentFrame().getLocationOnScreen().y + 10; + setLocation((parent.getWidth() - dimension.width) / 2, positionY); + addMouseEvent(contentPane); + } + + + private Dimension calculatePreferSize() { + Dimension contentDimension = contentPane.getPreferredSize(); + int height = Math.max(MIN_HEIGHT, contentDimension.height); + return new Dimension(contentDimension.width, height); + } + + + public void display(JPanel outerJPanel) { + outerJPanel.setLocation(0, -hide_height); + ScheduledExecutorService TIP_TOOL_TIMER = createToastScheduleExecutorService(); + TIP_TOOL_TIMER.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + Point point = outerJPanel.getLocation(); + if (point.y >= 0) { + TIP_TOOL_TIMER.shutdown(); + disappear(outerJPanel); + } + int showDistance = 5 + point.y < 0 ? 5 : -point.y; + outerJPanel.setLocation(point.x, point.y + showDistance); + Dimension dimension = ToastMsgDialog.this.getSize(); + ToastMsgDialog.this.setSize(new Dimension(dimension.width, dimension.height + showDistance)); + System.out.println(ToastMsgDialog.this.getHeight()); + } + }, 0, 50, TimeUnit.MILLISECONDS); + + } + + private void disappear(JPanel outerJPanel) { + TIMER = createToastScheduleExecutorService(); + TIMER.schedule(new Runnable() { + @Override + public void run() { + ScheduledExecutorService TIP_TOOL_TIMER = createToastScheduleExecutorService(); + TIP_TOOL_TIMER.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + Point point = outerJPanel.getLocation(); + if (point.y <= -hide_height) { + TIP_TOOL_TIMER.shutdown(); + ToastMsgDialog.this.setVisible(false); + ToastMsgDialog.this.dispose(); + } + outerJPanel.setLocation(point.x, point.y - 5); + Dimension dimension = ToastMsgDialog.this.getSize(); + ToastMsgDialog.this.setSize(new Dimension(dimension.width, dimension.height - 5)); + } + }, 0,50, TimeUnit.MILLISECONDS); + + } + }, 5000, TimeUnit.MILLISECONDS); + } + + private ScheduledExecutorService createToastScheduleExecutorService() { + return ModuleContext.getExecutor().newSingleThreadScheduledExecutor(new NamedThreadFactory(TOAST_MSG_TIMER)); + } + + private void addMouseEvent(JPanel jPanel) { + jPanel.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + TIMER.shutdownNow(); + } + + @Override + public void mouseExited(MouseEvent e) { + disappear(jPanel); + } + }); + } + + + @Override + public void checkValid() throws Exception { + } + + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + display(contentPane); + } + } + + @Override + public void dispose() { + super.dispose(); + } + + +} diff --git a/designer-base/src/main/resources/com/fr/design/images/form/designer/widget_apply_icon.png b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget_apply_icon.png new file mode 100644 index 000000000..3817f4c47 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/form/designer/widget_apply_icon.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/toast/reuse_icon.png b/designer-base/src/main/resources/com/fr/design/images/toast/reuse_icon.png new file mode 100644 index 000000000..90f59aa1d Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/toast/reuse_icon.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/toast/toast_prompt.png b/designer-base/src/main/resources/com/fr/design/images/toast/toast_prompt.png new file mode 100644 index 000000000..5662a88a4 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/toast/toast_prompt.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/toast/toast_warning.png b/designer-base/src/main/resources/com/fr/design/images/toast/toast_warning.png new file mode 100644 index 000000000..6499c48e0 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/toast/toast_warning.png differ diff --git a/designer-base/src/test/java/com/fr/design/mainframe/reuse/ComponentReuseNotificationInfoTest.java b/designer-base/src/test/java/com/fr/design/mainframe/reuse/ComponentReuseNotificationInfoTest.java new file mode 100644 index 000000000..c90e7a8b0 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/mainframe/reuse/ComponentReuseNotificationInfoTest.java @@ -0,0 +1,46 @@ +package com.fr.design.mainframe.reuse; + +import com.fr.stable.xml.XMLPrintWriter; +import com.fr.stable.xml.XMLableReader; +import com.fr.third.javax.xml.stream.XMLStreamException; +import org.junit.Assert; +import org.junit.Test; + +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; + +/** + * Created by kerry on 5/10/21 + */ +public class ComponentReuseNotificationInfoTest { + + @Test + public void testReadXML() { + try { + XMLableReader xmlReader = XMLableReader.createXMLableReader(new StringReader("\n")); + ComponentReuseNotificationInfo notificationInfo = ComponentReuseNotificationInfo.getInstance(); + notificationInfo.readXML(xmlReader); + xmlReader.close(); + Assert.assertEquals(2, notificationInfo.getNotifiedNumber()); + Assert.assertEquals(true, notificationInfo.isClickedWidgetLib()); + Assert.assertEquals(1620612153215L, notificationInfo.getLastNotifyTime()); + } catch (XMLStreamException e) { + Assert.fail(e.getMessage()); + } + } + + @Test + public void testWriteXML() { + StringWriter sw = new StringWriter(); + XMLPrintWriter writer = XMLPrintWriter.create(new PrintWriter(sw)); + ComponentReuseNotificationInfo notificationInfo = ComponentReuseNotificationInfo.getInstance(); + notificationInfo.setNotifiedNumber(1); + notificationInfo.writeXML(writer); + writer.flush(); + writer.close(); + Assert.assertEquals("\n" + + "\n", sw.toString()); + } + +} diff --git a/designer-form/src/main/java/com/fr/design/designer/treeview/ComponentTreeCellRenderer.java b/designer-form/src/main/java/com/fr/design/designer/treeview/ComponentTreeCellRenderer.java index 48938dcee..e4bfcfaf1 100644 --- a/designer-form/src/main/java/com/fr/design/designer/treeview/ComponentTreeCellRenderer.java +++ b/designer-form/src/main/java/com/fr/design/designer/treeview/ComponentTreeCellRenderer.java @@ -1,17 +1,24 @@ package com.fr.design.designer.treeview; +import com.fr.base.BaseUtils; +import com.fr.base.iofile.attr.ExtendSharableAttrMark; import com.fr.design.constants.UIConstants; import com.fr.design.designer.creator.XCreator; import com.fr.design.designer.creator.XCreatorUtils; +import com.fr.form.ui.AbstractBorderStyleWidget; import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; import java.awt.Component; +import java.awt.Graphics; public class ComponentTreeCellRenderer extends DefaultTreeCellRenderer { + private boolean needAddShareIcon = false; + private static final Icon SHARE_ICON = BaseUtils.readIcon("/com/fr/design/images/toast/icon_reuse.png"); public ComponentTreeCellRenderer() { } @@ -31,13 +38,31 @@ public class ComponentTreeCellRenderer extends DefaultTreeCellRenderer { } if (icon != null) { setIcon(icon); + this.needAddShareIcon = isShareWidget((XCreator) value); } + } this.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0)); this.setBackgroundNonSelectionColor(UIConstants.TREE_BACKGROUND); return this; } + private boolean isShareWidget(XCreator xCreator) { + if (!xCreator.toData().acceptType(AbstractBorderStyleWidget.class)) { + return false; + } + ExtendSharableAttrMark attrMark = ((AbstractBorderStyleWidget) xCreator.toData()).getWidgetAttrMark(ExtendSharableAttrMark.XML_TAG); + return attrMark != null && StringUtils.isNotEmpty(attrMark.getShareId()); + + } + + public void paint(Graphics g) { + super.paint(g); + if (needAddShareIcon) { + SHARE_ICON.paintIcon(this, g, 10, 0); + } + } + @Override public Icon getClosedIcon() { return getIcon(); diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java b/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java index ddd78c13f..b5722e9da 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormParaWidgetPane.java @@ -15,6 +15,7 @@ import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.imenu.UIPopupMenu; +import com.fr.design.i18n.Toolkit; import com.fr.design.module.DesignModuleFactory; import com.fr.design.utils.gui.LayoutUtils; import com.fr.form.ui.UserDefinedWidgetConfig; @@ -35,6 +36,8 @@ import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.SwingConstants; +import java.awt.Cursor; +import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -233,8 +236,48 @@ public class FormParaWidgetPane extends JPanel { } add(createWidgetCombinationPane(widgetPane, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_ToolBar_Widget"))); add(createJSeparator()); + + add(createComponentReuseToolPane()); + add(createJSeparator()); } + private JPanel createComponentReuseToolPane() { + JPanel jPanel = new JPanel(new BorderLayout(17, 10)); + UILabel uiLabel = new UILabel(BaseUtils.readIcon("/com/fr/design/images/form/designer/widget_apply_icon.png")); + jPanel.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + ComponentReuseNotifyUtil.enterWidgetLib(); + } + + @Override + public void mousePressed(MouseEvent e) { + jPanel.setBackground(Color.decode("#419BF9")); + } + + @Override + public void mouseReleased(MouseEvent e) { + jPanel.setBackground(null); + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + jPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + jPanel.add(uiLabel, BorderLayout.NORTH); + jPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Component_Reuse_Apply_Widget")), BorderLayout.CENTER); + jPanel.setToolTipText(Toolkit.i18nText("Fine-Design_Component_Reuse_Apply_Widget")); + return jPanel; + } + + private void loadPredefinedWidget() { predifinedwidgeList.clear(); if (designer != null) { diff --git a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java index a851074be..54db2ec1a 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java @@ -319,6 +319,13 @@ public class JForm extends JTemplate implements BaseJForm jt) { + ReuseTriggerPointManager.getInstance().registerJForm(JForm.this); + } }); return tabCenterPane; diff --git a/designer-form/src/main/java/com/fr/design/mainframe/ReuseTriggerPointManager.java b/designer-form/src/main/java/com/fr/design/mainframe/ReuseTriggerPointManager.java new file mode 100644 index 000000000..c4bdc8bf7 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/mainframe/ReuseTriggerPointManager.java @@ -0,0 +1,214 @@ + + +package com.fr.design.mainframe; + +import com.fr.base.iofile.attr.ExtendSharableAttrMark; +import com.fr.design.DesignerEnvManager; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.adaptve.config.TriggerPointProvider; + +import com.fr.design.mainframe.adaptve.config.impl.CellStyleTriggerPoint; +import com.fr.design.mainframe.adaptve.config.impl.CellValueImageChangeTriggerPoint; +import com.fr.design.mainframe.adaptve.config.ReuseNotifyInfo; +import com.fr.design.mainframe.reuse.ComponentReuseNotificationInfo; +import com.fr.design.mainframe.toast.DesignerToastMsgUtil; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.event.Listener; +import com.fr.event.Null; +import com.fr.form.main.Form; +import com.fr.form.main.WidgetGather; +import com.fr.form.ui.AbstractBorderStyleWidget; +import com.fr.form.ui.Widget; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Created by kerry on 4/28/21 + */ +public class ReuseTriggerPointManager { + private static final long ONE_WEEK_TIME = 7 * 24 * 3600 * 1000L; + + private static class Holder { + private static final ReuseTriggerPointManager HOLDER = new ReuseTriggerPointManager(); + } + + public static ReuseTriggerPointManager getInstance() { + return Holder.HOLDER; + } + + private Map map = new HashMap<>(); + + private List listeners = new ArrayList<>(); + + private ReuseTriggerPointManager() { + if (!hasNotifiedTwice()) { + List list = getTriggerPoints(); + for (TriggerPointProvider triggerPoint : list) { + Listener listener = new Listener() { + @Override + public void on(Event event, Null o) { + triggerPoint.triggerAction(); + } + }; + EventDispatcher.listen(triggerPoint.triggerEvent(), listener); + listeners.add(listener); + } + + } + } + + private List getTriggerPoints() { + List list = new ArrayList<>(); + list.add(new CellStyleTriggerPoint()); + list.add(new CellValueImageChangeTriggerPoint()); + return list; + } + + + private boolean hasNotifiedTwice() { + return ComponentReuseNotificationInfo.getInstance().getNotifiedNumber() >= 2; + } + + + private void reCount() { + //重新计次数 + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + iterator.next().getValue().reset(); + } + } + + private void writeTriggerInfo2xml() { + int number = ComponentReuseNotificationInfo.getInstance().getNotifiedNumber() + 1; + ComponentReuseNotificationInfo.getInstance().setNotifiedNumber(number); + ComponentReuseNotificationInfo.getInstance().setLastNotifyTime(System.currentTimeMillis()); + DesignerEnvManager.getEnvManager().saveXMLFile(); + //如果已经提示过两次了 + if (hasNotifiedTwice()) { + for (Listener listener : listeners) { + EventDispatcher.stopListen(listener); + } + this.map.clear(); + } + } + + public boolean needTrigger() { + boolean result = true; + if (ComponentReuseNotificationInfo.getInstance().getLastNotifyTime() > 0L) { + result = System.currentTimeMillis() - ComponentReuseNotificationInfo.getInstance().getLastNotifyTime() > ONE_WEEK_TIME; + } + return !hasNotifiedTwice() && result; + } + + + public void registerJForm(JForm jForm) { + if (!hasNotifiedTwice()) { + this.map.put(jForm, new ReuseNotifyInfo()); + } + + } + + public void removeJForm(JForm jForm) { + if (!hasNotifiedTwice()) { + this.map.remove(jForm); + } + } + + + public ReuseNotifyInfo getReuseNotifyInfo() { + JTemplate currentJTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (!(currentJTemplate instanceof JForm && hasUseReuseComponent((JForm) currentJTemplate)) + && !ReuseTriggerPointManager.getInstance().needTrigger()) { + return null; + } + return map.get(currentJTemplate); + } + + public void reuseNotify(ReuseNotifyInfo notifyInfo) { + if (notifyInfo.matchCondition()) { + ReuseTriggerPointManager.getInstance().reCount(); + //弹出提示框 + JTemplate currentJTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + DesignerToastMsgUtil.toastPrompt(createReusePrompt((JForm) currentJTemplate)); + ReuseTriggerPointManager.getInstance().writeTriggerInfo2xml(); + } + } + + + private boolean hasUseReuseComponent(JForm jForm) { + Form form = jForm.getTarget(); + List extendSharableWidgetList = new ArrayList<>(); + Form.traversalWidget(form.getContainer(), new WidgetGather() { + @Override + public void dealWith(Widget widget) { + ExtendSharableAttrMark attrMark = ((AbstractBorderStyleWidget) widget).getWidgetAttrMark(ExtendSharableAttrMark.XML_TAG); + if (attrMark != null && StringUtils.isNotEmpty(attrMark.getShareId())) { + extendSharableWidgetList.add(widget); + } + } + + @Override + public boolean dealWithAllCards() { + return true; + } + }, AbstractBorderStyleWidget.class); + return extendSharableWidgetList.size() > 0; + } + + + private JPanel createReusePrompt(JForm jForm) { + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + jPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Component_Reuse_Try_Prompt")), BorderLayout.WEST); + UILabel reuseLabel = new UILabel(Toolkit.i18nText("Fine-Design_Component_Reuse")); + reuseLabel.addMouseListener(new MouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + jForm.tabChanged(0); + ComponentReuseNotifyUtil.enterWidgetLib(); + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + reuseLabel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); + reuseLabel.setForeground(Color.BLUE); + reuseLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + jPanel.add(reuseLabel, BorderLayout.CENTER); + return jPanel; + } + + +} diff --git a/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/ReuseNotifyInfo.java b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/ReuseNotifyInfo.java new file mode 100644 index 000000000..01dc7bbb8 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/ReuseNotifyInfo.java @@ -0,0 +1,27 @@ +package com.fr.design.mainframe.adaptve.config; + +/** + * Created by kerry on 5/7/21 + */ +public class ReuseNotifyInfo { + private int cellStyleModifiedNumber = 0; + private int cellImageValueNumber = 0; + + public void addCellStyleModify() { + cellStyleModifiedNumber++; + } + + + public void addCellImageValueModify() { + cellImageValueNumber++; + } + + public boolean matchCondition() { + return cellStyleModifiedNumber >= 3 || cellImageValueNumber >= 1; + } + + public void reset() { + this.cellImageValueNumber = 0; + this.cellStyleModifiedNumber = 0; + } +} diff --git a/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/TriggerPointProvider.java b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/TriggerPointProvider.java new file mode 100644 index 000000000..5fa4db80f --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/TriggerPointProvider.java @@ -0,0 +1,20 @@ +package com.fr.design.mainframe.adaptve.config; + + +import com.fr.event.Event; + +/** + * Created by kerry on 4/29/21 + */ +public interface TriggerPointProvider { + /** + * 触发后的操作 + */ + void triggerAction(); + + /** + * 触发事件 + * @return + */ + Event triggerEvent(); +} diff --git a/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/impl/CellStyleTriggerPoint.java b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/impl/CellStyleTriggerPoint.java new file mode 100644 index 000000000..47055eec1 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/impl/CellStyleTriggerPoint.java @@ -0,0 +1,28 @@ +package com.fr.design.mainframe.adaptve.config.impl; + +import com.fr.design.mainframe.DesignOperationEvent; +import com.fr.design.mainframe.ReuseTriggerPointManager; +import com.fr.design.mainframe.adaptve.config.ReuseNotifyInfo; +import com.fr.design.mainframe.adaptve.config.TriggerPointProvider; +import com.fr.event.Event; + +/** + * Created by kerry on 5/7/21 + */ +public class CellStyleTriggerPoint implements TriggerPointProvider { + @Override + public void triggerAction() { + ReuseNotifyInfo notifyInfo = ReuseTriggerPointManager.getInstance().getReuseNotifyInfo(); + if (notifyInfo == null) { + return; + } + notifyInfo.addCellStyleModify(); + ReuseTriggerPointManager.getInstance().reuseNotify(notifyInfo); + + } + + @Override + public Event triggerEvent() { + return DesignOperationEvent.CELL_STYLE_MODIFY; + } +} diff --git a/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/impl/CellValueImageChangeTriggerPoint.java b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/impl/CellValueImageChangeTriggerPoint.java new file mode 100644 index 000000000..1429ebeab --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/mainframe/adaptve/config/impl/CellValueImageChangeTriggerPoint.java @@ -0,0 +1,27 @@ +package com.fr.design.mainframe.adaptve.config.impl; + +import com.fr.design.mainframe.DesignOperationEvent; +import com.fr.design.mainframe.ReuseTriggerPointManager; +import com.fr.design.mainframe.adaptve.config.ReuseNotifyInfo; +import com.fr.design.mainframe.adaptve.config.TriggerPointProvider; +import com.fr.event.Event; + +/** + * Created by kerry on 5/7/21 + */ +public class CellValueImageChangeTriggerPoint implements TriggerPointProvider { + @Override + public void triggerAction() { + ReuseNotifyInfo notifyInfo = ReuseTriggerPointManager.getInstance().getReuseNotifyInfo(); + if (notifyInfo == null) { + return; + } + notifyInfo.addCellImageValueModify(); + ReuseTriggerPointManager.getInstance().reuseNotify(notifyInfo); + } + + @Override + public Event triggerEvent() { + return DesignOperationEvent.CELL_IMAGE_VALUE_MODIFY; + } +} diff --git a/designer-form/src/test/java/com/fr/design/mainframe/ReuseTriggerPointManagerTest.java b/designer-form/src/test/java/com/fr/design/mainframe/ReuseTriggerPointManagerTest.java new file mode 100644 index 000000000..8a80e7f7d --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/mainframe/ReuseTriggerPointManagerTest.java @@ -0,0 +1,45 @@ +package com.fr.design.mainframe; + +import com.fr.design.mainframe.reuse.ComponentReuseNotificationInfo; +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 5/10/21 + */ +public class ReuseTriggerPointManagerTest { + private static final long ONE_WEEK_TIME = 7 * 24 * 3600 * 1000L; + + + @Test + public void testNeedTrigger() { + ComponentReuseNotificationInfo notificationInfo = ComponentReuseNotificationInfo.getInstance(); + notificationInfo.setNotifiedNumber(0); + Assert.assertTrue(ReuseTriggerPointManager.getInstance().needTrigger()); + + notificationInfo.setNotifiedNumber(1); + Assert.assertTrue(ReuseTriggerPointManager.getInstance().needTrigger()); + + notificationInfo.setNotifiedNumber(2); + Assert.assertFalse(ReuseTriggerPointManager.getInstance().needTrigger()); + + notificationInfo.setNotifiedNumber(0); + notificationInfo.setLastNotifyTime(System.currentTimeMillis()); + Assert.assertFalse(ReuseTriggerPointManager.getInstance().needTrigger()); + + notificationInfo.setNotifiedNumber(1); + notificationInfo.setLastNotifyTime(System.currentTimeMillis()); + Assert.assertFalse(ReuseTriggerPointManager.getInstance().needTrigger()); + + + notificationInfo.setNotifiedNumber(1); + notificationInfo.setLastNotifyTime(System.currentTimeMillis() - ONE_WEEK_TIME - 1); + Assert.assertTrue(ReuseTriggerPointManager.getInstance().needTrigger()); + + + notificationInfo.setNotifiedNumber(2); + notificationInfo.setLastNotifyTime(System.currentTimeMillis()); + Assert.assertFalse(ReuseTriggerPointManager.getInstance().needTrigger()); + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/actions/utils/ReportActionUtils.java b/designer-realize/src/main/java/com/fr/design/actions/utils/ReportActionUtils.java index 7f4a51394..2c0b73fe9 100644 --- a/designer-realize/src/main/java/com/fr/design/actions/utils/ReportActionUtils.java +++ b/designer-realize/src/main/java/com/fr/design/actions/utils/ReportActionUtils.java @@ -2,7 +2,9 @@ package com.fr.design.actions.utils; import com.fr.base.Style; import com.fr.design.actions.cell.style.StyleActionInterface; +import com.fr.design.mainframe.DesignOperationEvent; import com.fr.design.mainframe.ElementCasePane; +import com.fr.event.EventDispatcher; import com.fr.grid.selection.CellSelection; import com.fr.grid.selection.FloatSelection; import com.fr.grid.selection.Selection; @@ -50,9 +52,7 @@ public class ReportActionUtils { @Override public void dealWith(CellElement editCellElement) { Style style2Mod = editCellElement.getStyle(); - editCellElement.setStyle( - styleActionInterface.executeStyle(style2Mod, selectedStyle)); - + updateCellStyle(editCellElement, styleActionInterface.executeStyle(style2Mod, selectedStyle)); } }); } @@ -119,4 +119,9 @@ public class ReportActionUtils { //peter:直接返回当前编辑元素的Style return editCellElement.getStyle(); } -} \ No newline at end of file + + public static void updateCellStyle(CellElement cellElement, Style style){ + cellElement.setStyle(style); + EventDispatcher.fire(DesignOperationEvent.CELL_STYLE_MODIFY); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellStylePane.java b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellStylePane.java index 4bfd9fa46..049b0111a 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellStylePane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellStylePane.java @@ -1,6 +1,7 @@ package com.fr.design.mainframe.cell.settingpane; import com.fr.base.Style; +import com.fr.design.actions.utils.ReportActionUtils; import com.fr.design.constants.UIConstants; import com.fr.design.mainframe.cell.settingpane.style.StylePane; import com.fr.design.style.BorderUtils; @@ -63,7 +64,7 @@ public class CellStylePane extends AbstractCellAttrPane { @Override public void updateBean(TemplateCellElement cellElement) { - cellElement.setStyle(stylePane.updateBean()); + ReportActionUtils.updateCellStyle(cellElement, stylePane.updateBean()); } @Override diff --git a/designer-realize/src/main/java/com/fr/grid/Grid.java b/designer-realize/src/main/java/com/fr/grid/Grid.java index dc414f673..abbd60928 100644 --- a/designer-realize/src/main/java/com/fr/grid/Grid.java +++ b/designer-realize/src/main/java/com/fr/grid/Grid.java @@ -15,9 +15,11 @@ import com.fr.design.constants.UIConstants; import com.fr.design.file.HistoryTemplateListPane; import com.fr.design.fun.GridUIProcessor; import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.mainframe.DesignOperationEvent; import com.fr.design.mainframe.ElementCasePane; import com.fr.design.mainframe.JTemplate; import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.event.EventDispatcher; import com.fr.general.ComparatorUtils; import com.fr.grid.event.CellEditorEvent; import com.fr.grid.event.CellEditorListener; @@ -1083,6 +1085,7 @@ public class Grid extends BaseGridComponent { imageChange = true; } if (styleChange || imageChange) { + EventDispatcher.fire(DesignOperationEvent.CELL_IMAGE_VALUE_MODIFY); return true; } } else {