UI面板+部分资源 1-提供部分 Utils, 帮助调试 UI 2-抽象 notification 3-抽象 table 4-完成 envDetect -> notification 的联系feature/x
@ -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<T extends Exception> { |
||||||
|
|
||||||
|
void run() throws T; |
||||||
|
|
||||||
|
static <T extends Exception> Runnable toRunnable(ThrowableRunnable<T> runnable) { |
||||||
|
|
||||||
|
return () -> { |
||||||
|
try { |
||||||
|
runnable.run(); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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; |
||||||
|
|
||||||
|
/** |
||||||
|
* 右下角的提醒 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=388333688">异常提醒</a> |
||||||
|
* |
||||||
|
* 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<NotificationModel> notificationModels; |
||||||
|
private PageControlModel pageControlModel; |
||||||
|
|
||||||
|
private JPanel body; |
||||||
|
private JPanel headerPanel; |
||||||
|
private JPanel contentPanel; |
||||||
|
private JPanel tailPanel; |
||||||
|
|
||||||
|
public NotificationDialog(NotificationDialogProperties properties, List<NotificationModel> 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<? extends JComponent> 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<JPanel> 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
|
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<NotificationMessage> 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.fr.design.components.notification; |
||||||
|
|
||||||
|
/** |
||||||
|
* 提醒类型 |
||||||
|
* 决定图标种类 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/05/27 |
||||||
|
**/ |
||||||
|
public enum NotificationType { |
||||||
|
|
||||||
|
INFO, |
||||||
|
|
||||||
|
ERROR, |
||||||
|
|
||||||
|
WARNING |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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<PageControlModel, PageControlModel> 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.fr.design.constants; |
||||||
|
|
||||||
|
import java.awt.Color; |
||||||
|
|
||||||
|
/** |
||||||
|
* 见 <a href="https://www.figma.com/file/G2f40rv7cY8zpDDYbt6pY2/%E8%AE%BE%E8%AE%A1%E5%99%A811.0%E8%A7%84%E8%8C%83%E6%95%B4%E7%90%86">设计器规范</a> |
||||||
|
* 将相关的逻辑抽象过来 |
||||||
|
* 如果后面更改的话, 可以统一修改 |
||||||
|
* 如果换版本,可以换成 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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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"}); |
||||||
|
} |
||||||
|
} |
@ -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<Frame> 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<Frame>() { |
||||||
|
@Override |
||||||
|
public void accept(Frame frame) { |
||||||
|
|
||||||
|
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(frame); |
||||||
|
envDetectorDialog.setVisible(true); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -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><body style=\"" + style + "\">" + html + "</body></html>"; |
||||||
|
} |
||||||
|
|
||||||
|
public static String generateLinkTag(String link, String text) { |
||||||
|
|
||||||
|
return "<a href=\"" + link + "\">" + text + "</a>"; |
||||||
|
} |
||||||
|
|
||||||
|
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()); |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 171 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 971 B |
After Width: | Height: | Size: 696 B |
After Width: | Height: | Size: 780 B |
@ -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<Frame>() { |
||||||
|
@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 <a href='#'>model2</a> test", "abc")); |
||||||
|
|
||||||
|
NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); |
||||||
|
NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); |
||||||
|
dialog.setVisible(true); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} |