vito
11 months ago
20 changed files with 732 additions and 662 deletions
@ -0,0 +1,407 @@
|
||||
package com.fine.theme.light.ui; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.fine.theme.utils.FineClientProperties; |
||||
import com.fine.theme.utils.FineUIUtils; |
||||
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||
import com.fr.base.GraphHelper; |
||||
import com.fr.base.vcs.DesignerMode; |
||||
import com.fr.design.file.MultiTemplateTabPane; |
||||
import com.fr.stable.collections.combination.Pair; |
||||
|
||||
import javax.swing.Icon; |
||||
import javax.swing.JComponent; |
||||
import javax.swing.plaf.ComponentUI; |
||||
import javax.swing.plaf.PanelUI; |
||||
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.Insets; |
||||
import java.awt.geom.Path2D; |
||||
import java.awt.geom.Point2D; |
||||
import java.awt.geom.Rectangle2D; |
||||
|
||||
import static com.fine.theme.utils.FineUIScale.scale; |
||||
import static com.fine.theme.utils.FineUIUtils.paintRoundTabBorder; |
||||
import static com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; |
||||
|
||||
/** |
||||
* 文件Tab栏UI |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/27 |
||||
*/ |
||||
public class FineTemplateTabPaneUI extends PanelUI { |
||||
private MultiTemplateTabPane tabPane; |
||||
|
||||
private static final String ELLIPSIS = "..."; |
||||
private static final int iconTextGap = 4; |
||||
private static final int LEADING_WIDTH = 0; |
||||
private static final int TRAILING_WIDTH = 34; |
||||
|
||||
|
||||
@Styleable(dot = true) |
||||
protected Color background; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color selectedBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color hoverColor; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color closeIconHoverBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color closeHoverBackground; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Color borderColor; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int tabHeight; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int separatorHeight; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int borderWidth; |
||||
|
||||
@Styleable(dot = true) |
||||
protected int tabArc; |
||||
|
||||
@Styleable(dot = true) |
||||
protected Insets tabInsets; |
||||
|
||||
protected Icon fileIcon; |
||||
protected Icon moreAction; |
||||
protected Icon addAction; |
||||
protected Icon moreHoverAction; |
||||
|
||||
|
||||
private Icon closeIcon; |
||||
|
||||
private Icon closeHoverIcon; |
||||
|
||||
private int leadingWidth; |
||||
private int trailingWidth; |
||||
|
||||
protected FineTemplateTabPaneUI() { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 创建UI |
||||
* |
||||
* @param c 组件 |
||||
* @return ComponentUI |
||||
*/ |
||||
public static ComponentUI createUI(JComponent c) { |
||||
return new FineTemplateTabPaneUI(); |
||||
} |
||||
|
||||
@Override |
||||
public void installUI(JComponent c) { |
||||
super.installUI(c); |
||||
this.tabPane = (MultiTemplateTabPane) c; |
||||
closeIcon = new LazyIcon("clear"); |
||||
closeHoverIcon = new LazyIcon("clear_hover"); |
||||
addAction = new LazyIcon("add_worksheet"); |
||||
moreAction = new LazyIcon("tool_more"); |
||||
moreHoverAction = new LazyIcon("clear_hover"); |
||||
fileIcon = new LazyIcon("cpt_icon"); |
||||
FineClientProperties.setStyle(tabPane, "Default"); |
||||
leadingWidth = scale(LEADING_WIDTH); |
||||
trailingWidth = scale(TRAILING_WIDTH); |
||||
|
||||
borderWidth = FineUIUtils.getUIInt("TemplateTabPane.borderWidth", "TemplateTabPane.borderWidth"); |
||||
tabArc = FineUIUtils.getUIInt("TemplateTabPane.tabArc", "TemplateTabPane.tabArc"); |
||||
background = FineUIUtils.getUIColor("TemplateTabPane.background", "TabbedPane.background"); |
||||
selectedBackground = FineUIUtils.getUIColor("TemplateTabPane.selectedBackground", "TemplateTabPane.selectedBackground"); |
||||
closeHoverBackground = FineUIUtils.getUIColor("TemplateTabPane.closeHoverBackground", "TemplateTabPane.closeHoverBackground"); |
||||
borderColor = FineUIUtils.getUIColor("TemplateTabPane.borderColor", "TabbedPane.tabSeparatorColor"); |
||||
hoverColor = FineUIUtils.getUIColor("TemplateTabPane.hoverColor", "TemplateTabPane.hoverColor"); |
||||
closeIconHoverBackground = FineUIUtils.getUIColor("TemplateTabPane.icon.hoverBackground ", "TemplateTabPane.icon.hoverBackground "); |
||||
// ---- scaled ----
|
||||
tabInsets = FineUIUtils.getAndScaleUIInsets("TemplateTabPane.tabInsets", new Insets(4, 6, 4, 6)); |
||||
tabHeight = FineUIUtils.getAndScaleInt("TemplateTabPane.tabHeight", "TabbedPane.tabHeight"); |
||||
separatorHeight = FineUIUtils.getAndScaleInt("TemplateTabPane.separatorHeight", "TemplateTabPane.separatorHeight"); |
||||
} |
||||
|
||||
@Override |
||||
public void uninstallUI(JComponent c) { |
||||
super.uninstallUI(c); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void update(Graphics g, JComponent c) { |
||||
super.update(g, c); |
||||
double maxWidth = c.getWidth() - leadingWidth - trailingWidth; |
||||
Graphics2D g2d = (Graphics2D) g; |
||||
paintDefaultBackground(g2d); |
||||
paintPaneUnderLine(c.getWidth(), g2d); |
||||
paintTabs(g2d, maxWidth); |
||||
} |
||||
|
||||
private void paintDefaultBackground(Graphics2D g2d) { |
||||
//画默认背景
|
||||
g2d.setPaint(new GradientPaint(0, 0, tabPane.getBackground(), 1, (float) (tabHeight), tabPane.getBackground())); |
||||
g2d.fillRect(0, 0, tabPane.getWidth(), tabHeight); |
||||
} |
||||
|
||||
private void paintPaneUnderLine(float w, Graphics2D g2d) { |
||||
g2d.setPaint(borderColor); |
||||
float h = (float) tabHeight; |
||||
int t = scale(borderWidth); |
||||
Path2D border = new Path2D.Float(Path2D.WIND_EVEN_ODD); |
||||
border.append(FlatUIUtils.createComponentRectangle(0, 0, w, h, 0), false); |
||||
border.append(FlatUIUtils.createComponentRectangle(0, 0, w, h - t, 0), false); |
||||
g2d.fill(border); |
||||
} |
||||
|
||||
private void paintTabs(Graphics2D g2d, double maxWidth) { |
||||
|
||||
int maxStringlength = calculateStringMaxLength(); |
||||
if (tabPane.getSelectedIndex() >= tabPane.getTabCount()) { |
||||
tabPane.setSelectedIndex(tabPane.getTabCount() - 1); |
||||
} |
||||
if (tabPane.getSelectedIndex() < 0) { |
||||
tabPane.setSelectedIndex(0); |
||||
} |
||||
double templateStartX = leadingWidth; |
||||
|
||||
|
||||
//从可以开始展示在tab面板上的tab开始画
|
||||
Pair<Integer, Integer> viewRange = tabPane.getViewRange(); |
||||
for (int i = viewRange.getFirst(); i <= viewRange.getSecond(); i++) { |
||||
Icon icon = tabPane.getTemplateIconByIndex(i); |
||||
String name = tabPane.getTemplateShowNameByIndex(i); |
||||
//如果tab名字的长度大于最大能显示的英文字符长度,则进行省略号处理
|
||||
if (getStringWidth(name) > maxStringlength) { |
||||
name = getEllipsisName(name, maxStringlength); |
||||
} |
||||
|
||||
|
||||
Icon tabcloseIcon = tabPane.isCloseCurrent(i) ? closeHoverIcon : closeIcon; |
||||
if (i == tabPane.getSelectedIndex()) { |
||||
paintSelectedTab(g2d, icon, templateStartX, name, tabcloseIcon); |
||||
} else { |
||||
paintUnSelectedTab(g2d, icon, templateStartX, name, tabcloseIcon, |
||||
tabPane.getHoverIndex(), i); |
||||
} |
||||
templateStartX += tabPane.getTabWidth(); |
||||
} |
||||
|
||||
paintSeparators(g2d); |
||||
|
||||
if (!DesignerMode.isVcsMode()) { |
||||
paintTrailingAction(g2d, maxWidth); |
||||
} |
||||
} |
||||
|
||||
private void paintSeparators(Graphics2D g2d) { |
||||
g2d.setPaint(borderColor); |
||||
float x = leadingWidth; |
||||
Pair<Integer, Integer> viewRange = tabPane.getViewRange(); |
||||
for (int i = viewRange.getFirst(); i <= viewRange.getSecond(); i++) { |
||||
if (i != tabPane.getSelectedIndex() |
||||
&& i + 1 != tabPane.getSelectedIndex()) { |
||||
paintSeparator(g2d, x); |
||||
} |
||||
x += tabPane.getTabWidth(); |
||||
} |
||||
} |
||||
|
||||
private void paintLeadingAction(Graphics2D g2d, double tabPaneWidth) { |
||||
int x = (leadingWidth - addAction.getIconWidth()) / 2; |
||||
int y = (tabHeight - addAction.getIconHeight()) / 2; |
||||
if (tabPane.isHoverMoreAction()) { |
||||
closeHoverIcon.paintIcon(tabPane, g2d, x, y); |
||||
} else { |
||||
addAction.paintIcon(tabPane, g2d, x, y); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
private void paintTrailingAction(Graphics2D g2d, double tabPaneWidth) { |
||||
int x = leadingWidth + (int) tabPaneWidth + (trailingWidth - moreAction.getIconWidth()) / 2; |
||||
int y = (tabHeight - moreAction.getIconHeight()) / 2; |
||||
if (tabPane.isHoverMoreAction()) { |
||||
closeHoverIcon.paintIcon(tabPane, g2d, x, y); |
||||
} else { |
||||
moreAction.paintIcon(tabPane, g2d, x, y); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 判断tab文字的长度大于能装下的最大文字长度,要用省略号 |
||||
* |
||||
* @param name |
||||
* @param maxStringlength |
||||
* @return |
||||
*/ |
||||
private String getEllipsisName(String name, int maxStringlength) { |
||||
|
||||
int ellipsisWidth = getStringWidth(ELLIPSIS); |
||||
int leftkeyPoint = 0; |
||||
int rightKeyPoint = name.length() - 1; |
||||
int leftStrWidth = 0; |
||||
int rightStrWidth = 0; |
||||
while (leftStrWidth + rightStrWidth + ellipsisWidth < maxStringlength) { |
||||
if (leftStrWidth <= rightStrWidth) { |
||||
leftkeyPoint++; |
||||
} else { |
||||
rightKeyPoint--; |
||||
} |
||||
leftStrWidth = getStringWidth(name.substring(0, leftkeyPoint)); |
||||
rightStrWidth = getStringWidth(name.substring(rightKeyPoint)); |
||||
|
||||
if (leftStrWidth + rightStrWidth + ellipsisWidth > maxStringlength) { |
||||
if (leftStrWidth <= rightStrWidth) { |
||||
rightKeyPoint++; |
||||
} else { |
||||
leftkeyPoint--; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
return name.substring(0, leftkeyPoint) + ELLIPSIS + name.substring(rightKeyPoint); |
||||
} |
||||
|
||||
/** |
||||
* 计算过长度之后的每个tab的能接受的文字的英文字符数 |
||||
* |
||||
* @return |
||||
*/ |
||||
private int calculateStringMaxLength() { |
||||
return tabPane.getTabWidth() |
||||
- tabInsets.left - tabInsets.right |
||||
- iconTextGap * 2 |
||||
- fileIcon.getIconWidth() - closeIcon.getIconWidth(); |
||||
|
||||
} |
||||
|
||||
private int getStringWidth(String str) { |
||||
FontMetrics fm = GraphHelper.getFontMetrics(tabPane.getFont()); |
||||
int size = 0; |
||||
for (int i = 0; i < str.length(); i++) { |
||||
size += fm.charWidth(str.codePointAt(i)); |
||||
} |
||||
return size; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 画选中的tab |
||||
* |
||||
* @param g2d |
||||
* @param sheeticon |
||||
* @param templateStartX |
||||
* @param sheetName |
||||
* @param closeIcon |
||||
* @return |
||||
*/ |
||||
private void paintSelectedTab(Graphics2D g2d, Icon sheeticon, double templateStartX, String sheetName, Icon closeIcon) { |
||||
Object[] oriRenderingHints = FlatUIUtils.setRenderingHints(g2d); |
||||
// 绘制选中背景
|
||||
g2d.setPaint(selectedBackground); |
||||
Path2D tabShape = FineUIUtils.createTopRoundRectangle(templateStartX, 0, |
||||
tabPane.getTabWidth(), tabHeight, tabArc); |
||||
g2d.fill(tabShape); |
||||
// 绘制选中边框
|
||||
g2d.setPaint(borderColor); |
||||
paintRoundTabBorder(g2d, templateStartX, 0, |
||||
tabPane.getTabWidth(), tabHeight, borderWidth, (float) tabArc); |
||||
FlatUIUtils.resetRenderingHints(g2d, oriRenderingHints); |
||||
// 绘制图标
|
||||
int sheetIconY = (tabHeight - sheeticon.getIconHeight()) / 2; |
||||
sheeticon.paintIcon(tabPane, g2d, (int) templateStartX + tabInsets.left, sheetIconY); |
||||
// 绘制字符
|
||||
g2d.setPaint(tabPane.getForeground()); |
||||
Point2D.Double textPoint = calTextPoint(templateStartX, sheeticon.getIconWidth()); |
||||
g2d.drawString(sheetName, (int) textPoint.x, (int) textPoint.y); |
||||
int closePosition = (int) templateStartX + tabPane.getTabWidth() |
||||
- this.closeIcon.getIconWidth() - tabInsets.right; |
||||
int closeY = (tabHeight - closeIcon.getIconHeight()) / 2; |
||||
if (!DesignerMode.isVcsMode()) { |
||||
closeIcon.paintIcon(tabPane, g2d, closePosition, closeY); |
||||
} |
||||
} |
||||
|
||||
private Point2D.Double calTextPoint(double x, int iconWidth) { |
||||
FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont()); |
||||
int ascent = fm.getAscent(); |
||||
int gap = (tabHeight - tabInsets.top - tabInsets.bottom - ascent) / 2; |
||||
double y = tabInsets.top + ascent + gap; |
||||
return new Point2D.Double(x + iconWidth + tabInsets.left + iconTextGap, y); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 画没有选中的tab |
||||
* |
||||
* @param g2d |
||||
* @param sheeticon |
||||
* @param templateStartX |
||||
* @param sheetName |
||||
* @param closeIcon |
||||
*/ |
||||
private void paintUnSelectedTab(Graphics2D g2d, Icon sheeticon, double templateStartX, String sheetName, Icon closeIcon, int mouseOveredIndex, int selfIndex) { |
||||
if (selfIndex == mouseOveredIndex) { |
||||
g2d.setPaint(hoverColor); |
||||
} else { |
||||
g2d.setPaint(background); |
||||
} |
||||
|
||||
Object[] oriRenderingHints = FlatUIUtils.setRenderingHints(g2d); |
||||
|
||||
Path2D tabShape = FineUIUtils.createTopRoundRectangle(templateStartX, 0, |
||||
tabPane.getTabWidth(), tabHeight - scale(borderWidth), tabArc); |
||||
g2d.fill(tabShape); |
||||
|
||||
|
||||
FlatUIUtils.resetRenderingHints(g2d, oriRenderingHints); |
||||
|
||||
int sheetIconY = (tabHeight - sheeticon.getIconHeight()) / 2; |
||||
sheeticon.paintIcon(tabPane, g2d, (int) templateStartX + tabInsets.left, sheetIconY); |
||||
// 画字符
|
||||
g2d.setPaint(tabPane.getForeground()); |
||||
Point2D.Double textPoint = calTextPoint(templateStartX, sheeticon.getIconWidth()); |
||||
g2d.drawString(sheetName, (int) textPoint.x, (int) textPoint.y); |
||||
int closeY = (tabHeight - closeIcon.getIconHeight()) / 2; |
||||
int closePosition = (int) templateStartX + tabPane.getTabWidth() |
||||
- this.closeIcon.getIconWidth() - tabInsets.right; |
||||
if (!DesignerMode.isVcsMode()) { |
||||
closeIcon.paintIcon(tabPane, g2d, closePosition, closeY); |
||||
} |
||||
} |
||||
|
||||
private void paintSeparator(Graphics2D g2d, float templateStartX) { |
||||
float x = templateStartX + tabPane.getTabWidth(); |
||||
float gap = (tabHeight - separatorHeight) / 2.0f; |
||||
g2d.fill(new Rectangle2D.Float(x, gap, scale(borderWidth), tabHeight - gap * 2)); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Dimension getPreferredSize(JComponent c) { |
||||
return new Dimension(c.getWidth(), tabHeight); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMinimumSize(JComponent c) { |
||||
return new Dimension(0, tabHeight); |
||||
} |
||||
|
||||
@Override |
||||
public Dimension getMaximumSize(JComponent c) { |
||||
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); |
||||
} |
||||
} |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 828 B |
After Width: | Height: | Size: 829 B |
@ -0,0 +1,42 @@
|
||||
package com.fr.design.gui.storybook.components; |
||||
|
||||
import com.fine.theme.icon.LazyIcon; |
||||
import com.fr.design.mainframe.JNullTemplate; |
||||
import com.fr.file.FILE; |
||||
import com.fr.file.MemFILE; |
||||
|
||||
import javax.swing.Icon; |
||||
|
||||
/** |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/12/18 |
||||
*/ |
||||
public class JTestTemplate extends JNullTemplate { |
||||
|
||||
private String name; |
||||
private FILE file; |
||||
|
||||
public JTestTemplate(String name) { |
||||
this.name = name; |
||||
this.file = new MemFILE(name); |
||||
} |
||||
|
||||
public String getTemplateName() { |
||||
return name; |
||||
} |
||||
|
||||
@Override |
||||
public Icon getIcon() { |
||||
return new LazyIcon("save"); |
||||
} |
||||
|
||||
public String getTemplateTabOperatorType(){ |
||||
return "DefaultTabOperator"; |
||||
} |
||||
|
||||
@Override |
||||
public FILE getEditingFILE() { |
||||
return file; |
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
package com.fr.design.gui.storybook.components; |
||||
|
||||
import com.fr.design.file.HistoryTemplateListCache; |
||||
import com.fr.design.file.MultiTemplateTabPane; |
||||
import com.fr.design.gui.storybook.Story; |
||||
import com.fr.design.gui.storybook.StoryBoard; |
||||
import com.fr.value.NullableLazyValue; |
||||
|
||||
/** |
||||
* 新建模版Tab |
||||
* |
||||
* @author vito |
||||
* @since 11.0 |
||||
* Created on 2023/11/27 |
||||
*/ |
||||
@Story |
||||
public class TemplateTabStoryBoard extends StoryBoard { |
||||
|
||||
final static NullableLazyValue<Void> init = NullableLazyValue.createValue(() -> { |
||||
HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(new JTestTemplate("ghjffdhsakjfjdks.cpt")); |
||||
HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(new JTestTemplate("模版1.cpt")); |
||||
HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(new JTestTemplate("模版.cpt")); |
||||
HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(new JTestTemplate("模版1.cpt")); |
||||
HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(new JTestTemplate("模版模版模版模版.cpt")); |
||||
return null; |
||||
}); |
||||
|
||||
public TemplateTabStoryBoard() { |
||||
super("新建模版Tab"); |
||||
|
||||
init.getValue(); |
||||
add(MultiTemplateTabPane.getInstance()); |
||||
} |
||||
|
||||
|
||||
} |
Loading…
Reference in new issue