@ -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,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,104 @@
|
||||
package com.fr.startup.ui; |
||||
|
||||
import com.fr.design.DesignerEnvManager; |
||||
import com.fr.design.env.DesignerWorkspaceInfo; |
||||
import com.fr.third.guava.collect.Lists; |
||||
|
||||
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); |
||||
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, 25)); |
||||
|
||||
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,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); |
||||
} |
||||
}); |
||||
} |
||||
} |
@ -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,116 @@
|
||||
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 { |
||||
DesignerStartupContext.getInstance().setOnStartup(false); |
||||
} |
||||
|
||||
FineLoggerFactory.getLogger().debug("designer-startup-page started cost {} ms", stopWatch.getTime(TimeUnit.MILLISECONDS)); |
||||
} |
||||
|
||||
@Override |
||||
public void stop() { |
||||
|
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
package com.fr.start.preload; |
||||
|
||||
import com.fr.start.common.DesignerStartupPool; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.concurrent.CompletableFuture; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/06/01 |
||||
**/ |
||||
public class PreLoadService { |
||||
|
||||
private List<CompletableFuture<?>> futures = new ArrayList<>(); |
||||
|
||||
public static PreLoadService getInstance() { |
||||
return PreLoadServiceHolder.INSTANCE; |
||||
} |
||||
|
||||
private static class PreLoadServiceHolder { |
||||
private static final PreLoadService INSTANCE = new PreLoadService(); |
||||
} |
||||
|
||||
public void addFuture(CompletableFuture<?> future) { |
||||
futures.add(future); |
||||
} |
||||
|
||||
public void addRunnable(Runnable runnable) { |
||||
futures.add(CompletableFuture.runAsync(runnable, DesignerStartupPool.common())); |
||||
} |
||||
|
||||
public void waitForAll() { |
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,31 @@
|
||||
package com.fr.start.util; |
||||
|
||||
import com.fr.module.ModuleContext; |
||||
import com.fr.start.module.DesignerWorkspaceActivator; |
||||
|
||||
/** |
||||
* created by Harrison on 2022/07/11 |
||||
**/ |
||||
public class DesignerStartupPageUtil { |
||||
|
||||
/** |
||||
* 进入工作目录 |
||||
*/ |
||||
public static void enterWorkspace() { |
||||
|
||||
ModuleContext |
||||
.getModule(DesignerWorkspaceActivator.class) |
||||
.start(); |
||||
} |
||||
|
||||
/** |
||||
* 退出工作目录 |
||||
*/ |
||||
public static void exitWorkspace() { |
||||
|
||||
ModuleContext |
||||
.getModule(DesignerWorkspaceActivator.class) |
||||
.stop(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,47 @@
|
||||
package com.fr.start.warmup; |
||||
|
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.start.common.DesignerStartupContext; |
||||
import com.fr.start.util.DesignerStartupPageUtil; |
||||
|
||||
import java.util.concurrent.CompletableFuture; |
||||
|
||||
/** |
||||
* 预热服务 |
||||
* |
||||
* created by Harrison on 2022/07/03 |
||||
**/ |
||||
public class DesignerPreWarmTask { |
||||
|
||||
private CompletableFuture<Void> warmupTask = null; |
||||
|
||||
public void start() { |
||||
|
||||
if (DesignerStartupContext.getInstance().canWarmup()) { |
||||
warmupTask = CompletableFuture.runAsync(() -> { |
||||
try { |
||||
DesignerStartupContext.getInstance().setOnWarmup(true); |
||||
|
||||
// 尝试预热,启动关闭
|
||||
// 这里测试直接启动/关闭,比等待插件启动更快
|
||||
// ps: 这里还有优化空间
|
||||
// 方向一:改成单纯的预热,不启动工程
|
||||
// 方向二:模块启动可以终止
|
||||
DesignerStartupPageUtil.enterWorkspace(); |
||||
DesignerStartupPageUtil.exitWorkspace(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().debug("designer warm up failed", e); |
||||
} finally { |
||||
DesignerStartupContext.getInstance().setOnWarmup(false); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
public void join() { |
||||
|
||||
if (warmupTask != null) { |
||||
warmupTask.join(); |
||||
} |
||||
} |
||||
} |