* commit '26e452fafdd582b9e5e4d5a6461a1a384c38aae1': (23 commits) REPORT-72828 启动页配置项屏蔽 REPORT-76291 【迭代】【数据连接为空】次管查看无权限的数据集,没有提示 【问题原因】获取数据连接名称的方式错了,误写成了获取数据集名称,因此判断成了有权限 【改动思路】改用正确方式获取数据连接的名称 【review建议】无 REPORT-75919 【迭代】更新日志-交互问题 1、修改交互 REPORT-76174 【迭代】【数据连接面板为空】数据表搜索框-交互问题 【问题原因】1. 开发的时候没做这个TextField鼠标悬浮变色的功能;2. 部分代码质量问题 【改动思路】1. 加上悬浮变色边框的逻辑;2. 代码质量问题修改 【review建议】无 REPORT-76076 插件管理-插件管理机制优化-内置插件多时,消息提示显示异常 【问题原因】设计器通知中心的消息弹窗,弹窗的宽度是由消息内容长度决定的,所以插件过多时,会把弹窗拉得很长 【改动思路】跟产品沟通后,添加分行逻辑 【review建议】无 REPORT-75919 【迭代】更新日志-交互问题 1、修改交互 REPORT-72635 - 数据连接面板为空的问题(含控制权限) 【问题原因】迭代任务,问题与改动思路详见https://kms.fineres.com/pages/viewpage.action?pageId=443822299 【改动思路】同上 【review建议】无 REPORT-75786 11.0feature版本设计器无法启动 REPORT-75786 11.0feature版本设计器无法启动 DesignUtils.initLookAndFeel 需要先解密。 会用到 GeneralUtils 的逻辑 REPORT-72828 设计器启动优化之起始页设计 1、修复远程目录的展示问题 2、UI效果,距离右边 6 px 3、参数设置放到 UI 线程中 REPORT-70746 更新日志的显示逻辑优化 REPORT-70746 更新日志的显示逻辑优化 REPORT-70746 更新日志的显示逻辑优化 REPORT-70537:数据连接优化埋点 补充注释 REPORT-72828 设计器启动优化之起始页设计 1-添加 loading 页面 2-手动调用部分功能 3-预热功能和在预热中对部分功能的忽视 4-启动页相关的UI 5-预加载服务 REPORT-72354 导出图片优化二期 REPORT-72384 && REPORT-72443 1、下拉树优化V2 2、控件性能优化内置V2 KERNEL-11406 FRM和FVS的新表格支持后台调整行高列宽 REPORT-74411 插件-插件管理机制优化-当本地不存在内置插件的时候,平台还是会收到内置通知 【问题原因】没有对消息做判空 【改动思路】1.添加判空,当不存在内置插件时,不推送消息通知;2.调一下提示语的间隔 【review建议】无 ...newui
@ -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,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: 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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,24 @@
|
||||
package com.fr.start.module.optimized; |
||||
|
||||
import com.fr.module.Activator; |
||||
import com.fr.module.ModuleContext; |
||||
import com.fr.plugin.PluginActivator; |
||||
import com.fr.start.common.DesignerStartupExecutor; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/06/22 |
||||
**/ |
||||
public class DesignerPluginActivator extends Activator { |
||||
|
||||
@Override |
||||
public void start() { |
||||
|
||||
DesignerStartupExecutor.getInstance().execute(() -> ModuleContext.getModule(PluginActivator.class).start()); |
||||
} |
||||
|
||||
@Override |
||||
public void stop() { |
||||
|
||||
ModuleContext.getModule(PluginActivator.class).stop(); |
||||
} |
||||
} |
@ -0,0 +1,119 @@
|
||||
package com.fr.start.module.optimized; |
||||
|
||||
import com.fr.design.ui.util.UIUtil; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.module.Activator; |
||||
import com.fr.start.SplashContext; |
||||
import com.fr.start.common.DesignerStartupContext; |
||||
import com.fr.start.module.StartupArgs; |
||||
import com.fr.start.util.DesignerStartupPageUtil; |
||||
import com.fr.start.warmup.DesignerPreWarmTask; |
||||
import com.fr.startup.ui.StartupPageModel; |
||||
import com.fr.startup.ui.StartupPageWindow; |
||||
import com.fr.third.org.apache.commons.lang3.time.StopWatch; |
||||
import com.fr.value.NotNullLazyValue; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* 设计器起始页启动器 |
||||
* 见 <a href="https://kms.fineres.com/pages/viewpage.action?pageId=416850313">设计文档</a> |
||||
* |
||||
* created by Harrison on 2022/07/03 |
||||
**/ |
||||
public class DesignerStartupPageActivator extends Activator { |
||||
|
||||
private final NotNullLazyValue<StartupArgs> startupArgsValue = new NotNullLazyValue<StartupArgs>() { |
||||
|
||||
@NotNull |
||||
@Override |
||||
protected StartupArgs compute() { |
||||
return findSingleton(StartupArgs.class); |
||||
} |
||||
}; |
||||
|
||||
@Override |
||||
public void start() { |
||||
|
||||
DesignerStartupContext context = DesignerStartupContext.getInstance(); |
||||
context.setStartupArgs(startupArgsValue.getValue()); |
||||
|
||||
if (context.isShowStartupPage()) { |
||||
showDesignerStartupPage(context); |
||||
} else { |
||||
DesignerStartupPageUtil.enterWorkspace(); |
||||
} |
||||
} |
||||
|
||||
private void showDesignerStartupPage(DesignerStartupContext context) { |
||||
|
||||
// 启动页关闭
|
||||
SplashContext.getInstance().hide(); |
||||
|
||||
// 预热任务启动
|
||||
DesignerPreWarmTask warmTask = new DesignerPreWarmTask(); |
||||
warmTask.start(); |
||||
|
||||
// 即时暂停
|
||||
DesignerStartupContext.getRecorder().suspend(); |
||||
|
||||
UIUtil.invokeLaterIfNeeded(() -> { |
||||
|
||||
StartupPageModel model = StartupPageModel.create(); |
||||
context.setStartupPageModel(model); |
||||
|
||||
// selectAndOpenLast
|
||||
model.setOpenLastTemplateRunnable(() -> { |
||||
context.setOpenLastFile(true); |
||||
launchAfterWarmup(warmTask); |
||||
}); |
||||
|
||||
// selectAndOpenEmpty
|
||||
model.setOpenEmptyTemplateRunnable(() -> { |
||||
context.setOpenEmpty(true); |
||||
launchAfterWarmup(warmTask); |
||||
}); |
||||
|
||||
// selectAndCreateNew
|
||||
model.setCreateNewTemplateRunnable(() -> { |
||||
context.setCreateNew(true); |
||||
launchAfterWarmup(warmTask); |
||||
}); |
||||
|
||||
StartupPageWindow window = new StartupPageWindow(model); |
||||
window.setVisible(true); |
||||
context.setOnWaiting(true); |
||||
}); |
||||
} |
||||
|
||||
private void launchAfterWarmup(DesignerPreWarmTask warmTask) { |
||||
|
||||
StopWatch stopWatch = StopWatch.createStarted(); |
||||
|
||||
try { |
||||
DesignerStartupContext.getRecorder().resume(); |
||||
|
||||
// 等待中切换
|
||||
DesignerStartupContext.getInstance().setOnWaiting(false); |
||||
|
||||
warmTask.join(); |
||||
|
||||
FineLoggerFactory.getLogger().debug("designer-startup-page warm up cost {} ms", stopWatch.getTime(TimeUnit.MILLISECONDS)); |
||||
DesignerStartupContext.getInstance().setOnStartup(true); |
||||
DesignerStartupPageUtil.enterWorkspace(); |
||||
} finally { |
||||
UIUtil.invokeLaterIfNeeded(() -> { |
||||
// 换到 awt 线程中关闭,不然异步会出现问题。
|
||||
DesignerStartupContext.getInstance().setOnStartup(false); |
||||
}); |
||||
} |
||||
|
||||
FineLoggerFactory.getLogger().debug("designer-startup-page started cost {} ms", DesignerStartupContext.getRecorder().getTime(TimeUnit.MILLISECONDS)); |
||||
} |
||||
|
||||
@Override |
||||
public void stop() { |
||||
|
||||
} |
||||
} |