diff --git a/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java b/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java new file mode 100644 index 000000000..c89ad2115 --- /dev/null +++ b/designer-base/src/main/java/com/fr/base/function/ThrowableRunnable.java @@ -0,0 +1,24 @@ +package com.fr.base.function; + +import com.fr.log.FineLoggerFactory; + +/** + * 可抛出异常的 Runnable + * + * created by Harrison on 2022/05/24 + **/ +public interface ThrowableRunnable { + + void run() throws T; + + static Runnable toRunnable(ThrowableRunnable runnable) { + + return () -> { + try { + runnable.run(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java new file mode 100644 index 000000000..d2f56976e --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationAction.java @@ -0,0 +1,21 @@ +package com.fr.design.components.notification; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface NotificationAction { + + /** + * 行为名 + * + * @return 名称 + */ + String name(); + + /** + * 行为动作 + * + * @param args 参数 + */ + void run(Object... args); +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java new file mode 100644 index 000000000..f7182ddca --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java @@ -0,0 +1,336 @@ +package com.fr.design.components.notification; + +import com.fr.base.function.ThrowableRunnable; +import com.fr.base.svg.IconUtils; +import com.fr.design.components.page.PageControlModel; +import com.fr.design.components.page.PageControlPanel; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * 右下角的提醒 异常提醒 + * + * created by Harrison on 2022/05/24 + **/ +public class NotificationDialog extends JDialog { + + private Dimension contentSize = new Dimension(300, 100); + private Dimension buttonDimension = new Dimension(68, 20); + + private NotificationDialogProperties properties; + + /* 数据 model */ + + private List notificationModels; + private PageControlModel pageControlModel; + + private JPanel body; + private JPanel headerPanel; + private JPanel contentPanel; + private JPanel tailPanel; + + public NotificationDialog(NotificationDialogProperties properties, List notificationModels) { + + super(properties.getOwner()); + setTitle(properties.getTitle()); + this.properties = properties; + + this.notificationModels = notificationModels; + this.pageControlModel = new PageControlModel(0, this.notificationModels.size()); + + initComponents(); + } + + public void initComponents() { + + //UI 配置 + configProperties(); + + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); + body.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + //首行 + layoutHeaderPanel(); + + //消息内容 + layoutContentPanel(); + + //查看详情 + layoutTailPanel(); + + add(body); + + Dimension dimension = body.getPreferredSize(); + setSize(dimension.width, dimension.height); + + Container parent = getParent(); + setLocation((parent.getWidth() - dimension.width - 30 + parent.getX()), + parent.getY() + parent.getHeight() - dimension.height - 30); + + } + + public void open() { + + setVisible(true); + } + + private void configProperties() { + + setModal(properties.isModal()); + setFocusable(false); + setAutoRequestFocus(false); + setResizable(false); + } + + protected JPanel createHeaderPanel() { + + return null; + } + + /** + * 内容 + * + * @return 内容面板 + */ + protected JPanel createContentPanel() { + + JPanel contentPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + contentPanel.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10)); + contentPanel.setName("contentPanel"); + + NotificationModel model = getCurrentModel(); + + UILabel icon = new UILabel(getIconForType(model.getType())); + icon.setPreferredSize(new Dimension(16, 16)); + JPanel iconPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + iconPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 10, 8)); + iconPanel.add(icon, BorderLayout.NORTH); + + contentPanel.add(iconPanel, BorderLayout.WEST); + + + NotificationMessage[] messages = model.getMessages(); + List messageComponents = Arrays.stream(messages) + .map((messageModel) -> { + if (messageModel.getType() == NotificationMessage.Type.LINK) { + NotificationMessage.LinkMessage linkMessage = (NotificationMessage.LinkMessage) messageModel; + return new MessageWithLink(linkMessage.format(), ThrowableRunnable.toRunnable(() -> { + Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); + })); + } + return new UILabel(messageModel.format()); + }) + .collect(Collectors.toList()); + + // 竖向排列 + JPanel messageSummaryPanel = new JPanel(); + messageSummaryPanel.setLayout(new GridLayout(messageComponents.size(), 1)); + messageComponents.forEach(messageSummaryPanel::add); + + JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20)); + + JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + jScrollPane.setBorder(BorderFactory.createEmptyBorder()); + centerPanel.add(jScrollPane, BorderLayout.CENTER); + centerPanel.setPreferredSize(contentSize); + + contentPanel.add(centerPanel, BorderLayout.CENTER); + + return contentPanel; + } + + /** + * 行动 + * + * UI布局 + * /翻页/不再提醒/提醒行为/我知道了 + * + * @return 行动面板 + */ + protected JPanel createTailPanel() { + + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + tailPanel.setName("tailPanel"); + + // 翻页按钮效果 + PageControlPanel pageControlPanel = new PageControlPanel(pageControlModel); + + pageControlPanel.actions(new Runnable() { + @Override + public void run() { + pageControlModel = pageControlPanel.performPrevious(); + refresh(); + } + }, new Runnable() { + @Override + public void run() { + pageControlModel = pageControlPanel.performNext(); + refresh(); + } + }); + + tailPanel.add(pageControlPanel, BorderLayout.WEST); + + // 行为效果 + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); + + { + actionsPanel.setBorder(BorderFactory.createEmptyBorder()); + actionsPanel.setName("actionsPanel"); + + UILabel notReminder = new UILabel(); + notReminder.setText(Toolkit.i18nText("Fine-Design_Basic_Not_Reminder")); + notReminder.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // todo + // 点击事件 + destroy(); + } + }); + Color color = new Color(65, 155, 249); + notReminder.setForeground(color); + actionsPanel.add(notReminder, BorderLayout.WEST); + + JPanel buttonPanel = FRGUIPaneFactory.createBorderLayout_M_Pane(); + buttonPanel.setBorder(BorderFactory.createEmptyBorder()); + + // real-action + NotificationModel currentModel = getCurrentModel(); + NotificationAction action = currentModel.getAction(); + if (action != null) { + UIButton actionButton = new UIButton(action.name()); + actionButton.setPreferredSize(buttonDimension); + actionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + action.run(); + } + }); + buttonPanel.add(actionButton, BorderLayout.WEST); + } + + UIButton knowButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Know")); + knowButton.setPreferredSize(buttonDimension); + knowButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (pageControlModel.isLast()) { + destroy(); + return; + } + pageControlModel = pageControlPanel.performNext(); + refresh(); + } + }); + buttonPanel.add(knowButton, BorderLayout.EAST); + + actionsPanel.add(buttonPanel, BorderLayout.EAST); + } + + tailPanel.add(actionsPanel, BorderLayout.EAST); + + return tailPanel; + } + + private void refresh() { + + layoutContentPanel(); + + layoutTailPanel(); + + this.repaint(); + } + + private void layoutHeaderPanel() { + + this.headerPanel = layoutPanel(this.headerPanel, this::createHeaderPanel, BorderLayout.NORTH); + } + + private void layoutTailPanel() { + + this.tailPanel = layoutPanel(this.tailPanel, this::createTailPanel, BorderLayout.SOUTH); + } + + private void layoutContentPanel() { + + this.contentPanel = layoutPanel(this.contentPanel, this::createContentPanel, BorderLayout.CENTER); + } + + private JPanel layoutPanel(JPanel oldPanel, Supplier supplier, Object constraints){ + + if (oldPanel != null) { + this.body.remove(oldPanel); + } + JPanel newPanel = supplier.get(); + if (newPanel != null) { + this.body.add(newPanel, constraints); + } + return newPanel; + } + + private NotificationModel getCurrentModel() { + + int index = pageControlModel.getIndex(); + return notificationModels.get(index); + } + + protected Icon getIconForType(NotificationType type) { + + String iconPath; + switch (type) { + case ERROR: + iconPath = "/com/fr/design/standard/reminder/reminder_error.svg"; + break; + case INFO: + iconPath = "/com/fr/design/standard/reminder/reminder_success.svg"; + break; + case WARNING: + iconPath = "/com/fr/design/standard/reminder/reminder_warning.svg"; + break; + default: + return null; + } + return IconUtils.readIcon(iconPath); + } + + private void destroy() { + + setVisible(false); + dispose(); + } + + @Override + public void dispose() { + + super.dispose(); + // todo + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java new file mode 100644 index 000000000..5bcff6715 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationDialogProperties.java @@ -0,0 +1,39 @@ +package com.fr.design.components.notification; + +import java.awt.Frame; + +/** + * 通知会话的属性 + * + * created by Harrison on 2022/05/24 + **/ +public class NotificationDialogProperties { + + private Frame owner; + + private String title; + + private boolean modal; + + public NotificationDialogProperties(Frame owner, String title) { + this.owner = owner; + this.title = title; + this.modal = false; + } + + public void setModal(boolean modal) { + this.modal = modal; + } + + public Frame getOwner() { + return owner; + } + + public String getTitle() { + return title; + } + + public boolean isModal() { + return modal; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java new file mode 100644 index 000000000..26dba1297 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationMessage.java @@ -0,0 +1,88 @@ +package com.fr.design.components.notification; + +import com.fr.design.utils.LinkStrUtil; + +/** + * created by Harrison on 2022/05/24 + **/ +public interface NotificationMessage { + + /** + * 格式化 + * + * @return 通知信息 + */ + String format(); + + /** + * 类型 + * + * @return 类型 + */ + Type getType(); + + enum Type { + + /** + * 简单型 + */ + SIMPLE, + + /** + * 链接 + */ + LINK + } + + class SimpleMessage implements NotificationMessage { + + private String text; + + public SimpleMessage(String text) { + this.text = text; + } + + @Override + public String format() { + return text; + } + + @Override + public Type getType() { + return Type.SIMPLE; + } + } + + class LinkMessage implements NotificationMessage { + + private String text; + + private String link; + + public LinkMessage(String text, String link) { + + this.text = text; + this.link = link; + } + + public String getText() { + return text; + } + + public String getLink() { + return link; + } + + @Override + public String format() { + + return LinkStrUtil.generateHtmlTag(text); + } + + @Override + public Type getType() { + + return Type.LINK; + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java new file mode 100644 index 000000000..86d5a9edf --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationModel.java @@ -0,0 +1,39 @@ +package com.fr.design.components.notification; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class NotificationModel { + + private final NotificationType type; + private final NotificationMessage[] messages; + + @Nullable + private final NotificationAction action; + + public NotificationModel(NotificationType type, @Nullable NotificationAction action, List messages) { + this(type, action, messages.toArray(new NotificationMessage[0])); + } + + public NotificationModel(NotificationType type, @Nullable NotificationAction action, NotificationMessage... messages) { + + this.type = type; + this.messages = messages; + this.action = action; + } + + public NotificationType getType() { + return type; + } + + public NotificationMessage[] getMessages() { + return messages == null ? new NotificationMessage[0] : messages; + } + + public @Nullable NotificationAction getAction() { + + return action; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java b/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java new file mode 100644 index 000000000..0f5ab7b34 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/notification/NotificationType.java @@ -0,0 +1,16 @@ +package com.fr.design.components.notification; + +/** + * 提醒类型 + * 决定图标种类 + * + * created by Harrison on 2022/05/27 + **/ +public enum NotificationType { + + INFO, + + ERROR, + + WARNING +} diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java new file mode 100644 index 000000000..3d024fb05 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlModel.java @@ -0,0 +1,78 @@ +package com.fr.design.components.page; + +/** + * created by Harrison on 2022/05/26 + **/ +public class PageControlModel { + + /** + * 当前索引 + * + * = (页数-1) + */ + private int index; + + /** + * 总页数 + */ + private int summary; + + public PageControlModel(int index, int summary) { + this.index = index; + this.summary = summary; + } + + public PageControlModel() { + } + + public PageControlModel previous() { + + this.index--; + return this; + } + + public PageControlModel next() { + + this.index++; + return this; + } + + /** + * 页数 + * index+1 + * + * @return 页数 + */ + public int getNumber() { + return index + 1; + } + + public boolean isFirst() { + return getNumber() == 1; + } + + public boolean isLast() { + return getNumber() == getSummary(); + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getSummary() { + return summary; + } + + public void setSummary(int summary) { + this.summary = summary; + } + + public String toContent() { + + return getNumber() + "/" + this.summary; + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java new file mode 100644 index 000000000..0059f9125 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/page/PageControlPanel.java @@ -0,0 +1,150 @@ +package com.fr.design.components.page; + +import com.fr.base.svg.IconUtils; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.function.Function; + +/** + * 翻页组件 + * + * created by Harrison on 2022/05/26 + **/ +public class PageControlPanel extends JPanel { + + private static final long serialVersionUID = 8140501834691131305L; + + private static final Dimension PAGE_CONTROL_BUTTON_DIMENSION = new Dimension(20, 20); + + private UIButton previous; + private Runnable previousAction; + + private UILabel content; + private PageControlModel model; + + private UIButton next; + private Runnable nextAction; + + public PageControlPanel(PageControlModel model) { + + this.model = model; + setBorder(BorderFactory.createEmptyBorder()); + setLayout(new BorderLayout(6, 0)); + + this.previous = new UIButton(); + previous.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); + previous.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_left.svg")); + previous.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_left.svg")); + previous.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + performAction(previousAction); + } + }); + + this.add(previous, BorderLayout.WEST); + + this.content = new UILabel(model.toContent()); + this.add(content, BorderLayout.CENTER); + + next = new UIButton(); + next.setPreferredSize(PAGE_CONTROL_BUTTON_DIMENSION); + next.setIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_enable_right.svg")); + next.setDisabledIcon(IconUtils.readIcon("/com/fr/design/standard/arrow/arrow_disable_right.svg")); + next.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + performAction(nextAction); + } + }); + this.add(next, BorderLayout.EAST); + + refresh(); + } + + public PageControlModel performPrevious() { + + update(PageControlModel::previous); + return this.model; + } + + public PageControlModel performNext() { + + update(PageControlModel::next); + return this.model; + } + + public PageControlModel getModel() { + + return this.model; + } + + public void update(PageControlModel model) { + + this.model.setIndex(model.getIndex()); + this.model.setSummary(model.getSummary()); + refresh(); + } + + public void update(Function updateAction) { + + PageControlModel newModel = updateAction.apply(this.model); + update(newModel); + refresh(); + } + + public void refresh() { + + this.content.setText(this.model.toContent()); + this.content.repaint(); + + this.previous.setEnabled(true); + this.next.setEnabled(true); + if (model.getNumber() == 1) { + // 禁用上一个 + disableButton(this.previous); + // 禁用next + if (model.getNumber() == model.getSummary()) { + disableButton(this.next); + } + return; + } + + // 禁用next + if (model.getNumber() == model.getSummary()) { + disableButton(this.next); + } + } + + private void enable(UIButton button) { + + button.setEnabled(true); + } + + private void disableButton(UIButton button) { + + button.setEnabled(false); + } + + private void performAction(Runnable action) { + + if (action != null) { + action.run(); + refresh(); + } + } + + public void actions(Runnable previousAction, Runnable nextAction) { + + this.previousAction = previousAction; + this.nextAction = nextAction; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java new file mode 100644 index 000000000..a1a9492f8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/components/table/TablePanel.java @@ -0,0 +1,180 @@ +package com.fr.design.components.table; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.third.org.apache.commons.lang3.ArrayUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridLayout; + +/** + * 表头 + * 内容 + * + * 适用于需要一个表格的 Panel + * + * created by Harrison on 2022/05/26 + **/ +public class TablePanel extends JPanel { + + private static final Color DEFAULT_HEADER_COLOR = new Color(232, 232, 233); + + private static final Color DEFAULT_ODD_ROW_COLOR = new Color(245, 245, 247); + + private static final Color DEFAULT_EVEN_ROW_COLOR = Color.WHITE; + + private JPanel headerPanel; + + private JPanel[] headerItemPanels; + + private JPanel contentPanel; + + private JPanel[][] cellPanels; + + public TablePanel(int row, int column) { + + setLayout(FRGUIPaneFactory.createBorderLayout()); + + /* header 部分 */ + + this.headerPanel = new JPanel(); + headerPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); + headerPanel.setName("header-panel"); + headerPanel.setPreferredSize(new Dimension(640, 24)); + + // border + headerPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); + syncHeaderColor(headerPanel); + + headerItemPanels = new JPanel[column]; + for (int i = 0; i < column; i++) { + JPanel headerItemWrapper = FRGUIPaneFactory.createBorderLayout_S_Pane(); + syncHeaderColor(headerItemWrapper); + headerItemWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + + JPanel headerItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + headerItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + headerItemPanels[i] = headerItemPanel; + + UILabel label = new UILabel(); + syncHeaderColor(label); + + headerItemPanel.add(new UILabel(), BorderLayout.CENTER); + + headerItemWrapper.add(headerItemPanel, BorderLayout.WEST); + if (i != column - 1) { + JSeparator separator = new JSeparator(JSeparator.VERTICAL); + separator.setBackground(new Color(218, 218, 221)); + headerItemWrapper.add(separator, BorderLayout.EAST); + } + headerPanel.add(headerItemWrapper); + } + + /* content 部分 */ + + contentPanel = new JPanel(); + + contentPanel.setLayout(new GridLayout(row, 1)); + contentPanel.setBorder(BorderFactory.createLineBorder(new Color(218, 218, 221))); + + cellPanels = new JPanel[row][column]; + for (int i = 0; i < row; i++) { + + JPanel rowPanel = new JPanel(); + // 获取行号 + Color rowColor = getRowColorByRowNumber(i + 1); + rowPanel.setBackground(rowColor); + rowPanel.setLayout(FRGUIPaneFactory.createNColumnGridLayout(column)); + rowPanel.setName("row-" + i); + rowPanel.setBorder(BorderFactory.createEmptyBorder()); + rowPanel.setPreferredSize(new Dimension(640, 24)); + + for (int j = 0; j < column; j++) { + + JPanel rowItemPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + rowItemPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + rowItemPanel.setName("rowItemPanel-"+ i + "-" + j); + final UILabel empty = new UILabel(); + empty.setPreferredSize(new Dimension(210, 24)); + rowItemPanel.setBackground(rowPanel.getBackground()); + rowItemPanel.add(empty, BorderLayout.CENTER); + + rowPanel.add(rowItemPanel); + cellPanels[i][j] = rowItemPanel; + } + + contentPanel.add(rowPanel); + } + + add(headerPanel, BorderLayout.NORTH); + add(contentPanel, BorderLayout.SOUTH); + } + + /** + * 获取行的颜色 + * + * @param row 行号 + * @return 颜色 + */ + private Color getRowColorByRowNumber(int row) { + + Color rowColor; + if (row % 2 != 0) { + rowColor = DEFAULT_EVEN_ROW_COLOR; + } else { + rowColor = DEFAULT_ODD_ROW_COLOR; + } + return rowColor; + } + + public void updateHeaders(String[] headers) { + + for (int i = 0; i < headers.length; i++) { + String header = headers[i]; + UILabel headerContent = new UILabel(header); + JPanel headerItemPanel = headerItemPanels[i]; + if (ArrayUtils.getLength(headerItemPanel.getComponents()) == 1) { + headerItemPanel.remove(0); + } + headerItemPanel.add(headerContent); + syncHeaderColor(headerItemPanel); + } + } + + public void updateCell(int row, int column, Component component) { + + int x = row - 1; + int y = column - 1; + + JPanel cellPanel = this.cellPanels[x][y]; + if (ArrayUtils.getLength(cellPanel.getComponents()) == 1) { + cellPanel.remove(0); + } + cellPanel.add(component); + } + + public void updateCell(int row, int column, String value) { + + UILabel cellContent = new UILabel(value); + syncCellColor(row, cellContent); + this.updateCell(row, column, cellContent); + } + + private void syncHeaderColor(Component component) { + + component.setBackground(DEFAULT_HEADER_COLOR); + } + + private void syncCellColor(int row, Component component) { + + Color rowColor = getRowColorByRowNumber(row); + component.setBackground(rowColor); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java b/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java new file mode 100644 index 000000000..0ece84cca --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/constants/DesignerColor.java @@ -0,0 +1,24 @@ +package com.fr.design.constants; + +import java.awt.Color; + +/** + * 见 设计器规范 + * 将相关的逻辑抽象过来 + * 如果后面更改的话, 可以统一修改 + * 如果换版本,可以换成 v2 这种类推 + * + * created by Harrison on 2022/05/26 + **/ +public interface DesignerColor { + + interface Button { + + interface Primary { + + Color PRESSED = new Color(29, 122, 220); + Color HOVER = new Color(84, 165, 249); + Color NORMAL = new Color(65, 155, 249); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java index 2e839d27c..a6746d227 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java +++ b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java @@ -1,6 +1,6 @@ package com.fr.design.dialog.link; -import com.fr.design.gui.ilable.UILabel; +import com.fr.design.utils.LinkStrUtil; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; @@ -12,6 +12,8 @@ import java.awt.Desktop; import java.awt.Font; import java.net.URI; +import static com.fr.design.utils.LinkStrUtil.LABEL; + /** * 用来构建JOptionPane带超链的消息提示 * @@ -21,8 +23,13 @@ import java.net.URI; */ public class MessageWithLink extends JEditorPane { - private static final UILabel LABEL = new UILabel(); - + public MessageWithLink(String htmlText, Runnable action) { + + super("text/html", htmlText); + initListener(action); + setEditable(false); + setBorder(null); + } public MessageWithLink(String message, String linkName, String link) { this(message, linkName, link, LABEL.getBackground(), LABEL.getFont()); @@ -49,36 +56,34 @@ public class MessageWithLink extends JEditorPane { } public MessageWithLink(String frontMessage, String linkName, String link, String backMessage, Color backgroundColor, Font font, Color fontColor) { - super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); + + super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); initListener(link); setEditable(false); setBorder(null); } - - protected void initListener(String link) { + + public void initListener(Runnable runnable) { + addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { - try { - Desktop.getDesktop().browse(URI.create(link)); - } catch (Exception exception) { - FineLoggerFactory.getLogger().error(exception.getMessage(), exception); - } + runnable.run(); } } }); } - private static StringBuilder generateStyle(Color backgroundColor, Font font, Color fontColor) { - // 构建相同风格样式 - StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); - style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); - style.append("font-size:").append(font.getSize()).append("pt;"); - style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); - style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); - - return style; + protected void initListener(String link) { + + initListener(() -> { + try { + Desktop.getDesktop().browse(URI.create(link)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }); } } diff --git a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java index b7583d0dd..86093a6d8 100644 --- a/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java +++ b/designer-base/src/main/java/com/fr/design/ui/util/UIUtil.java @@ -1,9 +1,12 @@ package com.fr.design.ui.util; import com.fr.log.FineLoggerFactory; +import com.fr.third.guava.base.Stopwatch; +import com.fr.third.guava.base.Supplier; import org.jetbrains.annotations.NotNull; import javax.swing.SwingUtilities; +import java.util.concurrent.TimeUnit; /** * 一些常用的 GUI 工具。 @@ -52,4 +55,30 @@ public class UIUtil { } } } + + /** + * 有些时候,交互上需要有一些等待的效果, + * 如果没有等待到, 会让交互失去作用。 + * + * @param supplier 结果 + * @param timeout 超时 + * @param timeUnit 单位 + * @return 结果 + */ + public static T waitUntil(Supplier supplier, long timeout, TimeUnit timeUnit) { + + Stopwatch st = Stopwatch.createStarted(); + T result = supplier.get(); + long elapsed = st.elapsed(timeUnit); + long minus = timeout - elapsed; + if (minus > 0) { + long value = timeUnit.toMillis(minus); + try { + Thread.sleep(value); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + return result; + } } diff --git a/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java b/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java new file mode 100644 index 000000000..2125455d3 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java @@ -0,0 +1,12 @@ +package com.fr.design.utils; + +/** + * created by Harrison on 2022/05/26 + **/ +public class DevDebugUtil { + + public static void main(String[] args) { + + org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtil"}); + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/DevUtil.java b/designer-base/src/main/java/com/fr/design/utils/DevUtil.java new file mode 100644 index 000000000..caf6fc966 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/DevUtil.java @@ -0,0 +1,49 @@ +package com.fr.design.utils; + +import com.fr.design.ui.util.UIUtil; +import com.fr.env.detect.ui.EnvDetectorDialog; + +import javax.swing.JFrame; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.util.function.Consumer; + +/** + * 设计器的开发时工具 + * 帮助进行 UI 页面的调试 + * + * created by Harrison on 2022/05/23 + **/ +public class DevUtil { + + public static void show(Consumer consumer) { + + DesignUtils.initLookAndFeel(); + + UIUtil.invokeLaterIfNeeded(() -> { + + JFrame frame = new JFrame("dev-util"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(dimension); + + consumer.accept(frame); + + frame.setVisible(true); + }); + + } + + public static void main(String[] args) { + + DevUtil.show(new Consumer() { + @Override + public void accept(Frame frame) { + + EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(frame); + envDetectorDialog.setVisible(true); + } + }); + } +} diff --git a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java new file mode 100644 index 000000000..08cd2614f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtil.java @@ -0,0 +1,55 @@ +package com.fr.design.utils; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.stable.StringUtils; + +import java.awt.Color; +import java.awt.Font; + +/** + * created by Harrison on 2022/05/24 + **/ +public class LinkStrUtil { + + public static final UILabel LABEL = new UILabel(); + + public static String generateHtmlTag(String html) { + + String defaultStyle = generateDefaultStyle(); + return generateHtmlTag(defaultStyle, html); + } + + public static String generateHtmlTag(String style, String html) { + + if (StringUtils.isEmpty(style)) { + throw new NullPointerException("style"); + } + if (StringUtils.isEmpty(html)) { + throw new NullPointerException("html"); + } + return "" + html + ""; + } + + public static String generateLinkTag(String link, String text) { + + return "" + text + ""; + } + + public static String generateStyle(Color backgroundColor, Font font, Color fontColor) { + + // 构建相同风格样式 + StringBuilder style = new StringBuilder("font-family:" + font.getFamily() + ";"); + + style.append("font-weight:").append(font.isBold() ? "bold" : "normal").append(";"); + style.append("font-size:").append(font.getSize()).append("pt;"); + style.append("color:rgb(").append(fontColor.getRed()).append(",").append(fontColor.getGreen()).append(",").append(fontColor.getBlue()).append(");"); + style.append("background-color: rgb(").append(backgroundColor.getRed()).append(",").append(backgroundColor.getGreen()).append(",").append(backgroundColor.getBlue()).append(");"); + + return style.toString(); + } + + public static String generateDefaultStyle() { + + return generateStyle(LABEL.getBackground(), LABEL.getFont(), LABEL.getForeground()); + } +} diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg new file mode 100755 index 000000000..0a20cf97a --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg new file mode 100755 index 000000000..9d111eff9 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_disable_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg new file mode 100755 index 000000000..4ab4c796e --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg new file mode 100755 index 000000000..040db18bd --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/arrow/arrow_enable_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif new file mode 100644 index 000000000..99f95a0fa Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-120.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif new file mode 100644 index 000000000..9db4c738f Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/standard/loading/loading-64.gif differ diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg new file mode 100755 index 000000000..6ef70206f --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg new file mode 100755 index 000000000..c26d221b5 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_success.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg new file mode 100755 index 000000000..5b0eabe82 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/standard/reminder/reminder_warning.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java new file mode 100644 index 000000000..fec7e8069 --- /dev/null +++ b/designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java @@ -0,0 +1,43 @@ +package com.fr.design.components.notification; + +import com.fr.design.utils.DevUtil; +import com.fr.third.guava.collect.Lists; + +import java.awt.Frame; +import java.util.function.Consumer; + +public class NotificationDialogTest { + + public static void main(String[] args) { + + testShow(); + } + + public static void testShow() { + + DevUtil.show(new Consumer() { + @Override + public void accept(Frame frame) { + + NotificationModel model1 = new NotificationModel(NotificationType.WARNING, null, new NotificationMessage.LinkMessage("test", "abc"), new NotificationMessage.LinkMessage("abc", "aaa")); + + NotificationModel model2 = new NotificationModel(NotificationType.INFO, new NotificationAction() { + @Override + public String name() { + return "action"; + } + + @Override + public void run(Object... args) { + System.out.println("1111"); + } + }, new NotificationMessage.LinkMessage("display model2 test", "abc")); + + NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); + NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); + dialog.setVisible(true); + } + }); + } + +} \ No newline at end of file