* commit '75c997eb4d01e7269156386ff83e7a3a1b1fcdff': (41 commits) REPORT-77217 ConnectionProvider-修改之后数据连接信息无法保存 【问题原因】产生假保存的原因是,在保存的时候会判断现有的Connection与以前存储的Connection是否有不同,如果不同就代表需要更新。实际上插件中的Connection相关类没有写equals方法,然后新旧Connection被认为是相同了,就没有走后面的更新配置的逻辑 【改动方案】拉rinoux、vito、hugh一起沟通了下方案,暂时主代码里做个兼容,让主代码内置的两种Connection去判断是否要更新,其余的Connection(插件Connection)都必须更新 【review建议】 REPORT-75991 插件完整性校验,弹窗链接帮助文档说明 【问题原因】安全类需求,将原来仅对官方插件开启的 插件完整性校验,扩大到了 第三方插件。但弹窗不明确,对于用户侧没有闭环,需要添加帮助文档链接,帮助用户清楚前因后果与解决方案。 【改动方案】1.提示文案内容修改;2.提示中存在超链接,将插件这边因为"插件完整性校验"而失败的弹窗单独处理了下,由原本的JOptionPane修改为JEditorPane 【review建议】其它的弹窗暂时没有变动的需求,跟产品沟通过了,如果以后有必要统一的时候再来统一处理 REPORT-76635 数据集参数默认值为空的情况下,组合图条件属性内容错乱 REPORT-76370 提供一个数据连接前置检查接口&写错了应该是远程调用 REPORT-76370 提供一个数据连接前置检查接口&设计器数据连接保存 fix REPORT-74340 设计器菜单栏-登录,密码输错超过100次,没有错误提示 REPORT-76259 【迭代】【数据连接面板为空】概率出现编辑数据集一直加载中 根据评审意见修改下,不特地分出两个方法了 REPORT-76259 【迭代】【数据连接面板为空】概率出现编辑数据集一直加载中 【问题原因】远程设计下,数据集面板的初始化中有两个耗时操作(下拉框取数据连接、TableViewList根据下拉框值取连接对应的所有表),对应两个SwingWorker,设为A和B;数据集面板本身的展示过程也对应一个SwingWorker(需要查询此连接是否有权限),设为C;当C执行完后,会在done方法里调用数据集面板的setVisible方法以让它展示,如果此时A和B才执行完doInbackground,准备执行done的时候(done的执行是invokeLater的),C的面板先显示了,会导致它们的done方法会被已经显示的模态dialog给阻塞住,从而无法完成面板数据更新 【改动思路】将setVisible方法用invokeLater包一层,让A和B的done方法不被阻塞 【review建议】无 REPORT-75752 兼容下旧插件 REPORT-75752 表格辅助线的功能从frm扩展到dashboard REPORT-76403 【迭代】更新日志-更新内容为空,报错Unparseable date: "null" 1、兼容下更新日志的缓存 2、昨天提交到feature的,没合的,提交到release REPORT-75575 design feat:fvs支持服务器全局参数 REPORT-76403【迭代】更新日志-更新内容为空,报错Unparseable date: "null" 1、处理下异常 REPORT-76403【迭代】更新日志-更新内容为空,报错Unparseable date: "null" 1、规范下代码 REPORT-76403【迭代】更新日志-更新内容为空,报错Unparseable date: "null" 1、兼容下本地缓存更新日志的逻辑 REPORT-72828 启动页配置项屏蔽 REPORT-76291 【迭代】【数据连接为空】次管查看无权限的数据集,没有提示 【问题原因】获取数据连接名称的方式错了,误写成了获取数据集名称,因此判断成了有权限 【改动思路】改用正确方式获取数据连接的名称 【review建议】无 REPORT-75919 【迭代】更新日志-交互问题 1、修改交互 REPORT-76174 【迭代】【数据连接面板为空】数据表搜索框-交互问题 【问题原因】1. 开发的时候没做这个TextField鼠标悬浮变色的功能;2. 部分代码质量问题 【改动思路】1. 加上悬浮变色边框的逻辑;2. 代码质量问题修改 【review建议】无 ...fix-lag
@ -0,0 +1,24 @@ |
|||||||
|
package com.fr.base.function; |
||||||
|
|
||||||
|
import com.fr.design.utils.DesignUtils; |
||||||
|
import com.fr.runtime.FineRuntime; |
||||||
|
|
||||||
|
/** |
||||||
|
* UI 终止动作 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/07/14 |
||||||
|
**/ |
||||||
|
public abstract class UITerminator { |
||||||
|
|
||||||
|
public void run() { |
||||||
|
|
||||||
|
// 先执行必须的逻辑
|
||||||
|
FineRuntime.start(); |
||||||
|
DesignUtils.initLookAndFeel(); |
||||||
|
|
||||||
|
// 在执行核心逻辑
|
||||||
|
doRun(); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract void doRun(); |
||||||
|
} |
@ -0,0 +1,148 @@ |
|||||||
|
package com.fr.design.components.loading; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.Timer; |
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.BasicStroke; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Composite; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.LayoutManager; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 10.0 |
||||||
|
* Created by hades on 2021/4/12 |
||||||
|
*/ |
||||||
|
public class LoadingPane extends JComponent implements ActionListener { |
||||||
|
|
||||||
|
private AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f); |
||||||
|
private volatile boolean mIsRunning; |
||||||
|
private volatile boolean mIsFadingOut; |
||||||
|
private Timer mTimer; |
||||||
|
private int mAngle; |
||||||
|
private int mFadeCount; |
||||||
|
private int mFadeLimit = 15; |
||||||
|
private int lines = 12; |
||||||
|
private int maxAngle = 360; |
||||||
|
private int angleAdd = 30; |
||||||
|
|
||||||
|
public LoadingPane() { |
||||||
|
|
||||||
|
addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
setLayout(getCoverLayout()); |
||||||
|
setBackground(null); |
||||||
|
setOpaque(false); |
||||||
|
} |
||||||
|
|
||||||
|
protected LayoutManager getCoverLayout() { |
||||||
|
return new LayoutManager() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeLayoutComponent(Component comp) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension preferredLayoutSize(Container parent) { |
||||||
|
return parent.getPreferredSize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension minimumLayoutSize(Container parent) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void layoutContainer(Container parent) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addLayoutComponent(String name, Component comp) { |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
int w = this.getWidth(); |
||||||
|
int h = this.getHeight(); |
||||||
|
super.paint(g); |
||||||
|
if (!mIsRunning) { |
||||||
|
return; |
||||||
|
} |
||||||
|
Graphics2D g2 = (Graphics2D) g.create(); |
||||||
|
float fade = (float) mFadeCount / (float) mFadeLimit; |
||||||
|
Composite urComposite = g2.getComposite(); |
||||||
|
g2.setComposite(composite); |
||||||
|
g2.fillRect(0, 0, w, h); |
||||||
|
g2.setComposite(urComposite); |
||||||
|
int s = Math.min(w, h) / 50; |
||||||
|
int cx = w / 2; |
||||||
|
int cy = h / 2; |
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); |
||||||
|
g2.setPaint(Color.BLACK); |
||||||
|
g2.rotate(Math.PI * mAngle / 180, cx, cy); |
||||||
|
for (int i = 0; i < lines; i++) { |
||||||
|
float scale = (11.0f - (float) i) / 11.0f; |
||||||
|
g2.drawLine(cx + s, cy, cx + s * 2, cy); |
||||||
|
g2.rotate(-Math.PI / 6, cx, cy); |
||||||
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, scale * fade)); |
||||||
|
} |
||||||
|
g2.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
if (mIsRunning) { |
||||||
|
repaint(); |
||||||
|
mAngle += angleAdd; |
||||||
|
if (mAngle >= maxAngle) { |
||||||
|
mAngle = 0; |
||||||
|
} |
||||||
|
if (mIsFadingOut) { |
||||||
|
if (--mFadeCount == 0) { |
||||||
|
mIsRunning = false; |
||||||
|
mTimer.stop(); |
||||||
|
} |
||||||
|
} else if (mFadeCount < mFadeLimit) { |
||||||
|
mFadeCount++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void start() { |
||||||
|
if (mIsRunning) { |
||||||
|
return; |
||||||
|
} |
||||||
|
mIsRunning = true; |
||||||
|
mIsFadingOut = false; |
||||||
|
mFadeCount = 0; |
||||||
|
int fps = 24; |
||||||
|
int tick = 1000 / fps; |
||||||
|
mTimer = new Timer(tick, this); |
||||||
|
mTimer.start(); |
||||||
|
} |
||||||
|
|
||||||
|
public void stop() { |
||||||
|
mIsRunning = false; |
||||||
|
mIsFadingOut = true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
package com.fr.design.components.tooltip; |
||||||
|
|
||||||
|
import com.fr.base.GraphHelper; |
||||||
|
import com.fr.design.gui.itooltip.UIToolTip; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JToolTip; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import javax.swing.plaf.ToolTipUI; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FontMetrics; |
||||||
|
import java.awt.GradientPaint; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.geom.GeneralPath; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.StringReader; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.util.Vector; |
||||||
|
|
||||||
|
/** |
||||||
|
* 现代化的 UIToolTip |
||||||
|
* 见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=416850313">设计文档</a> |
||||||
|
* |
||||||
|
* created by Harrison on 2022/07/09 |
||||||
|
**/ |
||||||
|
public class ModernToolTip extends UIToolTip { |
||||||
|
|
||||||
|
public ModernToolTip() { |
||||||
|
super(); |
||||||
|
setUI(new ModernToolTipUI()); |
||||||
|
} |
||||||
|
|
||||||
|
private class ModernToolTipUI extends ToolTipUI { |
||||||
|
|
||||||
|
private String[] strs; |
||||||
|
private Icon icon; |
||||||
|
private boolean needPaint; |
||||||
|
public void paint(Graphics g, JComponent c) { |
||||||
|
if (!needPaint) { |
||||||
|
return; |
||||||
|
} |
||||||
|
FontMetrics metrics = GraphHelper.getFontMetrics(c.getFont()); |
||||||
|
Dimension size = c.getSize(); |
||||||
|
int width = size.width; |
||||||
|
int height = size.height; |
||||||
|
Graphics2D g2 = (Graphics2D) g; |
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
g2.setColor(new Color(51, 51, 52, (int) Math.round(0.7 * 255))); |
||||||
|
g2.fillRoundRect(0, 0, width, height, 0, 0); |
||||||
|
|
||||||
|
g2.setColor(Color.WHITE); |
||||||
|
if (strs != null) { |
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT); |
||||||
|
for (int i = 0; i < strs.length; i++) { |
||||||
|
g2.drawString(strs[i], icon.getIconWidth() + 6, (metrics.getHeight()) * (i + 1)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getPreferredSize(JComponent c) { |
||||||
|
FontMetrics metrics = GraphHelper.getFontMetrics(c.getFont()); |
||||||
|
String tipText = ((JToolTip) c).getTipText(); |
||||||
|
icon = ((UIToolTip)c).getIcon(); |
||||||
|
needPaint = true; |
||||||
|
if (tipText == null) { |
||||||
|
if(icon.getIconWidth() == -1) { |
||||||
|
needPaint = false; |
||||||
|
} |
||||||
|
tipText = " "; |
||||||
|
} |
||||||
|
BufferedReader br = new BufferedReader(new StringReader(tipText)); |
||||||
|
String line; |
||||||
|
int maxWidth = 0; |
||||||
|
Vector<String> v = new Vector<String>(); |
||||||
|
try { |
||||||
|
while ((line = br.readLine()) != null) { |
||||||
|
int width = SwingUtilities.computeStringWidth(metrics, line); |
||||||
|
maxWidth = (maxWidth < width) ? width : maxWidth; |
||||||
|
v.addElement(line); |
||||||
|
} |
||||||
|
} catch (IOException ex) { |
||||||
|
FineLoggerFactory.getLogger().error(ex.getMessage(), ex); |
||||||
|
} |
||||||
|
int lines = v.size(); |
||||||
|
if (lines < 1) { |
||||||
|
strs = null; |
||||||
|
lines = 1; |
||||||
|
} else { |
||||||
|
strs = new String[lines]; |
||||||
|
int i = 0; |
||||||
|
for (Enumeration<String> e = v.elements(); e.hasMoreElements(); i++) { |
||||||
|
strs[i] = e.nextElement(); |
||||||
|
} |
||||||
|
} |
||||||
|
int height = metrics.getHeight() * lines; |
||||||
|
return new Dimension(maxWidth + icon.getIconWidth() + 10, Math.max(height, icon.getIconHeight()) + 6); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
package com.fr.design.data.datapane.auth; |
||||||
|
|
||||||
|
import com.fr.base.TableData; |
||||||
|
import com.fr.data.impl.Connection; |
||||||
|
import com.fr.data.impl.DBTableData; |
||||||
|
import com.fr.data.impl.NameDatabaseConnection; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import com.fr.workspace.server.connection.DBConnectAuth; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Collections; |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据连接权限相关的工具类 |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class TableDataAuthHelper { |
||||||
|
|
||||||
|
/** |
||||||
|
* 编辑数据集时是否需要检查权限 |
||||||
|
* @param tableData |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static boolean needCheckAuthWhenEdit(TableData tableData) { |
||||||
|
// 远程设计下,编辑DBTableData时需要判断权限
|
||||||
|
return !WorkContext.getCurrent().isLocal() && tableData instanceof DBTableData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取无权限数据连接集合 |
||||||
|
* 远程下需要调用RPC,为耗时操作,谨慎使用 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static Collection<String> getNoAuthConnections() { |
||||||
|
// 获取无权限连接集合
|
||||||
|
Collection<String> noAuthConnections = WorkContext.getCurrent().get(DBConnectAuth.class).getNoAuthConnections(); |
||||||
|
return noAuthConnections == null ? Collections.emptyList() : noAuthConnections; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 通过数据集获取其数据连接的名称 |
||||||
|
* |
||||||
|
* 注意: |
||||||
|
* 1. Connection接口本身是不提供名称的,只有我们内部为了使用方便,将其包装成了NameDataBaseConnection |
||||||
|
* 如果不是NameDataBaseConnection类型,则无名称,因此这里只能用判断类型的方式获取名称 |
||||||
|
* 2. 仅支持DBTableData获取连接名 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getConnectionNameByDBTableData(DBTableData tableData) { |
||||||
|
Connection database = tableData.getDatabase(); |
||||||
|
if (database instanceof NameDatabaseConnection) { |
||||||
|
return ((NameDatabaseConnection) database).getName(); |
||||||
|
} |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.data.tabledata.tabledatapane.loading; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 可切换的DBTableData对应的数据集面板,需要使用CardLayout布局 |
||||||
|
* 主要是给插件适配用的 |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public interface SwitchableTableDataPane { |
||||||
|
|
||||||
|
/** Loading面板 */ |
||||||
|
String LOADING_PANE_NAME = "Loading"; |
||||||
|
/** 内容面板 */ |
||||||
|
String CONTENT_PANE_NAME = "Content"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据面板名称切换面板 |
||||||
|
* @param paneName 面板名称 |
||||||
|
*/ |
||||||
|
void switchTo(String paneName); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package com.fr.design.data.tabledata.tabledatapane.loading; |
||||||
|
|
||||||
|
import com.fr.design.dialog.BasicPane; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
import java.awt.CardLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class TableDataLoadingPane extends BasicPane { |
||||||
|
|
||||||
|
/** Loading面板 */ |
||||||
|
public static final String LOADING_PANE_NAME = "Loading"; |
||||||
|
/** 无权限提示面板 */ |
||||||
|
public static final String NO_AUTH_PANE_NAME = "NoAuthority"; |
||||||
|
/** 错误提示面板 */ |
||||||
|
public static final String ERROR_NAME = "Error"; |
||||||
|
|
||||||
|
private CardLayout card; |
||||||
|
|
||||||
|
/** 加载中面板 */ |
||||||
|
private JPanel loadingPane; |
||||||
|
/** 错误提示面板 */ |
||||||
|
private JPanel errorPane; |
||||||
|
/** 数据连接无权限面板 */ |
||||||
|
private JPanel noAuthorityPane; |
||||||
|
|
||||||
|
public TableDataLoadingPane() { |
||||||
|
initPanes(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initPanes() { |
||||||
|
card = new CardLayout(); |
||||||
|
this.setLayout(card); |
||||||
|
loadingPane = new TipsPane(true); |
||||||
|
errorPane = new TipsPane(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Error")); |
||||||
|
noAuthorityPane = new TipsPane(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_No_Auth")); |
||||||
|
add(LOADING_PANE_NAME, loadingPane); |
||||||
|
add(NO_AUTH_PANE_NAME, noAuthorityPane); |
||||||
|
add(ERROR_NAME, errorPane); |
||||||
|
switchTo(LOADING_PANE_NAME); |
||||||
|
} |
||||||
|
|
||||||
|
public void switchTo(String panelName) { |
||||||
|
card.show(this, panelName); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String title4PopupWindow() { |
||||||
|
return Toolkit.i18nText("Fine-Design_Basic_DS-Database_Query"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.data.tabledata.tabledatapane.loading; |
||||||
|
|
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JProgressBar; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* 提示面板,支持自定义提示,支持进度条配置可选 |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class TipsPane extends JPanel { |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认提示 |
||||||
|
*/ |
||||||
|
private static final String LOADING = Toolkit.i18nText("Fine-Design_Basic_Loading_And_Waiting"); |
||||||
|
|
||||||
|
public TipsPane () { |
||||||
|
this(LOADING, false); |
||||||
|
} |
||||||
|
|
||||||
|
public TipsPane (String tip) { |
||||||
|
this(tip, false); |
||||||
|
} |
||||||
|
|
||||||
|
public TipsPane (boolean needProgressBar) { |
||||||
|
this(LOADING, needProgressBar); |
||||||
|
} |
||||||
|
|
||||||
|
public TipsPane (String tips, boolean needProgressBar) { |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
UILabel tipsLabel = new UILabel(tips, SwingConstants.CENTER); |
||||||
|
this.add(tipsLabel, BorderLayout.CENTER); |
||||||
|
if (needProgressBar) { |
||||||
|
JProgressBar progressBar = new JProgressBar(); |
||||||
|
progressBar.setIndeterminate(true); |
||||||
|
this.add(progressBar, BorderLayout.SOUTH); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
package com.fr.design.extra.exe.callback.handle; |
||||||
|
|
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.dialog.link.MessageWithLink; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.plugin.error.PluginErrorCode; |
||||||
|
import com.fr.plugin.manage.control.PluginTaskResult; |
||||||
|
|
||||||
|
/** |
||||||
|
* 帮助处理插件操作(安装、更新等)的弹窗 |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class PluginCallBackHelper { |
||||||
|
|
||||||
|
private static final String NEW_LINE_TAG = "<br/>"; |
||||||
|
private static final String REFERENCE = Toolkit.i18nText("Fine-Design_Basic_Plugin_File_Validate_Reference"); |
||||||
|
private static final String HELP_DOCUMENT_NAME = Toolkit.i18nText("Fine-Design_Basic_Plugin_File_Validate_HELP_DOCUMENT_NAME"); |
||||||
|
private static final String CONNECTOR = "-"; |
||||||
|
private static final String HELP_DOCUMENT_LINK = Toolkit.i18nText("Fine-Design_Basic_Plugin_File_Validate_HELP_DOCUMENT_LINK"); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 展示插件操作失败后的提示弹窗 |
||||||
|
* @param result |
||||||
|
* @param pluginInfo |
||||||
|
*/ |
||||||
|
public static void showErrorMessage(PluginTaskResult result, String pluginInfo) { |
||||||
|
// 单独处理下插件完整性校验失败的提示
|
||||||
|
if (PluginCallBackHelper.needHandleInvalidatePackage(result)) { |
||||||
|
MessageWithLink messageWithLink = PluginCallBackHelper.generate4InvalidatePackage(pluginInfo); |
||||||
|
PluginTaskResultErrorDialog resultDialog = new PluginTaskResultErrorDialog(null, messageWithLink); |
||||||
|
resultDialog.showResult(); |
||||||
|
} else { |
||||||
|
FineJOptionPane.showMessageDialog(null, pluginInfo, Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning"), FineJOptionPane.ERROR_MESSAGE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否需要处理 插件安装包校验失败 导致的安装失败任务 |
||||||
|
* @param result |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static boolean needHandleInvalidatePackage(PluginTaskResult result) { |
||||||
|
return !result.isSuccess() && result.getCode() == PluginErrorCode.InstallPackageValidateFailed; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据插件原始报错信息,构建MessageWithLink |
||||||
|
* @param originInfo |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static MessageWithLink generate4InvalidatePackage(String originInfo) { |
||||||
|
|
||||||
|
return new MessageWithLink(getSupplementaryMessage(originInfo), HELP_DOCUMENT_NAME, HELP_DOCUMENT_LINK); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据插件原始报错信息,获取增加了补充说明后的信息 |
||||||
|
* @param originInfo |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static String getSupplementaryMessage(String originInfo) { |
||||||
|
return new StringBuilder() |
||||||
|
.append(originInfo) |
||||||
|
.append(NEW_LINE_TAG) |
||||||
|
.append(REFERENCE) |
||||||
|
.append(CONNECTOR) |
||||||
|
.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
package com.fr.design.extra.exe.callback.handle; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
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 com.fr.design.layout.VerticalFlowLayout; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Frame; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前仅处理Error提示,之后有需要再扩展 |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class PluginTaskResultErrorDialog extends JDialog { |
||||||
|
|
||||||
|
private static final Dimension LABEL = new Dimension(60, 90); |
||||||
|
|
||||||
|
private JPanel contentPane; |
||||||
|
|
||||||
|
private UILabel errorLabel; |
||||||
|
|
||||||
|
private UIButton confirmButton; |
||||||
|
|
||||||
|
private MessageWithLink messageWithLink; |
||||||
|
|
||||||
|
public PluginTaskResultErrorDialog(Frame parent, MessageWithLink messageWithLink) { |
||||||
|
super(parent, true); |
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Plugin_Warning")); |
||||||
|
this.messageWithLink = messageWithLink; |
||||||
|
|
||||||
|
initContentPane(); |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
this.setResizable(false); |
||||||
|
this.add(contentPane, BorderLayout.CENTER); |
||||||
|
this.setSize(new Dimension( 380, 160)); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化内容面板 |
||||||
|
*/ |
||||||
|
private void initContentPane() { |
||||||
|
this.contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
// error图标
|
||||||
|
errorLabel = new UILabel(IconUtils.readIcon("/com/fr/design/standard/system/error_tips.svg")); |
||||||
|
errorLabel.setPreferredSize(LABEL); |
||||||
|
errorLabel.setBorder(BorderFactory.createEmptyBorder(10, 20, 40, 20)); |
||||||
|
// 提示内容
|
||||||
|
JPanel messagePane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
messagePane.add(errorLabel, BorderLayout.WEST); |
||||||
|
messagePane.add(messageWithLink, BorderLayout.CENTER); |
||||||
|
messagePane.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 10)); |
||||||
|
this.contentPane.add(messagePane, BorderLayout.CENTER); |
||||||
|
// 确定按钮
|
||||||
|
confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Button_OK")); |
||||||
|
confirmButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
hideResult(); |
||||||
|
} |
||||||
|
}); |
||||||
|
JPanel confirmPane = new JPanel(new VerticalFlowLayout()); |
||||||
|
confirmPane.add(confirmButton); |
||||||
|
confirmPane.setBorder(BorderFactory.createEmptyBorder(0, 160, 10, 0)); |
||||||
|
this.contentPane.add(confirmPane, BorderLayout.SOUTH); |
||||||
|
} |
||||||
|
|
||||||
|
public void showResult() { |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void hideResult() { |
||||||
|
this.setVisible(false); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package com.fr.design.gui.itree.refreshabletree; |
||||||
|
|
||||||
|
import com.fr.data.impl.TreeAttr; |
||||||
|
|
||||||
|
public interface TreeAttrChangeListener { |
||||||
|
void doChange(TreeAttr treeAttr); |
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
package com.fr.design.plugin.remind; |
||||||
|
|
||||||
|
import com.fr.design.dialog.NotificationDialogAction; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.notification.Notification; |
||||||
|
import com.fr.design.notification.NotificationCenter; |
||||||
|
import com.fr.plugin.error.PluginErrorRemindHandler; |
||||||
|
import com.fr.plugin.error.PluginErrorReminder; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.collections.CollectionUtils; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
|
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 插件错误信息提醒-设计器模块 |
||||||
|
* |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class PluginErrorDesignReminder implements PluginErrorReminder { |
||||||
|
|
||||||
|
private static final String MESSAGE_ID = "plugin-invalidate-remind"; |
||||||
|
public static final String COMMA = "、"; |
||||||
|
public static final String COLON = ":"; |
||||||
|
public static final String NEW_LINE_TAG = "<br/>"; |
||||||
|
private static final int MAX_PLUGIN_NAME_PER_LINE = 3; |
||||||
|
|
||||||
|
private static class Holder { |
||||||
|
private static final PluginErrorDesignReminder INSTANCE = new PluginErrorDesignReminder(); |
||||||
|
} |
||||||
|
|
||||||
|
private PluginErrorDesignReminder() {} |
||||||
|
|
||||||
|
public static PluginErrorDesignReminder getInstance() { |
||||||
|
return Holder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remindStartFailedPlugins() { |
||||||
|
|
||||||
|
if (!WorkContext.getCurrent().isLocal()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String content = PluginErrorRemindHandler.pluginErrorContent(); |
||||||
|
if (StringUtils.isNotEmpty(content)) { |
||||||
|
// 该操作需要在当前awt操作之后执行,使用SwingUtilities.invokeLater将其置于awt操作对列末尾
|
||||||
|
// 若使用UIUtil.invokeLaterIfNeeded,会立即执行,影响其他UI操作
|
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
PluginStartFailedRemindDialog dialog = new PluginStartFailedRemindDialog(DesignerContext.getDesignerFrame(), content); |
||||||
|
dialog.setVisible(true); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void remindInvalidatePlugins() { |
||||||
|
|
||||||
|
if (!WorkContext.getCurrent().isLocal()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// 获取失效插件名称列表
|
||||||
|
List<String> embedPluginNames = PluginErrorRemindHandler.getInvalidateEmbedPluginNames(); |
||||||
|
if (!CollectionUtils.isEmpty(embedPluginNames)) { |
||||||
|
// 构建消息
|
||||||
|
String message = generateMessageContent(embedPluginNames); |
||||||
|
Notification notification = generateNotification(message, embedPluginNames); |
||||||
|
// 添加消息
|
||||||
|
NotificationCenter.getInstance().addNotification(notification); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建消息内容 |
||||||
|
* @param invalidatePluginNames |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String generateMessageContent(List<String> invalidatePluginNames) { |
||||||
|
return new StringBuilder() |
||||||
|
.append(Toolkit.i18nText("Fine-Design_Plugin_Embed_Notice")) |
||||||
|
.append(COLON) |
||||||
|
.append(NEW_LINE_TAG) |
||||||
|
.append(NEW_LINE_TAG) |
||||||
|
.append(dealWithPluginNames(invalidatePluginNames)) |
||||||
|
.append(NEW_LINE_TAG) |
||||||
|
.append(NEW_LINE_TAG) |
||||||
|
.append(Toolkit.i18nText("Fine-Design_Plugin_Embed_Description")) |
||||||
|
.append(NEW_LINE_TAG) |
||||||
|
.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处理消息中的插件名称展示 |
||||||
|
* 由于Notification那边展示的弹窗是随消息宽度变化的,所以插件名称很多时会变得很长。这里做个分行 |
||||||
|
* @return |
||||||
|
* @param invalidatePluginNames |
||||||
|
*/ |
||||||
|
public String dealWithPluginNames(List<String> invalidatePluginNames) { |
||||||
|
StringBuilder builder = new StringBuilder(); |
||||||
|
for (int i = 0; i < invalidatePluginNames.size(); i++) { |
||||||
|
String pluginName = invalidatePluginNames.get(i); |
||||||
|
builder.append(pluginName); |
||||||
|
if (i != invalidatePluginNames.size() - 1) { |
||||||
|
builder.append(i % MAX_PLUGIN_NAME_PER_LINE == (MAX_PLUGIN_NAME_PER_LINE - 1) ? NEW_LINE_TAG : COMMA); |
||||||
|
} |
||||||
|
} |
||||||
|
return builder.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 构建通知对象 |
||||||
|
* @param message |
||||||
|
* @param invalidatePluginNames |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private Notification generateNotification(String message, List<String> invalidatePluginNames) { |
||||||
|
return new Notification( |
||||||
|
MESSAGE_ID, |
||||||
|
Notification.WARNING_MESSAGE, |
||||||
|
message, |
||||||
|
new NotificationDialogAction() { |
||||||
|
@Override |
||||||
|
public String name() { |
||||||
|
return "plugin-invalidate-remind-show"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void doClick() { |
||||||
|
PluginInvalidateRemindDialog remindDialog = new PluginInvalidateRemindDialog( |
||||||
|
DesignerContext.getDesignerFrame(), |
||||||
|
invalidatePluginNames, |
||||||
|
PluginErrorRemindHandler.getInvalidateEmbedPluginMarkers(), |
||||||
|
MESSAGE_ID); |
||||||
|
remindDialog.setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,211 @@ |
|||||||
|
package com.fr.design.plugin.remind; |
||||||
|
|
||||||
|
import com.fr.common.util.Collections; |
||||||
|
import com.fr.design.actions.UpdateAction; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.DesignSizeI18nManager; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.notification.NotificationCenter; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.general.FRFont; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.plugin.context.PluginMarker; |
||||||
|
import com.fr.plugin.manage.PluginManager; |
||||||
|
import com.fr.plugin.manage.control.PluginTaskCallback; |
||||||
|
import com.fr.plugin.manage.control.PluginTaskResult; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JScrollPane; |
||||||
|
import javax.swing.JTextPane; |
||||||
|
import javax.swing.text.SimpleAttributeSet; |
||||||
|
import javax.swing.text.StyleConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Frame; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
public class PluginInvalidateRemindDialog extends JDialog implements ActionListener{ |
||||||
|
|
||||||
|
/** |
||||||
|
* 因内置而失效的插件Marker列表 |
||||||
|
*/ |
||||||
|
private List<PluginMarker> pluginMarkers; |
||||||
|
|
||||||
|
/** |
||||||
|
* 因内置而失效的插件名称列表 |
||||||
|
*/ |
||||||
|
private List<String> pluginNames; |
||||||
|
|
||||||
|
/** |
||||||
|
* 本弹窗对应的消息的id |
||||||
|
* 方便弹窗逻辑执行完之后,删除通知中心的消息 |
||||||
|
*/ |
||||||
|
private String messageID; |
||||||
|
|
||||||
|
|
||||||
|
public PluginInvalidateRemindDialog(Frame parent, List<String> pluginNames, List<PluginMarker> pluginMarkers, String messageId) { |
||||||
|
super(parent, true); |
||||||
|
this.pluginMarkers = pluginMarkers; |
||||||
|
this.pluginNames = pluginNames; |
||||||
|
this.messageID = messageId; |
||||||
|
// 标题
|
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Tool_Tips")); |
||||||
|
this.setResizable(false); |
||||||
|
|
||||||
|
this.add(initTopPane(), BorderLayout.NORTH); |
||||||
|
this.add(initCenterPanel(), BorderLayout.CENTER); |
||||||
|
this.add(initBottomPanel(), BorderLayout.SOUTH); |
||||||
|
this.setSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.plugin.remind.PluginInvalidateRemindDialog.dialog")); |
||||||
|
|
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 上层的面板,用于解释插件内置 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private JPanel initTopPane() { |
||||||
|
JPanel topPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
|
||||||
|
// 图标面板
|
||||||
|
JPanel imagePanel = new JPanel(); |
||||||
|
Icon icon = IOUtils.readIcon("com/fr/design/images/warnings/icon_WarningIcon_normal.png"); |
||||||
|
UILabel imageLabel = new UILabel(); |
||||||
|
imageLabel.setIcon(icon); |
||||||
|
imagePanel.add(imageLabel); |
||||||
|
imagePanel.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 10)); |
||||||
|
|
||||||
|
JPanel verticalPanel = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true); |
||||||
|
UILabel noticeLabel = new UILabel(Toolkit.i18nText("Fine-Design_Plugin_Embed_Notice")); |
||||||
|
noticeLabel.setFont(FRFont.getInstance().applySize(16)); |
||||||
|
UILabel adviceLabel = new UILabel(Toolkit.i18nText("Fine-Design_Plugin_Embed_Advice")); |
||||||
|
UILabel descriptionLabel = new UILabel(Toolkit.i18nText("Fine-Design_Plugin_Embed_Description")); |
||||||
|
verticalPanel.add(noticeLabel); |
||||||
|
verticalPanel.add(adviceLabel); |
||||||
|
verticalPanel.add(descriptionLabel); |
||||||
|
|
||||||
|
topPane.add(imagePanel, BorderLayout.WEST); |
||||||
|
topPane.add(verticalPanel, BorderLayout.CENTER); |
||||||
|
topPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 20)); |
||||||
|
return topPane; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 中层面板,用于展示内置插件列表 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private JPanel initCenterPanel() { |
||||||
|
JPanel centerPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
|
||||||
|
// "插件列表"标签面板
|
||||||
|
UILabel textLabel = new UILabel(Toolkit.i18nText("Fine-Design_Plugin_Embed_List")); |
||||||
|
textLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); |
||||||
|
|
||||||
|
JTextPane textPane = new JTextPane(); |
||||||
|
textPane.setEditable(false); |
||||||
|
textPane.setBackground(Color.WHITE); |
||||||
|
SimpleAttributeSet attributeSet = new SimpleAttributeSet(); |
||||||
|
StyleConstants.setLineSpacing(attributeSet, 0.5f); |
||||||
|
textPane.setParagraphAttributes(attributeSet, true); |
||||||
|
textPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
||||||
|
textPane.setText(generatePluginListText(pluginNames)); |
||||||
|
JScrollPane scrollPane = new JScrollPane(textPane); |
||||||
|
|
||||||
|
centerPane.add(textLabel, BorderLayout.NORTH); |
||||||
|
centerPane.add(scrollPane, BorderLayout.CENTER); |
||||||
|
centerPane.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 10)); |
||||||
|
centerPane.setPreferredSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.plugin.remind.PluginInvalidateRemindDialog.centerPane")); |
||||||
|
return centerPane; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 生成中间面板展示的插件列表 |
||||||
|
* @param pluginNames |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private String generatePluginListText(List<String> pluginNames) { |
||||||
|
String space = " "; |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
for (String pluginName : pluginNames) { |
||||||
|
sb.append(space).append(pluginName).append("\n"); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 底层面板,用于处理用户操作 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private JPanel initBottomPanel() { |
||||||
|
JPanel bottomPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
|
||||||
|
UIButton ignore = new UIButton(Toolkit.i18nText("Fine-Design_Plugin_Embed_Ignore")); |
||||||
|
UIButton delete = new UIButton(Toolkit.i18nText("Fine-Design_Plugin_Embed_Delete_Plugins")); |
||||||
|
UILabel emptyLabel = new UILabel(); |
||||||
|
ignore.addActionListener(this); |
||||||
|
delete.addActionListener(new DeleteEmbedPluginsAction(this)); |
||||||
|
bottomPane.add(ignore, BorderLayout.WEST); |
||||||
|
bottomPane.add(emptyLabel, BorderLayout.CENTER); |
||||||
|
bottomPane.add(delete, BorderLayout.EAST); |
||||||
|
return bottomPane; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
this.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
private class DeleteEmbedPluginsAction extends UpdateAction { |
||||||
|
|
||||||
|
private JDialog dialog; |
||||||
|
|
||||||
|
|
||||||
|
DeleteEmbedPluginsAction(JDialog dialog) { |
||||||
|
this.dialog = dialog; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
this.dialog.dispose(); |
||||||
|
// 删除插件
|
||||||
|
deleteEmbedPlugins(pluginMarkers); |
||||||
|
// 删除消息
|
||||||
|
NotificationCenter.getInstance().removeNotificationByMessageID(messageID); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 删除插件 |
||||||
|
* @param toDelete |
||||||
|
*/ |
||||||
|
private void deleteEmbedPlugins(List<PluginMarker> toDelete) { |
||||||
|
if (Collections.isEmpty(toDelete)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
for (PluginMarker pluginMarker : toDelete) { |
||||||
|
// 删除插件
|
||||||
|
PluginManager.getController().uninstall(pluginMarker, false, new PluginTaskCallback() { |
||||||
|
@Override |
||||||
|
public void done(PluginTaskResult result) { |
||||||
|
// 输出结果日志
|
||||||
|
FineLoggerFactory.getLogger().info( |
||||||
|
"Detele Embed Plugin(id = {}, version = {}) {}", |
||||||
|
pluginMarker.getPluginID(), |
||||||
|
pluginMarker.getVersion(), result.isSuccess() ? "Succeeded" : "Failed"); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.fr.design.update.actions; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.common.util.Strings; |
||||||
|
import com.fr.design.utils.BrowseUtils; |
||||||
|
import com.fr.general.CloudCenter; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 帮助-更新升级 |
||||||
|
* 点击查看新特性 |
||||||
|
* */ |
||||||
|
public class NewFeatureAction implements ActionListener { |
||||||
|
|
||||||
|
|
||||||
|
public static String DEFAULT_UPDATE_DETAIL_URL = "https://help.fanruan.com/finereport/doc-view-4699.html"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
try { |
||||||
|
String url = CloudCenter.getInstance().acquireConf("fr.latest.update.detil"); |
||||||
|
if (Strings.isEmpty(url)) { |
||||||
|
url = DEFAULT_UPDATE_DETAIL_URL; |
||||||
|
} |
||||||
|
BrowseUtils.browser(url); |
||||||
|
} catch (Exception ex) { |
||||||
|
FineLoggerFactory.getLogger().error(ex.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
package com.fr.start.common; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.file.HistoryTemplateListPane; |
||||||
|
import com.fr.design.file.MutilTempalteTabPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.DesignSizeI18nManager; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.DesignerFrame; |
||||||
|
import com.fr.design.utils.ColorUtils; |
||||||
|
|
||||||
|
import javax.swing.JButton; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.border.EmptyBorder; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/09 |
||||||
|
**/ |
||||||
|
public class DesignerOpenEmptyPanel extends JPanel { |
||||||
|
|
||||||
|
private static final Color BUTTON_COLOR = new Color(63, 155, 249); |
||||||
|
private static final int ARC = 4; |
||||||
|
|
||||||
|
private final JPanel body; |
||||||
|
|
||||||
|
public DesignerOpenEmptyPanel() { |
||||||
|
|
||||||
|
this.body = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
|
||||||
|
UILabel createIcon = new UILabel(IconUtils.readIcon("/com/fr/design/startup/create_new_template.svg")); |
||||||
|
JButton createButton = new JButton(Toolkit.i18nText("Fine-Design_New_Template")) { |
||||||
|
@Override |
||||||
|
public void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
g2d.setColor(BUTTON_COLOR); |
||||||
|
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), ARC, ARC); |
||||||
|
super.paintComponent(g2d); |
||||||
|
} |
||||||
|
}; |
||||||
|
createButton.setPreferredSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.start.common.DesignerOpenEmptyPanel.createButton")); |
||||||
|
createButton.setForeground(Color.WHITE); |
||||||
|
createButton.setBorderPainted(false); |
||||||
|
createButton.setContentAreaFilled(false); |
||||||
|
createButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
DesignerFrame df = DesignerContext.getDesignerFrame(); |
||||||
|
df.addAndActivateJTemplate(); |
||||||
|
// 如果没有模板,则需要确认一下
|
||||||
|
MutilTempalteTabPane.getInstance().setTemTemplate(HistoryTemplateListPane.getInstance().getCurrentEditingTemplate()); |
||||||
|
} |
||||||
|
}); |
||||||
|
createButton.setBorder(new EmptyBorder(0, 10, 0, 10)); |
||||||
|
JPanel createButtonPanel = new JPanel(FRGUIPaneFactory.createCenterFlowLayout()); |
||||||
|
createButtonPanel.add(createButton); |
||||||
|
createButtonPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); |
||||||
|
createButtonPanel.setBackground(Color.WHITE); |
||||||
|
this.body.add(createIcon, BorderLayout.NORTH); |
||||||
|
this.body.add(createButtonPanel, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
setLayout(FRGUIPaneFactory.createCenterLayout(this.body)); |
||||||
|
|
||||||
|
ColorUtils.syncBackground(this, Color.WHITE); |
||||||
|
|
||||||
|
add(this.body); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package com.fr.start.common; |
||||||
|
|
||||||
|
import com.fr.stable.xml.XMLPrintWriter; |
||||||
|
import com.fr.stable.xml.XMLable; |
||||||
|
import com.fr.stable.xml.XMLableReader; |
||||||
|
|
||||||
|
public class DesignerStartupConfig implements XMLable { |
||||||
|
|
||||||
|
public static final String XML_TAG = "DesignerStartupConfig"; |
||||||
|
|
||||||
|
private static final long serialVersionUID = -8170289826729582122L; |
||||||
|
|
||||||
|
private static final DesignerStartupConfig INSTANCE = new DesignerStartupConfig(); |
||||||
|
|
||||||
|
public static DesignerStartupConfig getInstance() { |
||||||
|
|
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认值是 false |
||||||
|
*/ |
||||||
|
private boolean enabled = false; |
||||||
|
|
||||||
|
public boolean isEnabled() { |
||||||
|
return enabled; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) { |
||||||
|
this.enabled = enabled; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Object clone() throws CloneNotSupportedException { |
||||||
|
DesignerStartupConfig config = new DesignerStartupConfig(); |
||||||
|
config.setEnabled(true); |
||||||
|
return config; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readXML(XMLableReader reader) { |
||||||
|
if (reader.isAttr()) { |
||||||
|
this.setEnabled(reader.getAttrAsBoolean("isEnabled", false)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeXML(XMLPrintWriter writer) { |
||||||
|
writer.startTAG(XML_TAG); |
||||||
|
writer.attr("isEnabled", this.isEnabled()); |
||||||
|
writer.end(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,176 @@ |
|||||||
|
package com.fr.start.common; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.env.DesignerWorkspaceInfo; |
||||||
|
import com.fr.design.env.DesignerWorkspaceType; |
||||||
|
import com.fr.start.module.StartupArgs; |
||||||
|
import com.fr.startup.ui.StartupPageModel; |
||||||
|
import com.fr.third.guava.collect.Lists; |
||||||
|
import com.fr.third.org.apache.commons.lang3.time.StopWatch; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Iterator; |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动页上下文 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/06/22 |
||||||
|
**/ |
||||||
|
public class DesignerStartupContext { |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动参数 |
||||||
|
*/ |
||||||
|
private StartupArgs startupArgs; |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动数据 |
||||||
|
*/ |
||||||
|
private StartupPageModel startupPageModel; |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否在起始页打开的等待过程中 |
||||||
|
*/ |
||||||
|
private boolean onWaiting = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否在启动中 |
||||||
|
*/ |
||||||
|
private boolean onStartup = true; |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否在预热中 |
||||||
|
*/ |
||||||
|
private boolean onWarmup = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* 打开上一次的文件 |
||||||
|
*/ |
||||||
|
private boolean openLastFile; |
||||||
|
|
||||||
|
/** |
||||||
|
* 打开空设计器 |
||||||
|
*/ |
||||||
|
private boolean openEmpty; |
||||||
|
|
||||||
|
/** |
||||||
|
* 直接创建一个新模板 |
||||||
|
*/ |
||||||
|
private boolean createNew; |
||||||
|
|
||||||
|
/** |
||||||
|
* 时间记录 |
||||||
|
*/ |
||||||
|
private static StopWatch STOP_WATCH = new StopWatch(); |
||||||
|
|
||||||
|
public static DesignerStartupContext getInstance() { |
||||||
|
return StartupContextHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class StartupContextHolder { |
||||||
|
private static final DesignerStartupContext INSTANCE = new DesignerStartupContext(); |
||||||
|
} |
||||||
|
|
||||||
|
public static StopWatch getRecorder() { |
||||||
|
return STOP_WATCH; |
||||||
|
} |
||||||
|
|
||||||
|
/* 启动模式 */ |
||||||
|
|
||||||
|
/** |
||||||
|
* 展示启动页 |
||||||
|
* 1. 判断当前的工作目录数量 |
||||||
|
* 2. 判断是否是 demo、还是打开目标文件 |
||||||
|
* 3. 功能是否开启 |
||||||
|
* |
||||||
|
* @return 是/否 |
||||||
|
*/ |
||||||
|
public boolean isShowStartupPage() { |
||||||
|
|
||||||
|
DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); |
||||||
|
Iterator<String> envNameIterator = envManager.getEnvNameIterator(); |
||||||
|
ArrayList<String> envs = Lists.newArrayList(envNameIterator); |
||||||
|
return !startupArgs.isDemo() && DesignerStartupUtil.convertArgs2FILE(startupArgs.get()) == null && !envs.isEmpty() && envManager.isStartupPageEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
/* 预热相关 */ |
||||||
|
|
||||||
|
public boolean onWarmup() { |
||||||
|
|
||||||
|
return this.onWarmup; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean canWarmup() { |
||||||
|
|
||||||
|
String curEnvName = DesignerEnvManager.getEnvManager().getCurEnvName(); |
||||||
|
DesignerWorkspaceInfo workspaceInfo = DesignerEnvManager.getEnvManager().getWorkspaceInfo(curEnvName); |
||||||
|
return workspaceInfo.getType() == DesignerWorkspaceType.Local; |
||||||
|
} |
||||||
|
|
||||||
|
public void setStartupPageModel(StartupPageModel startupPageModel) { |
||||||
|
this.startupPageModel = startupPageModel; |
||||||
|
} |
||||||
|
|
||||||
|
public StartupPageModel getStartupPageModel() { |
||||||
|
|
||||||
|
return startupPageModel; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isOnWaiting() { |
||||||
|
return onWaiting; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOnWaiting(boolean onWaiting) { |
||||||
|
this.onWaiting = onWaiting; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isSupport() { |
||||||
|
return onStartup && isShowStartupPage(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isOnStartup() { |
||||||
|
return onStartup; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOnStartup(boolean onStartup) { |
||||||
|
this.onStartup = onStartup; |
||||||
|
} |
||||||
|
|
||||||
|
/* 文件相关*/ |
||||||
|
|
||||||
|
public boolean isOpenLastFile() { |
||||||
|
|
||||||
|
return this.openLastFile; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isOpenEmpty() { |
||||||
|
|
||||||
|
return this.openEmpty; |
||||||
|
} |
||||||
|
|
||||||
|
/* set */ |
||||||
|
|
||||||
|
public void setOnWarmup(boolean onWarmup) { |
||||||
|
this.onWarmup = onWarmup; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOpenLastFile(boolean openLastFile) { |
||||||
|
this.openLastFile = openLastFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOpenEmpty(boolean openEmpty) { |
||||||
|
this.openEmpty = openEmpty; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isCreateNew() { |
||||||
|
return createNew; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCreateNew(boolean createNew) { |
||||||
|
this.createNew = createNew; |
||||||
|
} |
||||||
|
|
||||||
|
public void setStartupArgs(StartupArgs startupArgs) { |
||||||
|
this.startupArgs = startupArgs; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.start.common; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/03 |
||||||
|
**/ |
||||||
|
public class DesignerStartupExecutor { |
||||||
|
|
||||||
|
private List<Runnable> warmupTasks = new ArrayList<>(); |
||||||
|
|
||||||
|
public void execute(Runnable runnable) { |
||||||
|
|
||||||
|
if (!DesignerStartupContext.getInstance().onWarmup()) { |
||||||
|
runnable.run(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
|
||||||
|
warmupTasks.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
public static DesignerStartupExecutor getInstance() { |
||||||
|
return DesignerStartupExecutorHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class DesignerStartupExecutorHolder { |
||||||
|
private static final DesignerStartupExecutor INSTANCE = new DesignerStartupExecutor(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.fr.start.common; |
||||||
|
|
||||||
|
import com.fr.concurrent.FineExecutors; |
||||||
|
import com.fr.concurrent.NamedThreadFactory; |
||||||
|
|
||||||
|
import java.util.concurrent.Executor; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/03 |
||||||
|
**/ |
||||||
|
public class DesignerStartupPool { |
||||||
|
|
||||||
|
private static final Executor COMMON_EXECUTOR = FineExecutors.newCachedThreadPool(new NamedThreadFactory("startup-common")); |
||||||
|
|
||||||
|
public static Executor common() { |
||||||
|
|
||||||
|
return COMMON_EXECUTOR; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.fr.start.common; |
||||||
|
|
||||||
|
import com.fr.base.extension.FileExtension; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.file.FILE; |
||||||
|
import com.fr.file.FILEFactory; |
||||||
|
import com.fr.file.FileFILE; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/09 |
||||||
|
**/ |
||||||
|
public class DesignerStartupUtil { |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public static FILE convertArgs2FILE(String[] args) { |
||||||
|
|
||||||
|
// p:需要打开这个报表文件,这个代码不能删除.
|
||||||
|
FILE file = null; |
||||||
|
for (String arg : args) { |
||||||
|
if (ComparatorUtils.equals("demo", arg)) { |
||||||
|
file = FILEFactory.createFILE(FILEFactory.ENV_PREFIX + DesignerEnvManager.getEnvManager().getLastOpenFile()); |
||||||
|
break; |
||||||
|
} |
||||||
|
File f = new File(arg); |
||||||
|
String path = f.getAbsolutePath(); |
||||||
|
if (isAcceptFilePathEnd(path)) { |
||||||
|
file = new FileFILE(f); |
||||||
|
} |
||||||
|
} |
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isAcceptFilePathEnd(String path) { |
||||||
|
FileExtension[] acceptFileExtensions = new FileExtension[]{ |
||||||
|
FileExtension.CPT, FileExtension.XLS, FileExtension.XLSX, FileExtension.FRM, FileExtension.CHT, FileExtension.VIS |
||||||
|
}; |
||||||
|
for (FileExtension acceptFileExtension : acceptFileExtensions) { |
||||||
|
String[] extensions = acceptFileExtension.getExtensions(); |
||||||
|
for (String extension : extensions) { |
||||||
|
if (path.endsWith("." + extension)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/07 |
||||||
|
**/ |
||||||
|
public class StartupPageConstants { |
||||||
|
|
||||||
|
/** |
||||||
|
* 圆弧长度 |
||||||
|
*/ |
||||||
|
public static final int ARC_DIAMETER = 20; |
||||||
|
|
||||||
|
/** |
||||||
|
* 内容宽度 |
||||||
|
*/ |
||||||
|
public static final int CONTENT_WIDTH = 850; |
||||||
|
} |
@ -0,0 +1,111 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.env.DesignerWorkspaceInfo; |
||||||
|
import com.fr.design.env.DesignerWorkspaceType; |
||||||
|
import com.fr.third.guava.collect.Lists; |
||||||
|
import com.fr.workspace.connect.WorkspaceConnectionInfo; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/06 |
||||||
|
**/ |
||||||
|
public class StartupPageModel { |
||||||
|
|
||||||
|
private StartupWorkspaceBean selectWorkspaceInfo; |
||||||
|
|
||||||
|
private List<StartupWorkspaceBean> workspaceInfos = new ArrayList<>(); |
||||||
|
|
||||||
|
private Map<String, List<String>> recentFilesMap = new HashMap<>(); |
||||||
|
|
||||||
|
private Runnable openLastTemplateRunnable; |
||||||
|
|
||||||
|
private Runnable createNewTemplateRunnable; |
||||||
|
|
||||||
|
private Runnable openEmptyTemplateRunnable; |
||||||
|
|
||||||
|
public static StartupPageModel create() { |
||||||
|
|
||||||
|
DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); |
||||||
|
Iterator<String> envNameIterator = envManager.getEnvNameIterator(); |
||||||
|
List<StartupWorkspaceBean> infos = Lists.newArrayList(envNameIterator) |
||||||
|
.stream() |
||||||
|
.map((e) -> { |
||||||
|
DesignerWorkspaceInfo workspaceInfo = envManager.getWorkspaceInfo(e); |
||||||
|
if (workspaceInfo.getType() == DesignerWorkspaceType.Remote) { |
||||||
|
WorkspaceConnectionInfo connection = workspaceInfo.getConnection(); |
||||||
|
return new StartupWorkspaceBean(e, connection.getUrl(), workspaceInfo.getType()); |
||||||
|
} else { |
||||||
|
return new StartupWorkspaceBean(e, workspaceInfo.getPath(), workspaceInfo.getType()); |
||||||
|
} |
||||||
|
}) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
Map<String, List<String>> recentFileMap = new HashMap<>(); |
||||||
|
for (StartupWorkspaceBean info : infos) { |
||||||
|
String name = info.getName(); |
||||||
|
List<String> recentFiles = envManager.getRecentOpenedFilePathList4Env(name); |
||||||
|
recentFileMap.put(name, recentFiles); |
||||||
|
} |
||||||
|
return new StartupPageModel(infos, recentFileMap); |
||||||
|
} |
||||||
|
|
||||||
|
public StartupPageModel(List<StartupWorkspaceBean> workspaceInfos, Map<String, List<String>> recentFilesMap) { |
||||||
|
this.selectWorkspaceInfo = workspaceInfos.get(0); |
||||||
|
this.workspaceInfos = workspaceInfos; |
||||||
|
this.recentFilesMap = recentFilesMap; |
||||||
|
} |
||||||
|
|
||||||
|
public StartupWorkspaceBean getSelectWorkspaceInfo() { |
||||||
|
return selectWorkspaceInfo; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSelectWorkspaceInfo(StartupWorkspaceBean selectWorkspaceInfo) { |
||||||
|
this.selectWorkspaceInfo = selectWorkspaceInfo; |
||||||
|
} |
||||||
|
|
||||||
|
public List<StartupWorkspaceBean> getWorkspaceInfos() { |
||||||
|
return workspaceInfos; |
||||||
|
} |
||||||
|
|
||||||
|
public void setWorkspaceInfos(List<StartupWorkspaceBean> workspaceInfos) { |
||||||
|
this.workspaceInfos = workspaceInfos; |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, List<String>> getRecentFilesMap() { |
||||||
|
return recentFilesMap; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRecentFilesMap(Map<String, List<String>> recentFilesMap) { |
||||||
|
this.recentFilesMap = recentFilesMap; |
||||||
|
} |
||||||
|
|
||||||
|
public Runnable getOpenLastTemplateRunnable() { |
||||||
|
return openLastTemplateRunnable; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOpenLastTemplateRunnable(Runnable openLastTemplateRunnable) { |
||||||
|
this.openLastTemplateRunnable = openLastTemplateRunnable; |
||||||
|
} |
||||||
|
|
||||||
|
public Runnable getCreateNewTemplateRunnable() { |
||||||
|
return createNewTemplateRunnable; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCreateNewTemplateRunnable(Runnable createNewTemplateRunnable) { |
||||||
|
this.createNewTemplateRunnable = createNewTemplateRunnable; |
||||||
|
} |
||||||
|
|
||||||
|
public Runnable getOpenEmptyTemplateRunnable() { |
||||||
|
return openEmptyTemplateRunnable; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOpenEmptyTemplateRunnable(Runnable openEmptyTemplateRunnable) { |
||||||
|
this.openEmptyTemplateRunnable = openEmptyTemplateRunnable; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
import com.fr.base.svg.SVGIcon; |
||||||
|
import com.fr.design.env.DesignerWorkspaceType; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/11 |
||||||
|
**/ |
||||||
|
public class StartupPageUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取最近区域的 ICON |
||||||
|
* |
||||||
|
* @param workspaceBean 工作目录 |
||||||
|
* @return 图标 |
||||||
|
*/ |
||||||
|
public static Icon getIcon4RecentAreaByWorkspace(StartupWorkspaceBean workspaceBean) { |
||||||
|
|
||||||
|
if (workspaceBean.getType() == DesignerWorkspaceType.Local) { |
||||||
|
return SVGIcon.readSVGIcon("/com/fr/design/startup/local_server_background_36.svg", 36, 36); |
||||||
|
} |
||||||
|
return SVGIcon.readSVGIcon("/com/fr/design/startup/remote_server_background_36.svg", 36, 36); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取工作目录描述区域的 ICON |
||||||
|
* |
||||||
|
* @param workspaceBean 工作目录 |
||||||
|
* @return 图标 |
||||||
|
*/ |
||||||
|
public static Icon getIcon4DescAreaByWorkspace(StartupWorkspaceBean workspaceBean) { |
||||||
|
|
||||||
|
if (workspaceBean.getType() == DesignerWorkspaceType.Local) { |
||||||
|
return SVGIcon.readSVGIcon("/com/fr/design/startup/local_server_background_28.svg", 28, 28); |
||||||
|
} |
||||||
|
return SVGIcon.readSVGIcon("/com/fr/design/startup/remote_server_background_28.svg", 28, 28); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,339 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.components.loading.LoadingPane; |
||||||
|
import com.fr.design.dialog.UIExpandDialog; |
||||||
|
import com.fr.design.gui.icontainer.UIScrollPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.layout.VerticalFlowLayout; |
||||||
|
import com.fr.design.ui.util.UIUtil; |
||||||
|
import com.fr.design.utils.ColorUtils; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.collections.CollectionUtils; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.JLayeredPane; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JSeparator; |
||||||
|
import javax.swing.ScrollPaneConstants; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import javax.swing.border.EmptyBorder; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.Font; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.GridLayout; |
||||||
|
import java.awt.LayoutManager; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* 启动页 |
||||||
|
* 见<a href="https://kms.fineres.com/pages/viewpage.action?pageId=416850313">设计文档</a> |
||||||
|
* <p> |
||||||
|
* created by Harrison on 2022/07/06 |
||||||
|
**/ |
||||||
|
public class StartupPageWindow extends JFrame { |
||||||
|
|
||||||
|
private static final int CONTENT_LAYER = 0; |
||||||
|
private static final int TRANSPARENT_LAYER = 1; |
||||||
|
|
||||||
|
private static final Color HOVER_COLOR = new Color(65, 155, 249); |
||||||
|
private static final Color SEP_COLOR = new Color(224, 224, 225); |
||||||
|
|
||||||
|
private static final int GROUP_WIDTH = 600; |
||||||
|
private static final int RECENT_FILE_LIMIT = 6; |
||||||
|
private static final int RECENT_FILE_SCROLL = RECENT_FILE_LIMIT + 1; |
||||||
|
private static final int WORKSPACE_PANEL_WIDTH = 180; |
||||||
|
private static final int TITLE_FONT_SIZE = 24; |
||||||
|
private static final int ITEM_VERTICAL_GAP = 5; |
||||||
|
|
||||||
|
private static final Dimension SCREEN_SIZE = new Dimension(1600, 820); |
||||||
|
|
||||||
|
private StartupPageWorkspacePanel workspacePanel; |
||||||
|
|
||||||
|
private JPanel recentOpenPanel; |
||||||
|
|
||||||
|
private JPanel contentPane; |
||||||
|
|
||||||
|
private JPanel body; |
||||||
|
|
||||||
|
private LoadingPane loadingPane = new LoadingPane(); |
||||||
|
|
||||||
|
private JLayeredPane layeredPane = new JLayeredPane() { |
||||||
|
@Override |
||||||
|
public void doLayout() { |
||||||
|
for (Component comp : getComponents()) { |
||||||
|
comp.setBounds(0, 0, getWidth(), getHeight()); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public StartupPageWindow(StartupPageModel pageModel) { |
||||||
|
|
||||||
|
patchUIAction(pageModel); |
||||||
|
|
||||||
|
setLayout(new BorderLayout()); |
||||||
|
|
||||||
|
this.body = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
// Header
|
||||||
|
UILabel label = new UILabel(Toolkit.i18nText("Fine-Design_Startup_Page_Select_Workspace")); |
||||||
|
Font font = label.getFont(); |
||||||
|
Font titleFont = font.deriveFont(font.getStyle(), TITLE_FONT_SIZE); |
||||||
|
label.setFont(titleFont); |
||||||
|
JPanel headerPanel = new JPanel(); |
||||||
|
LayoutManager centerFlowLayout = FRGUIPaneFactory.createCenterFlowLayout(); |
||||||
|
headerPanel.setLayout(centerFlowLayout); |
||||||
|
headerPanel.add(label); |
||||||
|
this.body.add(headerPanel, BorderLayout.NORTH); |
||||||
|
|
||||||
|
// Workspace-description
|
||||||
|
this.workspacePanel = generateWorkspacePanel(pageModel); |
||||||
|
this.body.add(workspacePanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
workspacePanel.setSelectWorkspaceRunnable(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
JPanel newPanel = generateRecentOpenPanel(pageModel); |
||||||
|
|
||||||
|
body.remove(recentOpenPanel); |
||||||
|
recentOpenPanel = newPanel; |
||||||
|
body.add(recentOpenPanel, BorderLayout.SOUTH); |
||||||
|
validate(); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
this.recentOpenPanel = generateRecentOpenPanel(pageModel); |
||||||
|
this.body.add(recentOpenPanel, BorderLayout.SOUTH); |
||||||
|
this.contentPane = new JPanel(); |
||||||
|
this.contentPane.setLayout(getCenterLayout(body)); |
||||||
|
this.contentPane.add(this.body, BorderLayout.CENTER); |
||||||
|
this.contentPane.setPreferredSize(this.body.getPreferredSize()); |
||||||
|
|
||||||
|
this.layeredPane.setName("layered-pane"); |
||||||
|
this.layeredPane.add(this.contentPane, CONTENT_LAYER); |
||||||
|
this.layeredPane.add(this.loadingPane, TRANSPARENT_LAYER); |
||||||
|
this.layeredPane.moveToFront(this.contentPane); |
||||||
|
|
||||||
|
add(this.layeredPane, BorderLayout.CENTER); |
||||||
|
|
||||||
|
// Workspace-detail
|
||||||
|
setSize(SCREEN_SIZE); |
||||||
|
|
||||||
|
repaint(); |
||||||
|
validate(); |
||||||
|
revalidate(); |
||||||
|
|
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
} |
||||||
|
|
||||||
|
private void patchUIAction(StartupPageModel pageModel) { |
||||||
|
|
||||||
|
Runnable selectAndOpenLastTemplateRunnable = pageModel.getOpenLastTemplateRunnable(); |
||||||
|
pageModel.setOpenLastTemplateRunnable(() -> { |
||||||
|
enterWorkspace(selectAndOpenLastTemplateRunnable); |
||||||
|
}); |
||||||
|
|
||||||
|
Runnable createNewTemplateRunnable = pageModel.getCreateNewTemplateRunnable(); |
||||||
|
pageModel.setCreateNewTemplateRunnable(() -> { |
||||||
|
enterWorkspace(createNewTemplateRunnable); |
||||||
|
}); |
||||||
|
|
||||||
|
Runnable openEmptyTemplateRunnable = pageModel.getOpenEmptyTemplateRunnable(); |
||||||
|
pageModel.setOpenEmptyTemplateRunnable(() -> { |
||||||
|
enterWorkspace(openEmptyTemplateRunnable); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void enterWorkspace(Runnable action) { |
||||||
|
|
||||||
|
loadingPane.start(); |
||||||
|
layeredPane.moveToFront(loadingPane); |
||||||
|
SwingWorker<Void, Void> task = new SwingWorker<Void, Void>() { |
||||||
|
@Override |
||||||
|
protected Void doInBackground() throws Exception { |
||||||
|
action.run(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void done() { |
||||||
|
|
||||||
|
try { |
||||||
|
Void result = get(); |
||||||
|
setVisible(false); |
||||||
|
} catch (Exception e) { |
||||||
|
// 处理错误
|
||||||
|
UIUtil.invokeLaterIfNeeded(() -> { |
||||||
|
UIExpandDialog.Builder() |
||||||
|
.owner(StartupPageWindow.this) |
||||||
|
.title(Toolkit.i18nText("Fine-Design_Basic_Remote_Env_Try")) |
||||||
|
.message(Toolkit.i18nText("Fine-Design_Basic_Connection_Failed")) |
||||||
|
.messageType(UIExpandDialog.WARNING_MESSAGE) |
||||||
|
.detail(e.getMessage()) |
||||||
|
.expand(true) |
||||||
|
.modal(false) |
||||||
|
.build() |
||||||
|
.setVisible(true); |
||||||
|
}); |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
layeredPane.moveToFront(contentPane); |
||||||
|
} finally { |
||||||
|
loadingPane.stop(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
task.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel generateRecentOpenPanel(StartupPageModel pageModel) { |
||||||
|
|
||||||
|
JPanel recentOpenPanel = new JPanel() { |
||||||
|
@Override |
||||||
|
protected void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
g2d.setColor(Color.WHITE); |
||||||
|
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), StartupPageConstants.ARC_DIAMETER, StartupPageConstants.ARC_DIAMETER); |
||||||
|
} |
||||||
|
}; |
||||||
|
recentOpenPanel.setLayout(new BorderLayout()); |
||||||
|
recentOpenPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 6)); |
||||||
|
|
||||||
|
StartupWorkspaceBean workspaceInfo = pageModel.getSelectWorkspaceInfo(); |
||||||
|
JPanel workspaceWrapperYPanel = new JPanel(); |
||||||
|
workspaceWrapperYPanel.setName("workspace-wrapper"); |
||||||
|
{ |
||||||
|
workspaceWrapperYPanel.setLayout(new VerticalFlowLayout()); |
||||||
|
|
||||||
|
JPanel workspaceWrapperXPanel = new JPanel(); |
||||||
|
workspaceWrapperXPanel.setLayout(new FlowLayout()); |
||||||
|
workspaceWrapperXPanel.setBorder(BorderFactory.createEmptyBorder()); |
||||||
|
|
||||||
|
JPanel workspacePanel = new JPanel(); |
||||||
|
workspacePanel.setLayout(new BorderLayout(0, 15)); |
||||||
|
|
||||||
|
UILabel workspaceIcon = new UILabel(StartupPageUtil.getIcon4RecentAreaByWorkspace(workspaceInfo)); |
||||||
|
workspacePanel.add(workspaceIcon, BorderLayout.NORTH); |
||||||
|
|
||||||
|
UILabel nameLabel = new UILabel(workspaceInfo.getName()); |
||||||
|
nameLabel.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
workspacePanel.add(nameLabel, BorderLayout.SOUTH); |
||||||
|
workspaceWrapperXPanel.add(workspacePanel); |
||||||
|
Dimension preferredSize = workspaceWrapperXPanel.getPreferredSize(); |
||||||
|
workspaceWrapperXPanel.setPreferredSize(new Dimension(WORKSPACE_PANEL_WIDTH, (int) preferredSize.getHeight())); |
||||||
|
|
||||||
|
workspaceWrapperYPanel.add(workspaceWrapperXPanel); |
||||||
|
} |
||||||
|
recentOpenPanel.add(workspaceWrapperYPanel, BorderLayout.WEST); |
||||||
|
|
||||||
|
JPanel separatorPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
{ |
||||||
|
JSeparator sep = new JSeparator(); |
||||||
|
sep.setOrientation(JSeparator.VERTICAL); |
||||||
|
sep.setForeground(SEP_COLOR); |
||||||
|
separatorPanel.add(sep, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
recentOpenPanel.add(separatorPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
JComponent recentOpenGroupPanel = generateRecentOpenGroupPanel(pageModel, workspaceInfo); |
||||||
|
recentOpenPanel.add(recentOpenGroupPanel, BorderLayout.EAST); |
||||||
|
|
||||||
|
ColorUtils.syncBackground(recentOpenPanel, Color.WHITE); |
||||||
|
|
||||||
|
Dimension preferredSize = recentOpenPanel.getPreferredSize(); |
||||||
|
recentOpenPanel.setPreferredSize(new Dimension(StartupPageConstants.CONTENT_WIDTH, (int) preferredSize.getHeight())); |
||||||
|
|
||||||
|
JPanel recentOpenWrapperPanel = new JPanel(); |
||||||
|
recentOpenWrapperPanel.setName("recentOpenWrapper"); |
||||||
|
recentOpenWrapperPanel.setLayout(new BorderLayout(0, 0)); |
||||||
|
recentOpenWrapperPanel.setBorder(new EmptyBorder(0, 0, 0, 20)); |
||||||
|
recentOpenWrapperPanel.add(recentOpenPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
return recentOpenWrapperPanel; |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private JComponent generateRecentOpenGroupPanel(StartupPageModel pageModel, StartupWorkspaceBean workspaceInfo) { |
||||||
|
|
||||||
|
JPanel recentOpenGroupPanel = new JPanel(); |
||||||
|
Map<String, List<String>> recentFilesMap = pageModel.getRecentFilesMap(); |
||||||
|
|
||||||
|
boolean needScroll = false; |
||||||
|
double itemHeight = 0.0d; |
||||||
|
if (!CollectionUtils.isEmpty(recentFilesMap)) { |
||||||
|
String name = workspaceInfo.getName(); |
||||||
|
List<String> recentFiles = recentFilesMap.get(name); |
||||||
|
if (!CollectionUtils.isEmpty(recentFiles)) { |
||||||
|
recentOpenGroupPanel.setLayout(new GridLayout(recentFiles.size(), 1, 50, 5)); |
||||||
|
needScroll = recentFiles.size() > RECENT_FILE_LIMIT; |
||||||
|
for (String recentFile : recentFiles) { |
||||||
|
JPanel recentItemPanel = new JPanel(); |
||||||
|
recentItemPanel.setLayout(new FlowLayout(FlowLayout.LEFT, ITEM_VERTICAL_GAP, 0)); |
||||||
|
recentItemPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/system/cpt.svg"))); |
||||||
|
UILabel recentFileLabel = new UILabel(recentFile); |
||||||
|
Color recentFileLabelForeground = recentFileLabel.getForeground(); |
||||||
|
recentItemPanel.add(recentFileLabel); |
||||||
|
recentItemPanel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent e) { |
||||||
|
recentFileLabel.setForeground(HOVER_COLOR); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseExited(MouseEvent e) { |
||||||
|
recentFileLabel.setForeground(recentFileLabelForeground); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
DesignerEnvManager.getEnvManager().setLastOpenFile(recentFile); |
||||||
|
pageModel.getOpenLastTemplateRunnable().run(); |
||||||
|
} |
||||||
|
}); |
||||||
|
Dimension preferredSize = recentItemPanel.getPreferredSize(); |
||||||
|
itemHeight = preferredSize.getHeight(); |
||||||
|
recentOpenGroupPanel.add(recentItemPanel); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Dimension preferredSize = recentOpenGroupPanel.getPreferredSize(); |
||||||
|
recentOpenGroupPanel.setPreferredSize(new Dimension(GROUP_WIDTH, (int) preferredSize.getHeight())); |
||||||
|
|
||||||
|
if (needScroll) { |
||||||
|
int scrollHeight = (int) Math.round(itemHeight * RECENT_FILE_LIMIT + ITEM_VERTICAL_GAP * (RECENT_FILE_SCROLL)); |
||||||
|
UIScrollPane scrollPane = new UIScrollPane(recentOpenGroupPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
||||||
|
scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0)); |
||||||
|
scrollPane.setPreferredSize(new Dimension(GROUP_WIDTH, scrollHeight)); |
||||||
|
return scrollPane; |
||||||
|
} |
||||||
|
return recentOpenGroupPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private StartupPageWorkspacePanel generateWorkspacePanel(StartupPageModel pageModel) { |
||||||
|
|
||||||
|
return new StartupPageWorkspacePanel(pageModel); |
||||||
|
} |
||||||
|
|
||||||
|
protected LayoutManager getCenterLayout(JComponent centerBody) { |
||||||
|
|
||||||
|
return FRGUIPaneFactory.createCenterLayout(centerBody); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,501 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.components.tooltip.ModernToolTip; |
||||||
|
import com.fr.design.gui.icontainer.UIScrollPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.utils.ColorUtils; |
||||||
|
import com.fr.third.guava.collect.Lists; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JToolTip; |
||||||
|
import javax.swing.ScrollPaneConstants; |
||||||
|
import javax.swing.border.EmptyBorder; |
||||||
|
import java.awt.BasicStroke; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.Font; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.GridLayout; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.atomic.AtomicReference; |
||||||
|
|
||||||
|
import static com.fr.startup.ui.StartupPageConstants.ARC_DIAMETER; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/06 |
||||||
|
**/ |
||||||
|
public class StartupPageWorkspacePanel extends JPanel { |
||||||
|
|
||||||
|
/* color */ |
||||||
|
|
||||||
|
private static final Color SHALLOW_WHITE_COLOR = new Color(248, 250, 254); |
||||||
|
private static final Color HOVER_COLOR = new Color(65, 155, 249); |
||||||
|
private static final Color PATH_COLOR = new Color(51, 51, 52, (int) Math.round(255 * 0.5)); |
||||||
|
|
||||||
|
/* 长度 */ |
||||||
|
|
||||||
|
private static final int SCROLL_BAR_WIDTH = 20; |
||||||
|
|
||||||
|
private static final int CONTENT_WIDTH = StartupPageConstants.CONTENT_WIDTH + SCROLL_BAR_WIDTH; |
||||||
|
private static final int BORDER_THIN = 2; |
||||||
|
|
||||||
|
private static final int ITEM_VERTICAL_GAP = 20; |
||||||
|
private static final int SINGLE_ITEM_HEIGHT = 72; |
||||||
|
private static final int SCROLL_HEIGHT = SINGLE_ITEM_HEIGHT * 4 + ITEM_VERTICAL_GAP * 4; |
||||||
|
|
||||||
|
private static final int NAME_LABEL_SIZE = 15; |
||||||
|
private static final int PATH_LABEL_SIZE = 10; |
||||||
|
|
||||||
|
private static final Dimension LABEL_DIMENSION = new Dimension(28, 28); |
||||||
|
private static final Dimension PATH_DIMENSION = new Dimension(100, 20); |
||||||
|
private static final Dimension SELECT_WORKSPACE_DIMENSION = new Dimension(210, 72); |
||||||
|
private static final Dimension SELECT_CREATE_DIMENSION = new Dimension(60, 72); |
||||||
|
public static final int COLUMN_LIMIT = 3; |
||||||
|
|
||||||
|
/* model */ |
||||||
|
|
||||||
|
private final StartupPageModel pageModel; |
||||||
|
|
||||||
|
private final List<List<StartupWorkspaceBean>> partitions; |
||||||
|
|
||||||
|
private Runnable selectWorkspaceRunnable; |
||||||
|
|
||||||
|
private final Runnable createNewTemplateRunnable; |
||||||
|
|
||||||
|
private final Runnable openEmptyTemplateRunnable; |
||||||
|
|
||||||
|
private JComponent contentPanel; |
||||||
|
|
||||||
|
private JPanel tailPanel; |
||||||
|
|
||||||
|
private boolean showMore = true; |
||||||
|
|
||||||
|
public StartupPageWorkspacePanel(StartupPageModel pageModel) { |
||||||
|
|
||||||
|
this.setLayout(new BorderLayout(0, 0)); |
||||||
|
|
||||||
|
this.pageModel = pageModel; |
||||||
|
|
||||||
|
List<StartupWorkspaceBean> workspaceInfos = pageModel.getWorkspaceInfos(); |
||||||
|
this.partitions = Lists.partition(workspaceInfos, COLUMN_LIMIT); |
||||||
|
|
||||||
|
this.contentPanel = generateLimitContentPanel(partitions); |
||||||
|
this.add(contentPanel, BorderLayout.NORTH); |
||||||
|
|
||||||
|
this.tailPanel = generateTailPanel(); |
||||||
|
|
||||||
|
this.createNewTemplateRunnable = pageModel.getCreateNewTemplateRunnable(); |
||||||
|
this.openEmptyTemplateRunnable = pageModel.getOpenEmptyTemplateRunnable(); |
||||||
|
|
||||||
|
this.add(tailPanel, BorderLayout.SOUTH); |
||||||
|
this.repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
public void showLessContent() { |
||||||
|
|
||||||
|
this.remove(this.contentPanel); |
||||||
|
|
||||||
|
this.contentPanel = generateLimitContentPanel(this.partitions); |
||||||
|
this.add(contentPanel, BorderLayout.NORTH); |
||||||
|
} |
||||||
|
|
||||||
|
public void showMoreContent() { |
||||||
|
|
||||||
|
this.remove(this.contentPanel); |
||||||
|
|
||||||
|
this.contentPanel = generateUnLimitContentPanel(this.partitions); |
||||||
|
this.add(contentPanel, BorderLayout.NORTH); |
||||||
|
} |
||||||
|
|
||||||
|
private JComponent generateUnLimitContentPanel(List<List<StartupWorkspaceBean>> partitions) { |
||||||
|
|
||||||
|
JPanel workspaceDescPanel = new JPanel(); |
||||||
|
workspaceDescPanel.setLayout(new GridLayout(partitions.size(), 1, 0, ITEM_VERTICAL_GAP)); |
||||||
|
for (List<StartupWorkspaceBean> partition : partitions) { |
||||||
|
JPanel partitionPanel = generatePartitionPanel(partition); |
||||||
|
workspaceDescPanel.add(partitionPanel); |
||||||
|
} |
||||||
|
boolean needScroll = partitions.size() > 4; |
||||||
|
if (needScroll) { |
||||||
|
// 滚动条
|
||||||
|
UIScrollPane scrollPane = new UIScrollPane(workspaceDescPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); |
||||||
|
scrollPane.setBorder(new EmptyBorder(10, 0, 0, 0)); |
||||||
|
scrollPane.setPreferredSize(new Dimension(CONTENT_WIDTH, SCROLL_HEIGHT)); |
||||||
|
return scrollPane; |
||||||
|
} |
||||||
|
return workspaceDescPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel generateLimitContentPanel(List<List<StartupWorkspaceBean>> partitions) { |
||||||
|
|
||||||
|
JPanel workspaceDescPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEFT, 0, ITEM_VERTICAL_GAP); |
||||||
|
int limit = 2; |
||||||
|
for (int i = 0; i < partitions.size(); i++) { |
||||||
|
if (i >= limit) { |
||||||
|
break; |
||||||
|
} |
||||||
|
List<StartupWorkspaceBean> partition = partitions.get(i); |
||||||
|
|
||||||
|
JPanel partitionPanel = generatePartitionPanel(partition); |
||||||
|
workspaceDescPanel.add(partitionPanel); |
||||||
|
} |
||||||
|
return workspaceDescPanel; |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private JPanel generateTailPanel() { |
||||||
|
|
||||||
|
JPanel tailPanel = new JPanel(); |
||||||
|
{ |
||||||
|
tailPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
||||||
|
tailPanel.setBorder(new EmptyBorder(0, 0, 0, 20)); |
||||||
|
JPanel showAllPanel = new JPanel(); |
||||||
|
showAllPanel.setLayout(new BorderLayout(5, 0)); |
||||||
|
showAllPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); |
||||||
|
|
||||||
|
UILabel fontLabel = new UILabel(Toolkit.i18nText("Fine-Design_Startup_Page_Expand_All")); |
||||||
|
fontLabel.setForeground(HOVER_COLOR); |
||||||
|
showAllPanel.add(fontLabel, BorderLayout.WEST); |
||||||
|
|
||||||
|
UILabel iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/startup/show_more.svg")); |
||||||
|
showAllPanel.add(iconLabel, BorderLayout.EAST); |
||||||
|
|
||||||
|
Color showAllBackground = showAllPanel.getBackground(); |
||||||
|
|
||||||
|
showAllPanel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent e) { |
||||||
|
Color hoverColor = new Color(217, 235, 254); |
||||||
|
showAllPanel.setBackground(hoverColor); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseExited(MouseEvent e) { |
||||||
|
ColorUtils.syncBackground(showAllPanel, showAllBackground); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
if (showMore) { |
||||||
|
fontLabel.setText(Toolkit.i18nText("Fine-Design_Startup_Page_Collapse_Workspace")); |
||||||
|
iconLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/show_less.svg")); |
||||||
|
showMoreContent(); |
||||||
|
showMore = !showMore; |
||||||
|
} else { |
||||||
|
fontLabel.setText(Toolkit.i18nText("Fine-Design_Startup_Page_Expand_All")); |
||||||
|
iconLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/show_more.svg")); |
||||||
|
showLessContent(); |
||||||
|
showMore = !showMore; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
tailPanel.add(showAllPanel); |
||||||
|
Dimension preferredSize = tailPanel.getPreferredSize(); |
||||||
|
tailPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, (int) preferredSize.getHeight())); |
||||||
|
} |
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private JPanel generatePartitionPanel(List<StartupWorkspaceBean> partition) { |
||||||
|
|
||||||
|
JPanel partitionPanel = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(0, 20, 0);; |
||||||
|
partitionPanel.setName("partitionPanel"); |
||||||
|
|
||||||
|
for (StartupWorkspaceBean workspaceInfo : partition) { |
||||||
|
|
||||||
|
JPanel workspaceItemDesc = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
|
||||||
|
layoutSelectWorkspacePanel(workspaceInfo, workspaceItemDesc); |
||||||
|
|
||||||
|
layoutSelectAndCreatePanel(workspaceItemDesc); |
||||||
|
|
||||||
|
partitionPanel.add(workspaceItemDesc); |
||||||
|
|
||||||
|
Dimension preferredSize = partitionPanel.getPreferredSize(); |
||||||
|
partitionPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, (int) preferredSize.getHeight())); |
||||||
|
} |
||||||
|
return partitionPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private void layoutSelectWorkspacePanel(StartupWorkspaceBean workspaceInfo, JPanel workspaceItemDesc) { |
||||||
|
|
||||||
|
// 选择工作目录
|
||||||
|
// 图标 / 分隔符 / 说明-进入
|
||||||
|
// 选择并新建
|
||||||
|
AtomicReference<Color> borderColorRef = new AtomicReference<>(null); |
||||||
|
|
||||||
|
JPanel selectWorkspacePanel = new JPanel() { |
||||||
|
@Override |
||||||
|
public JToolTip createToolTip() { |
||||||
|
return new ModernToolTip(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
Color borderColor = borderColorRef.get(); |
||||||
|
|
||||||
|
Color backColor = Color.WHITE; |
||||||
|
g2d.setColor(backColor); |
||||||
|
// 直角和圆角上下叠合在一起
|
||||||
|
int rectOffset = 10; |
||||||
|
int roundOffset = 15; |
||||||
|
// 填充一个直角
|
||||||
|
g2d.fillRoundRect(0, 0, getWidth() - rectOffset, getHeight(), ARC_DIAMETER, ARC_DIAMETER); |
||||||
|
// 填充一个圆角
|
||||||
|
g2d.fillRoundRect(getWidth() - roundOffset, 0, roundOffset, getHeight(), 0, 0); |
||||||
|
paintBorderIfHover(g2d, borderColor, backColor); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当悬浮的时候,将边框展示出来 |
||||||
|
* 会叠合,然后填充中间 |
||||||
|
* |
||||||
|
* |----【-|--】 |
||||||
|
* | A 【C| B】 |
||||||
|
* |----【-|--】 |
||||||
|
* |
||||||
|
* 见上面有两种线,分别是 A-| 和 B-【 |
||||||
|
* 这里会将 A 和 B 叠在一起,中间则存在 C,然后将 C 的部分扩大一点,然后填充上背景 |
||||||
|
* 则形成下面这种图形 |
||||||
|
* |
||||||
|
* |----】 |
||||||
|
* |----】 |
||||||
|
* |
||||||
|
* @param g2d 绘画 |
||||||
|
* @param borderColor 边框颜色 |
||||||
|
* @param backColor 背景颜色 |
||||||
|
*/ |
||||||
|
private void paintBorderIfHover(Graphics2D g2d, Color borderColor, Color backColor) { |
||||||
|
|
||||||
|
if (borderColor != null) { |
||||||
|
g2d.setColor(borderColor); |
||||||
|
g2d.setStroke(new BasicStroke(BORDER_THIN)); |
||||||
|
// 需要一个修正值
|
||||||
|
int strokeOffset = BORDER_THIN / 2; |
||||||
|
// 直角和圆角上下叠合在一起
|
||||||
|
int rectOffset = 10; |
||||||
|
int roundOffset = 15; |
||||||
|
// 画一个圆角
|
||||||
|
int fixRoundWidth = getWidth() - rectOffset; |
||||||
|
int fixRoundHeight = getHeight() - BORDER_THIN; |
||||||
|
g2d.drawRoundRect(strokeOffset, strokeOffset, fixRoundWidth, fixRoundHeight, ARC_DIAMETER, ARC_DIAMETER); |
||||||
|
// 画一个直角
|
||||||
|
g2d.drawRoundRect(getWidth() - roundOffset, strokeOffset, roundOffset - strokeOffset, getHeight() - BORDER_THIN, 0, 0); |
||||||
|
|
||||||
|
g2d.setColor(backColor); |
||||||
|
|
||||||
|
// 绘制一个矩形,覆盖住多余的相交线
|
||||||
|
// 需要考虑上下的线宽
|
||||||
|
int coverHeight = getHeight() - (BORDER_THIN * 2); |
||||||
|
// 偏左一点的 fixedX
|
||||||
|
int fixedX = getWidth() - roundOffset - BORDER_THIN; |
||||||
|
// 圆角和直角相交的区域
|
||||||
|
int coverWidth = 10; |
||||||
|
g2d.fillRect(fixedX, BORDER_THIN, coverWidth, coverHeight); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
selectWorkspacePanel.setLayout(new BorderLayout(0,0)); |
||||||
|
selectWorkspacePanel.setToolTipText(Toolkit.i18nText("Fine-Design_Startup_Page_Double_Click_Enter_Workspace")); |
||||||
|
selectWorkspacePanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
||||||
|
{ |
||||||
|
|
||||||
|
JPanel iconPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
iconPanel.setBorder(new EmptyBorder(0, 10, 0, 10)); |
||||||
|
Icon icon = StartupPageUtil.getIcon4DescAreaByWorkspace(workspaceInfo); |
||||||
|
UILabel label = new UILabel(icon); |
||||||
|
label.setPreferredSize(LABEL_DIMENSION); |
||||||
|
iconPanel.add(label, BorderLayout.CENTER); |
||||||
|
selectWorkspacePanel.add(iconPanel, BorderLayout.WEST); |
||||||
|
|
||||||
|
// desc / >箭头
|
||||||
|
JPanel descPanel = new JPanel(); |
||||||
|
descPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||||
|
descPanel.setBorder(new EmptyBorder(0, 10, 0, 0)); |
||||||
|
|
||||||
|
JPanel simpleDescPanelWrapper = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.CENTER, 0, 0); |
||||||
|
JPanel simpleDescPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
UILabel nameLabel = new UILabel(workspaceInfo.getName()); |
||||||
|
Font font = nameLabel.getFont(); |
||||||
|
Font newSizeFont = font.deriveFont(font.getStyle(), NAME_LABEL_SIZE); |
||||||
|
nameLabel.setFont(newSizeFont); |
||||||
|
Color nameForeground = nameLabel.getForeground(); |
||||||
|
simpleDescPanel.add(nameLabel,BorderLayout.NORTH); |
||||||
|
|
||||||
|
UILabel pathLabel = new UILabel(workspaceInfo.getPath()); |
||||||
|
pathLabel.setPreferredSize(PATH_DIMENSION); |
||||||
|
Font pathFont = pathLabel.getFont(); |
||||||
|
pathLabel.setFont(pathFont.deriveFont(pathFont.getStyle(), PATH_LABEL_SIZE)); |
||||||
|
Color pathColor = PATH_COLOR; |
||||||
|
pathLabel.setForeground(pathColor); |
||||||
|
simpleDescPanel.add(pathLabel, BorderLayout.SOUTH); |
||||||
|
simpleDescPanelWrapper.add(simpleDescPanel); |
||||||
|
|
||||||
|
descPanel.add(simpleDescPanelWrapper, BorderLayout.WEST); |
||||||
|
|
||||||
|
MouseAdapter selectWorkspaceMouseListener = new MouseAdapter() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent e) { |
||||||
|
Color hoverColor = HOVER_COLOR; |
||||||
|
borderColorRef.set(hoverColor); |
||||||
|
nameLabel.setForeground(hoverColor); |
||||||
|
pathLabel.setForeground(hoverColor ); |
||||||
|
selectWorkspacePanel.getParent().repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseExited(MouseEvent e) { |
||||||
|
borderColorRef.set(Color.WHITE); |
||||||
|
nameLabel.setForeground(nameForeground); |
||||||
|
pathLabel.setForeground(pathColor); |
||||||
|
selectWorkspacePanel.getParent().repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
|
||||||
|
int clickCount = e.getClickCount(); |
||||||
|
if (clickCount == BORDER_THIN) { |
||||||
|
pageModel.setSelectWorkspaceInfo(workspaceInfo); |
||||||
|
openEmptyTemplateRunnable.run(); |
||||||
|
return; |
||||||
|
} |
||||||
|
// selectWorkspaceRunnable
|
||||||
|
pageModel.setSelectWorkspaceInfo(workspaceInfo); |
||||||
|
selectWorkspaceRunnable.run(); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
UILabel arrowLabel = new UILabel(IconUtils.readIcon("/com/fr/design/startup/more.svg")) { |
||||||
|
@Override |
||||||
|
public JToolTip createToolTip() { |
||||||
|
return new ModernToolTip(); |
||||||
|
} |
||||||
|
}; |
||||||
|
arrowLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Startup_Page_Enter_Workspace")); |
||||||
|
arrowLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent e) { |
||||||
|
arrowLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/more_hover.svg")); |
||||||
|
selectWorkspaceMouseListener.mouseEntered(e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseExited(MouseEvent e) { |
||||||
|
arrowLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/more.svg")); |
||||||
|
selectWorkspaceMouseListener.mouseExited(e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
openEmptyTemplateRunnable.run(); |
||||||
|
} |
||||||
|
}); |
||||||
|
descPanel.add(arrowLabel, BorderLayout.EAST); |
||||||
|
|
||||||
|
selectWorkspacePanel.add(descPanel, BorderLayout.CENTER); |
||||||
|
selectWorkspacePanel.addMouseListener(selectWorkspaceMouseListener); |
||||||
|
} |
||||||
|
|
||||||
|
ColorUtils.syncBackground(selectWorkspacePanel, Color.WHITE); |
||||||
|
selectWorkspacePanel.setPreferredSize(SELECT_WORKSPACE_DIMENSION); |
||||||
|
workspaceItemDesc.add(selectWorkspacePanel, BorderLayout.WEST); |
||||||
|
} |
||||||
|
|
||||||
|
private void layoutSelectAndCreatePanel(JPanel workspaceItemDesc) { |
||||||
|
|
||||||
|
// 选择并新建
|
||||||
|
AtomicReference<Color> borderColorRef = new AtomicReference<>(null); |
||||||
|
JPanel selectAndCreatePanel = new JPanel() { |
||||||
|
@Override |
||||||
|
public JToolTip createToolTip() { |
||||||
|
return new ModernToolTip(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
Color borderColor = borderColorRef.get(); |
||||||
|
|
||||||
|
Color backColor = SHALLOW_WHITE_COLOR; |
||||||
|
g2d.setColor(backColor); |
||||||
|
|
||||||
|
// 见 layoutSelectWorkspacePanel 部分的分析
|
||||||
|
// 直角和圆角上下叠合在一起
|
||||||
|
int rectOffset = 10; |
||||||
|
int roundOffset = 15; |
||||||
|
int strokeOffset = BORDER_THIN / 2; |
||||||
|
int fixedRoundOffset = roundOffset + strokeOffset; |
||||||
|
g2d.fillRoundRect(0, 0, getWidth() - rectOffset, getHeight(), 0, 0); |
||||||
|
g2d.fillRoundRect(getWidth() - fixedRoundOffset, 0, roundOffset, getHeight(), ARC_DIAMETER, ARC_DIAMETER); |
||||||
|
if (borderColor != null) { |
||||||
|
g2d.setColor(borderColor); |
||||||
|
g2d.setStroke(new BasicStroke(BORDER_THIN)); |
||||||
|
// 画画的笔触需要调整一下
|
||||||
|
g2d.drawRoundRect(strokeOffset, strokeOffset, getWidth() - rectOffset, getHeight() - BORDER_THIN, 0, 0); |
||||||
|
g2d.drawRoundRect(getWidth() - fixedRoundOffset, strokeOffset, roundOffset - strokeOffset, getHeight() - BORDER_THIN, ARC_DIAMETER, ARC_DIAMETER); |
||||||
|
g2d.setColor(backColor); |
||||||
|
int fillWidth = 11; |
||||||
|
g2d.fillRect(getWidth() - fixedRoundOffset - BORDER_THIN, BORDER_THIN, fillWidth, getHeight() - BORDER_THIN * 2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
selectAndCreatePanel.setToolTipText(Toolkit.i18nText("Fine-Design_Startup_Page_Enter_Workspace_And_Create")); |
||||||
|
selectAndCreatePanel.setBorder(new EmptyBorder(0, 0, 0, 0)); |
||||||
|
selectAndCreatePanel.setLayout(new BorderLayout()); |
||||||
|
{ |
||||||
|
UILabel label = new UILabel(IconUtils.readIcon("/com/fr/design/standard/system/add.svg")); |
||||||
|
label.setPreferredSize(new Dimension(ARC_DIAMETER, ARC_DIAMETER)); |
||||||
|
label.setForeground(HOVER_COLOR); |
||||||
|
selectAndCreatePanel.add(label, BorderLayout.CENTER); |
||||||
|
selectAndCreatePanel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent e) { |
||||||
|
borderColorRef.set(HOVER_COLOR); |
||||||
|
selectAndCreatePanel.getParent().repaint(); |
||||||
|
label.setIcon(IconUtils.readIcon("/com/fr/design/standard/system/add_hover.svg")); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseExited(MouseEvent e) { |
||||||
|
borderColorRef.set(null); |
||||||
|
selectAndCreatePanel.getParent().repaint(); |
||||||
|
label.setIcon(IconUtils.readIcon("/com/fr/design/standard/system/add.svg")); |
||||||
|
} |
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
createNewTemplateRunnable.run(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
ColorUtils.syncBackground(selectAndCreatePanel, Color.GREEN); |
||||||
|
selectAndCreatePanel.setPreferredSize(SELECT_CREATE_DIMENSION); |
||||||
|
workspaceItemDesc.add(selectAndCreatePanel, BorderLayout.EAST); |
||||||
|
} |
||||||
|
|
||||||
|
public void setSelectWorkspaceRunnable(Runnable selectWorkspaceRunnable) { |
||||||
|
|
||||||
|
this.selectWorkspaceRunnable = selectWorkspaceRunnable; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
import com.fr.design.env.DesignerWorkspaceType; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/07/07 |
||||||
|
**/ |
||||||
|
public class StartupWorkspaceBean { |
||||||
|
|
||||||
|
private String name; |
||||||
|
|
||||||
|
private String path; |
||||||
|
|
||||||
|
private DesignerWorkspaceType type; |
||||||
|
|
||||||
|
public StartupWorkspaceBean(String name, String path, DesignerWorkspaceType type) { |
||||||
|
this.name = name; |
||||||
|
this.path = path; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public static StartupWorkspaceBean create(String name, String path) { |
||||||
|
return new StartupWorkspaceBean(name, path, DesignerWorkspaceType.Local); |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getPath() { |
||||||
|
return path; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPath(String path) { |
||||||
|
this.path = path; |
||||||
|
} |
||||||
|
|
||||||
|
public DesignerWorkspaceType getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public void setType(DesignerWorkspaceType type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 407 B |
After Width: | Height: | Size: 407 B |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 561 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 921 B |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 190 B |
@ -0,0 +1,72 @@ |
|||||||
|
package com.fr.design.plugin.remind; |
||||||
|
|
||||||
|
import com.fr.plugin.error.PluginErrorRemindHandler; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import com.fr.workspace.empty.EmptyWorkspace; |
||||||
|
import junit.framework.TestCase; |
||||||
|
import org.easymock.EasyMock; |
||||||
|
import org.junit.Assert; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.powermock.api.easymock.PowerMock; |
||||||
|
import org.powermock.core.classloader.annotations.PrepareForTest; |
||||||
|
import org.powermock.modules.junit4.PowerMockRunner; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Yvan |
||||||
|
*/ |
||||||
|
@RunWith(PowerMockRunner.class) |
||||||
|
@PrepareForTest(PluginErrorRemindHandler.class) |
||||||
|
public class PluginErrorDesignReminderTest extends TestCase { |
||||||
|
|
||||||
|
@Before |
||||||
|
public void before() { |
||||||
|
WorkContext.switchTo(EmptyWorkspace.getInstance()); |
||||||
|
} |
||||||
|
|
||||||
|
public void testRemindStartFailedPlugins() { |
||||||
|
PowerMock.mockStatic(PluginErrorRemindHandler.class); |
||||||
|
EasyMock.expect(PluginErrorRemindHandler.pluginErrorContent()).andReturn("").once(); |
||||||
|
|
||||||
|
PowerMock.replayAll(); |
||||||
|
PluginErrorDesignReminder.getInstance().remindStartFailedPlugins(); |
||||||
|
|
||||||
|
PowerMock.verifyAll(); |
||||||
|
} |
||||||
|
|
||||||
|
public void testRemindInvalidatePlugins() { |
||||||
|
PowerMock.mockStatic(PluginErrorRemindHandler.class); |
||||||
|
EasyMock.expect(PluginErrorRemindHandler.getInvalidateEmbedPluginNames()).andReturn(new ArrayList<>()).once(); |
||||||
|
|
||||||
|
PowerMock.replayAll(); |
||||||
|
PluginErrorDesignReminder.getInstance().remindInvalidatePlugins(); |
||||||
|
|
||||||
|
PowerMock.verifyAll(); |
||||||
|
} |
||||||
|
|
||||||
|
public void testDealWithPluginNames() { |
||||||
|
List<String> pluginNames1 = Arrays.asList("1"); |
||||||
|
String content1 = PluginErrorDesignReminder.getInstance().dealWithPluginNames(pluginNames1); |
||||||
|
Assert.assertFalse(content1.contains(PluginErrorDesignReminder.COMMA)); |
||||||
|
Assert.assertFalse(content1.contains(PluginErrorDesignReminder.NEW_LINE_TAG)); |
||||||
|
|
||||||
|
List<String> pluginNames2 = Arrays.asList("1", "2"); |
||||||
|
String content2 = PluginErrorDesignReminder.getInstance().dealWithPluginNames(pluginNames2); |
||||||
|
Assert.assertTrue(content2.contains(PluginErrorDesignReminder.COMMA)); |
||||||
|
Assert.assertFalse(content2.contains(PluginErrorDesignReminder.NEW_LINE_TAG)); |
||||||
|
|
||||||
|
List<String> pluginNames3 = Arrays.asList("1", "2", "3", "4"); |
||||||
|
String content3 = PluginErrorDesignReminder.getInstance().dealWithPluginNames(pluginNames3); |
||||||
|
Assert.assertTrue(content3.contains(PluginErrorDesignReminder.COMMA)); |
||||||
|
Assert.assertTrue(content3.contains(PluginErrorDesignReminder.NEW_LINE_TAG)); |
||||||
|
|
||||||
|
List<String> pluginNames4 = Arrays.asList("1", "2", "3"); |
||||||
|
String content4 = PluginErrorDesignReminder.getInstance().dealWithPluginNames(pluginNames4); |
||||||
|
Assert.assertTrue(content4.contains(PluginErrorDesignReminder.COMMA)); |
||||||
|
Assert.assertFalse(content4.contains(PluginErrorDesignReminder.NEW_LINE_TAG)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.startup.ui; |
||||||
|
|
||||||
|
import com.fr.design.utils.DevUtils; |
||||||
|
import com.fr.third.guava.collect.Lists; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
public class StartupPageWindowTest { |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
|
||||||
|
DevUtils.show(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
HashMap<String, List<String>> recentOpenFileMap = new HashMap<>(); |
||||||
|
recentOpenFileMap.put("111", Lists.newArrayList("111.cpt", "222//3333.cpt","333.cpt", "444.cpt", "555.cpt", "666.cpt")); |
||||||
|
StartupPageModel model = new StartupPageModel(Stream.of( |
||||||
|
StartupWorkspaceBean.create("111", "222333344455556663333444555566633334445555666"), StartupWorkspaceBean.create("113", "222"), |
||||||
|
StartupWorkspaceBean.create("114", "222"), StartupWorkspaceBean.create("115", "222"), StartupWorkspaceBean.create("116", "222"), |
||||||
|
StartupWorkspaceBean.create("117", "222"), StartupWorkspaceBean.create("118", "222"), StartupWorkspaceBean.create("119", "222"), |
||||||
|
StartupWorkspaceBean.create("121", "222"), StartupWorkspaceBean.create("122", "222"), StartupWorkspaceBean.create("123", "222"), |
||||||
|
StartupWorkspaceBean.create("124", "222"), StartupWorkspaceBean.create("125", "222"), StartupWorkspaceBean.create("126", "222") |
||||||
|
).collect(Collectors.toList()), recentOpenFileMap); |
||||||
|
StartupPageWindow window = new StartupPageWindow(model); |
||||||
|
window.setVisible(true); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -1,20 +0,0 @@ |
|||||||
package com.fr.design.fit.common; |
|
||||||
|
|
||||||
import com.fr.base.DefaultAutoChangeLine; |
|
||||||
import com.fr.base.Style; |
|
||||||
import com.fr.stable.unit.UNIT; |
|
||||||
|
|
||||||
import java.awt.Font; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
public class NewUIModeAutoChangeLine extends DefaultAutoChangeLine { |
|
||||||
@Override |
|
||||||
public List<String> textAutoChangeLine(String text, Font font, Style style, UNIT unitWidth, int resolution) { |
|
||||||
return autoChangeLine(text, font, style, unitWidth, resolution); |
|
||||||
} |
|
||||||
|
|
||||||
protected double calculateShowWidth(double paintWidth, Style style, int resolution) { |
|
||||||
return paintWidth - style.getPaddingLeft() - style.getPaddingRight() - style.getBorderLeftWidth(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,69 +0,0 @@ |
|||||||
package com.fr.design.fit.common; |
|
||||||
|
|
||||||
import com.fr.base.BaseUtils; |
|
||||||
import com.fr.base.DefaultRotationTextDrawProvider; |
|
||||||
import com.fr.base.GraphHelper; |
|
||||||
import com.fr.base.Style; |
|
||||||
import com.fr.design.mainframe.PX; |
|
||||||
import com.fr.stable.Constants; |
|
||||||
|
|
||||||
import java.awt.Font; |
|
||||||
import java.awt.FontMetrics; |
|
||||||
import java.awt.Graphics2D; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
public class NewUIModeRotationDraw extends DefaultRotationTextDrawProvider { |
|
||||||
@Override |
|
||||||
public void drawRotationText(Graphics2D g2d, String text, Style style, Font rfont, int width, int height, int horizontalAlignment, int resolution) { |
|
||||||
FontMetrics cellFM = GraphHelper.getFontMetrics(rfont); |
|
||||||
List lineTextList = BaseUtils.getLineTextList(text, style, rfont, height, width, resolution, new NewUIModeAutoChangeLine()); |
|
||||||
drawRotationText(g2d, lineTextList, style, cellFM, width, height, horizontalAlignment, resolution); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
protected int calculateTextWidth(int width, Style style) { |
|
||||||
return width - style.getPaddingRight(); |
|
||||||
} |
|
||||||
|
|
||||||
protected double calculateTextX(Style style, int width, int textWidth, int horizontalAlignment, int resolution) { |
|
||||||
double textX = padding2PixExcludeRight(style.getPaddingLeft(), resolution); |
|
||||||
if (horizontalAlignment == Constants.CENTER) { |
|
||||||
textX += (width - textWidth - textX) / 2f; |
|
||||||
} else if (horizontalAlignment == Constants.RIGHT) { |
|
||||||
textX = width - style.getPaddingRight() - textWidth; |
|
||||||
} |
|
||||||
return textX; |
|
||||||
} |
|
||||||
|
|
||||||
protected int toPXWithResolution(double pt, int resolution) { |
|
||||||
return (int) PX.toPixWithResolution(pt, resolution); |
|
||||||
} |
|
||||||
|
|
||||||
protected double padding2PixExcludeRight(int padding, int resolution) { |
|
||||||
return PX.toPixWithResolution(padding, resolution); |
|
||||||
} |
|
||||||
|
|
||||||
protected int calculateTextY(Style style, int height, int textHeight, int textAscent, List lineTextList, int resolution) { |
|
||||||
// 计算Y的高度.
|
|
||||||
int textY = 0; |
|
||||||
int textAllHeight = textHeight * lineTextList.size(); |
|
||||||
double spacingBefore = toPXWithResolution(style.getSpacingBefore(), resolution); |
|
||||||
double spacingAfter = toPXWithResolution(style.getSpacingAfter(), resolution); |
|
||||||
double lineSpacing = toPXWithResolution(style.getLineSpacing(), resolution); |
|
||||||
textAllHeight += spacingBefore + spacingAfter + lineSpacing * lineTextList.size(); |
|
||||||
if (style.getVerticalAlignment() == Constants.TOP) { |
|
||||||
} else if (style.getVerticalAlignment() == Constants.CENTER) { |
|
||||||
if (height > textAllHeight) {// 如果所有文本的高度小于当前可以绘区域的高度,就从0开始画字符.
|
|
||||||
textY = (height - textAllHeight) / 2; |
|
||||||
} |
|
||||||
} else if (style.getVerticalAlignment() == Constants.BOTTOM) { |
|
||||||
if (height > textAllHeight) { |
|
||||||
textY = height - textAllHeight; |
|
||||||
} |
|
||||||
} |
|
||||||
textY += textAscent;// 在绘画的时候,必须添加Ascent的高度.
|
|
||||||
textY += spacingBefore + lineSpacing;//james:加上"段前间距"+“行间距”
|
|
||||||
return textY; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|