diff --git a/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java b/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java index f534163b04..28a2d6c939 100644 --- a/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java +++ b/designer-base/src/main/java/com/fr/design/fun/DefaultValueAdjustProvider.java @@ -3,11 +3,15 @@ package com.fr.design.fun; import com.fr.base.Utils; import com.fr.base.chart.BaseChartCollection; import com.fr.chartx.attr.ChartProvider; +import com.fr.design.style.color.FRColorSelectorStyle; import com.fr.general.FRFont; import com.fr.report.cell.CellElement; +import com.fr.stable.collections.combination.Pair; import com.fr.stable.fun.mark.Selectable; +import java.awt.Color; import java.awt.Font; +import java.util.List; /** * 主要用于fvs报表块内元素默认值的调整,以达到所见所得效果,后续fvs内置后删除 @@ -56,10 +60,27 @@ public interface DefaultValueAdjustProvider extends Selectable { /** * 修改设计可用字体默认列表 + * * @return */ default String[] getAvailableFontFamilyNames4Report() { return Utils.getAvailableFontFamilyNames4Report(); } + /** + * 修改配色方案默认值 + * @return + */ + default Pair adjustChartDefaultColorFill() { + + return new Pair<>(new Color[0], new Color[0]); + } + + /** + * 支持插件自定义主题色选择器风格 + * @return 颜色配置list + */ + default List getColorSelector(){ + return FRColorSelectorStyle.COLOR_CONFIG; + } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/predefined/ui/detail/ColorFillStylePane.java b/designer-base/src/main/java/com/fr/design/mainframe/predefined/ui/detail/ColorFillStylePane.java index 823dd6ff3a..32b12f05a2 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/predefined/ui/detail/ColorFillStylePane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/predefined/ui/detail/ColorFillStylePane.java @@ -9,6 +9,7 @@ import com.fr.config.predefined.PredefinedColorStyle; import com.fr.design.beans.BasicBeanPane; import com.fr.design.constants.LayoutConstants; import com.fr.design.event.UIObserverListener; +import com.fr.design.fun.DefaultValueAdjustProvider; import com.fr.design.gui.icombobox.ColorSchemeComboBox; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; @@ -17,7 +18,9 @@ import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.style.background.gradient.FixedGradientBarNoTheme; import com.fr.design.style.color.ColorAdjustPane; +import com.fr.design.utils.DesignUtils; import com.fr.stable.StringUtils; +import com.fr.stable.collections.combination.Pair; import javax.swing.JPanel; import java.util.Arrays; @@ -70,9 +73,20 @@ public class ColorFillStylePane extends BasicBeanPane { changeColorSetPane = new JPanel(cardLayout = new CardLayout()); changeColorSetPane.add(colorGradient = new FixedGradientBarNoTheme(4, 130), "gradient"); - gradientColors = new Color[]{Color.WHITE, FixedGradientBarNoTheme.NEW_CHARACTER}; - changeColorSetPane.add(colorAdjustPane = new ColorAdjustPane(), "acc"); - accColors = ColorAdjustPane.DEFAULT_COLORS; + DefaultValueAdjustProvider adjustProvider = DesignUtils.getValueAdjust(); + if (adjustProvider != null) { + Pair pair = adjustProvider.adjustChartDefaultColorFill(); + Color[] groupColors = pair.getFirst(); + colorAdjustPane = new ColorAdjustPane(groupColors); + gradientColors = pair.getSecond(); + accColors = groupColors; + } else { + colorAdjustPane = new ColorAdjustPane(); + gradientColors = new Color[]{Color.WHITE, FixedGradientBarNoTheme.NEW_CHARACTER}; + accColors = ColorAdjustPane.DEFAULT_COLORS; + } + changeColorSetPane.add(colorAdjustPane , "acc"); + cardLayout.show(changeColorSetPane, "acc"); customPane.add(changeColorSetPane, BorderLayout.CENTER); initListener(); diff --git a/designer-base/src/main/java/com/fr/design/style/color/ColorConfig.java b/designer-base/src/main/java/com/fr/design/style/color/ColorConfig.java new file mode 100644 index 0000000000..3a4c31d190 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/style/color/ColorConfig.java @@ -0,0 +1,32 @@ +package com.fr.design.style.color; + +import java.awt.Color; + +/** + * 颜色选择器中单元颜色具体配置 + */ +public class ColorConfig { + private boolean supportTheme; + private DeriveAlgorithm algorithm; + + private ColorConfig(boolean supportTheme, DeriveAlgorithm algorithm) { + this.supportTheme = supportTheme; + this.algorithm = algorithm; + } + + public static ColorConfig createThemeColorConfig(DeriveAlgorithm deriveAlgorithm) { + return new ColorConfig(true, deriveAlgorithm); + } + + public static ColorConfig createThemeColorConfig(boolean supportTheme, DeriveAlgorithm deriveAlgorithm) { + return new ColorConfig(supportTheme, deriveAlgorithm); + } + + public boolean isSupportTheme() { + return supportTheme; + } + + public Color[] getDeriveColorArr(Color color, int defaultDeriveCount) { + return algorithm.getDeriveColorArr(color, defaultDeriveCount); + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/style/color/DeriveAlgorithm.java b/designer-base/src/main/java/com/fr/design/style/color/DeriveAlgorithm.java new file mode 100644 index 0000000000..25219f5e4f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/style/color/DeriveAlgorithm.java @@ -0,0 +1,28 @@ +package com.fr.design.style.color; + +import com.fr.base.theme.FineColorDeriveState; + +import java.awt.Color; + +public interface DeriveAlgorithm { + /** + * 通用的衍生规则 + */ + DeriveAlgorithm PLAIN_ALGORITHM = new DeriveAlgorithm() { + @Override + public Color[] getDeriveColorArr(Color color, int defaultDeriveCount) { + return FineColorDeriveState.getDeriveColorArr(color, false, defaultDeriveCount); + } + }; + + /** + * 默认的衍生规则,主要针对字体和背景 + */ + DeriveAlgorithm DEFAULT_DERIVE_ALGORITHM = new DeriveAlgorithm() { + public Color[] getDeriveColorArr(Color color, int defaultDeriveCount) { + return FineColorDeriveState.getDeriveColorArr(color, true, defaultDeriveCount); + } + }; + + Color[] getDeriveColorArr(Color color, int defaultDeriveCount); +} diff --git a/designer-base/src/main/java/com/fr/design/style/color/FRColorSelectorStyle.java b/designer-base/src/main/java/com/fr/design/style/color/FRColorSelectorStyle.java new file mode 100644 index 0000000000..e05825f966 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/style/color/FRColorSelectorStyle.java @@ -0,0 +1,22 @@ +package com.fr.design.style.color; + +import java.util.ArrayList; +import java.util.List; + +public class FRColorSelectorStyle { + public static final List COLOR_CONFIG = new ArrayList<>(); + static { + // 8列主题色 + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(DeriveAlgorithm.PLAIN_ALGORITHM)); + // 2列灰度色 + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(false, DeriveAlgorithm.DEFAULT_DERIVE_ALGORITHM)); + COLOR_CONFIG.add(ColorConfig.createThemeColorConfig(false, DeriveAlgorithm.DEFAULT_DERIVE_ALGORITHM)); + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/style/color/NewColorSelectPane.java b/designer-base/src/main/java/com/fr/design/style/color/NewColorSelectPane.java index e9c8b7a7d3..b5f8976abf 100644 --- a/designer-base/src/main/java/com/fr/design/style/color/NewColorSelectPane.java +++ b/designer-base/src/main/java/com/fr/design/style/color/NewColorSelectPane.java @@ -1,13 +1,13 @@ package com.fr.design.style.color; import com.fr.base.FineColor; -import com.fr.base.theme.FineColorDeriveState; import com.fr.base.theme.TemplateTheme; import com.fr.design.DesignerEnvManager; import com.fr.design.border.UIRoundedBorder; import com.fr.design.constants.UIConstants; import com.fr.design.dialog.BasicPane; import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.fun.DefaultValueAdjustProvider; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; @@ -15,6 +15,7 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.theme.dialog.TemplateThemeProfileDialog; +import com.fr.design.utils.DesignUtils; import javax.swing.BorderFactory; import javax.swing.JButton; @@ -60,6 +61,8 @@ public class NewColorSelectPane extends BasicPane implements ColorSelectable { private final JPanel menuColorPane; private ColorCell[][] themeColorCellGrid; + private List colorSelector ; + public static NewColorSelectPane createColorSelectPaneWithTheme(boolean supportTheme) { return new NewColorSelectPane(true, supportTheme); } @@ -156,7 +159,11 @@ public class NewColorSelectPane extends BasicPane implements ColorSelectable { JPanel centerPane = new JPanel(new GridLayout(1, 8, DEFAULT_COLOR_HOR_INTERVAL, 0)); menuColorPane.add(northPane, BorderLayout.NORTH); menuColorPane.add(centerPane, BorderLayout.CENTER); - + this.colorSelector = FRColorSelectorStyle.COLOR_CONFIG; + DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); + if (valueAdjust != null) { + this.colorSelector = valueAdjust.getColorSelector(); + } Color[] colorArray = new Color[]{ // 8列主题色 Color.decode("#FFFFFF"), @@ -177,11 +184,11 @@ public class NewColorSelectPane extends BasicPane implements ColorSelectable { themeColorCellGrid = new ColorCell[colorArray.length][DEFAULT_DERIVE_COUNT]; for (int i = 0; i < colorArray.length; i++) { ColorCell[] colorCellColumn = new ColorCell[DEFAULT_DERIVE_COUNT]; - boolean isDefaultColor = (i == colorArray.length - 1 || i == colorArray.length - 2); + ColorConfig colorConfig = getColorConfig(colorSelector, i); Color color = colorArray[i]; - Color[] deriveColorArr = FineColorDeriveState.getDeriveColorArr(color, isDefaultColor, DEFAULT_DERIVE_COUNT); + Color[] deriveColorArr = colorConfig.getDeriveColorArr(color, DEFAULT_DERIVE_COUNT); for (int j = 0; j < deriveColorArr.length; j++) { - colorCellColumn[j] = createFineColorCell(deriveColorArr[j], isDefaultColor, i, j); + colorCellColumn[j] = createFineColorCell(deriveColorArr[j], !colorConfig.isSupportTheme(), i, j); } themeColorCellGrid[i] = colorCellColumn; } @@ -218,10 +225,10 @@ public class NewColorSelectPane extends BasicPane implements ColorSelectable { if (standardColors == null || standardColors.size() < 8) { return; } - - for (int i = 0; i < themeColorCellGrid.length - 2; i++) { - Color color = standardColors.get(i); - Color[] deriveColorArr = FineColorDeriveState.getDeriveColorArr(color, false, DEFAULT_DERIVE_COUNT); + for (int i = 0; i < standardColors.size(); i++) { + Color standardColor = standardColors.get(i); + ColorConfig colorConfig = getColorConfig(colorSelector, i); + Color[] deriveColorArr = colorConfig.getDeriveColorArr(standardColor, DEFAULT_DERIVE_COUNT); for (int j = 0; j < deriveColorArr.length; j++) { themeColorCellGrid[i][j].setColor(deriveColorArr[j]); } @@ -448,4 +455,11 @@ public class NewColorSelectPane extends BasicPane implements ColorSelectable { } + public ColorConfig getColorConfig(List colorConfigs, int i) { + if (i < 0 || i >= colorConfigs.size()) { + return colorConfigs.get(0); + } + return colorConfigs.get(i); + } + } diff --git a/designer-chart/src/main/java/com/fr/van/chart/config/DefaultStyleHelper4Van.java b/designer-chart/src/main/java/com/fr/van/chart/config/DefaultStyleHelper4Van.java index 60f208f228..54ff15fab8 100644 --- a/designer-chart/src/main/java/com/fr/van/chart/config/DefaultStyleHelper4Van.java +++ b/designer-chart/src/main/java/com/fr/van/chart/config/DefaultStyleHelper4Van.java @@ -56,10 +56,11 @@ public class DefaultStyleHelper4Van { dealChartColor((VanChart) chartProvider); // //主题中没有的 根据主题深浅色自动 的属性 默认自动 // ((VanChart) chart4Update).setAutoThemeCustom(); - DefaultValueAdjustProvider adjustProvider = DesignUtils.getValueAdjust(); - if (adjustProvider != null) { - adjustProvider.adjustChart(chartProvider); - } + } + + DefaultValueAdjustProvider adjustProvider = DesignUtils.getValueAdjust(); + if (adjustProvider != null) { + adjustProvider.adjustChart(chartProvider); } } diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java index 94886f9d46..314851b0e9 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java @@ -17,6 +17,7 @@ import com.fr.design.gui.itextfield.UINumberField; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.guide.FvsGuidePane; import com.fr.design.mainframe.share.ui.base.PopupMenuItem; import com.fr.design.scrollruler.BaseRuler; import com.fr.design.scrollruler.HorizontalRuler; @@ -30,6 +31,7 @@ import com.fr.form.main.mobile.FormMobileAttr; import com.fr.form.ui.container.WBodyLayoutType; import com.fr.form.ui.container.WBorderLayout; import com.fr.form.ui.container.WFitLayout; +import com.fr.general.ComparatorUtils; import com.fr.general.FRScreen; import com.fr.general.IOUtils; import com.fr.stable.AssistUtils; @@ -105,6 +107,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { if (useScrollBar) { this.setLayout(new FormRulerLayout()); designer.setBorder(new LineBorder(new Color(198, 198, 198))); + this.add(FormRulerLayout.FVS_GUIDE, new FvsGuidePane(this)); this.add(FormRulerLayout.CENTER, designer); addFormSize(); this.add(FormRulerLayout.VERTICAL, verScrollBar); @@ -812,6 +815,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { } private class FormRulerLayout extends RulerLayout { + public static final String FVS_GUIDE = "FvsGuide"; private static final int DESIGNER_WIDTH = 960; private static final int DESIGNER_HEIGHT = 540; private static final int TOPGAP = 8; @@ -819,10 +823,20 @@ public class FormArea extends JComponent implements ScrollRulerComponent { private int DESIGNERWIDTH = DESIGNER_WIDTH; private int DESIGNERHEIGHT = DESIGNER_HEIGHT; + private FvsGuidePane fvsGuidePane; + public FormRulerLayout() { super(); } + @Override + public void addLayoutComponent(String name, Component comp) { + super.addLayoutComponent(name, comp); + if (isValid && ComparatorUtils.equals(name, FVS_GUIDE)) { + fvsGuidePane = (FvsGuidePane) comp; + } + } + /** * 表单用的layout,当前不需要标尺 */ @@ -860,6 +874,12 @@ public class FormArea extends JComponent implements ScrollRulerComponent { DESIGNERWIDTH = right - vbarPreferredSize.width; int designerLeft = left + (verScrollBar.getX() - DESIGNERWIDTH) / 2; int designerTop = top + (horScrollBar.getY() - DESIGNERHEIGHT) / 2; + if (fvsGuidePane != null) { + Dimension fvsGuidePreferredSize = fvsGuidePane.getPreferredSize(); + fvsGuidePane.setBounds(left, top, right - BARSIZE - 2, fvsGuidePreferredSize.height); + DESIGNERHEIGHT -= fvsGuidePreferredSize.height; + designerTop += fvsGuidePreferredSize.height; + } rec = new Rectangle(designerLeft, designerTop, DESIGNERWIDTH, DESIGNERHEIGHT); } // designer是整个表单设计界面中的面板部分,目前只放自适应布局和参数界面。 @@ -867,6 +887,36 @@ public class FormArea extends JComponent implements ScrollRulerComponent { } } + @Override + public void removeLayoutComponent(Component comp) { + super.removeLayoutComponent(comp); + if (isValid && ComparatorUtils.equals(comp, fvsGuidePane)) { + fvsGuidePane = null; + } + } + + @Override + public Dimension preferredLayoutSize(Container target) { + synchronized (target.getTreeLock()) { + Dimension dim = super.preferredLayoutSize(target); + if (isValid && fvsGuidePane != null) { + dim.height += fvsGuidePane.getPreferredSize().height; + } + return dim; + } + } + + @Override + public Dimension minimumLayoutSize(Container target) { + synchronized (target.getTreeLock()) { + Dimension dim = super.minimumLayoutSize(target); + if (isValid && fvsGuidePane != null) { + Dimension dim1 = fvsGuidePane.getMinimumSize(); + dim.height += dim1.height; + } + return dim; + } + } } public boolean isFixLayout(){ diff --git a/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java b/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java new file mode 100644 index 0000000000..ee6f0d3dc7 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java @@ -0,0 +1,260 @@ +package com.fr.design.mainframe.guide; + +import com.fr.base.svg.IconUtils; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.ui.util.UIUtil; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTextPane; +import javax.swing.UIManager; +import javax.swing.border.LineBorder; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; +import javax.swing.event.HyperlinkEvent; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +/** + * FRM引导提示使用FVS + * + * @author Zhanying + * @since 11.0 + * Created on 2024/12/20 + */ +public class FvsGuidePane extends JPanel { + private static final String OMIT_TEXT = "..."; + private static final Color BACKGROUND_COLOR = new Color(255, 251, 230); + private static final Color BORDER_COLOR = new Color(255, 229, 143); + private static final Icon TIP_ICON = UIManager.getIcon("OptionPane.circularWarningIcon"); + private static final Icon CLOSE_ICON = IconUtils.readIcon("/com/fr/design/standard/close/close"); + // 引导URL + private static final String GUIDE_URL = "https://help.fanruan.com/finereport/doc-view-4222.html?source=3"; + private static final int MAX_PANE_HEIGHT = 80; + private static final int MAX_CONTENT_HEIGHT = 60; + private static final int LINE_HEIGHT = 20; + // 计算文本框最多能容纳的行数 = 最大高度 / 行高,向下取整 + private static final int MAX_LINES = (int) Math.floor((double) MAX_CONTENT_HEIGHT / LINE_HEIGHT); + private static final String CONTENT_FORMAT = "\n\n %s\n\n"; + private final JComponent parent; + private JTextPane textPane; + + public FvsGuidePane(JComponent parent) { + super(); + this.parent = parent; + initUI(); + } + + private void initUI() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setLayout(new BorderLayout()); + this.setBorder(new LineBorder(BORDER_COLOR)); + this.setBackground(BACKGROUND_COLOR); + this.setMaximumSize(new Dimension(Integer.MAX_VALUE, MAX_PANE_HEIGHT)); + JPanel wrapPane = new JPanel(new BorderLayout(10, 0)); + wrapPane.setBorder(BorderFactory.createEmptyBorder(8, 13, 8, 13)); + wrapPane.setBackground(BACKGROUND_COLOR); + wrapPane.add(this.createIconPane(), BorderLayout.WEST); + wrapPane.add(this.createContentPane(), BorderLayout.CENTER); + wrapPane.add(this.createCloseBtnPane(), BorderLayout.EAST); + this.add(wrapPane, BorderLayout.CENTER); + addListener(); + } + + private void addListener() { + this.textPane.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + refreshText(); + } + }); + this.textPane.addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) { + UIUtil.invokeLaterIfNeeded(FvsGuidePane.this::refreshText); + } + + @Override + public void ancestorRemoved(AncestorEvent event) { + + } + + @Override + public void ancestorMoved(AncestorEvent event) { + + } + }); + } + + private JPanel createIconPane() { + // 图标 + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 0)); + panel.setBackground(BACKGROUND_COLOR); + UILabel iconLabel = new UILabel(TIP_ICON); + panel.add(iconLabel, BorderLayout.NORTH); + return panel; + } + + private JPanel createCloseBtnPane() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBackground(BACKGROUND_COLOR); + JButton closeButton = new JButton(this.closeBtnText(), CLOSE_ICON); + closeButton.setOpaque(false); + closeButton.setBorderPainted(false); + closeButton.setContentAreaFilled(false); + closeButton.addActionListener(this::close); + panel.add(closeButton, BorderLayout.NORTH); + return panel; + } + + private JPanel createContentPane() { + JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setBackground(BACKGROUND_COLOR); + textPane = new JTextPane(); + // 设置文本格式为 HTML + textPane.setContentType("text/html"); + textPane.setEditable(false); + textPane.setBackground(BACKGROUND_COLOR); + + // 添加超链接监听器 + textPane.addHyperlinkListener(e -> { + if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) { + // 用户点击了超链接,处理跳转 + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (Exception ex) { + FineLoggerFactory.getLogger().error(ex.getMessage(), ex); + } + } + }); + contentPane.add(textPane, BorderLayout.CENTER); + return contentPane; + } + + private void refreshText() { + int textPaneWidth = textPane.getWidth(); + if (textPaneWidth <= 0) { + return; + } + // 获取可用宽度:减去可能的左右边距 + Insets insets = textPane.getInsets(); + int availableWidth = textPaneWidth - insets.left - insets.right - 15;// 留点冗余 + // 组装html + String buildHtml = buildHtml(textPane.getFont(), autoChangeLineAndOmit(availableWidth)); + textPane.setText(buildHtml); + } + + private String tipContent() { + return Toolkit.i18nText("Fine-Design_Form_Guide_Use_Fvs_Tips"); + } + + private String linkContent() { + return Toolkit.i18nText("Fine-Design_Form_Guide_Use_Fvs_Link_Tips"); + } + + private void close(ActionEvent e) { + parent.remove(this); + parent.revalidate(); + parent.repaint(); + } + + private String closeBtnText() { + return Toolkit.i18nText("Fine-Design_Form_Guide_Use_Fvs_Close_Tips"); + } + + private String buildHtml(Font font, String text) { + return String.format(CONTENT_FORMAT, font.getFamily(), font.getSize(), text); + } + + private String autoChangeLineAndOmit(double width) { + StringBuilder htmlBuilder = new StringBuilder(); + // 获取字体的大小 + FontMetrics fontMetrics = textPane.getFontMetrics(textPane.getFont()); + // 计算行间距 + int lineSpace = LINE_HEIGHT - fontMetrics.getHeight(); + String divStart = String.format("
", lineSpace / 2, lineSpace / 2); + htmlBuilder.append(divStart); + + // 计算省略号的长度 + int omitLength = getStringWidth(OMIT_TEXT, fontMetrics); + + String linkContent = linkContent(); + // 计算超链的长度 + int linkLength = getStringWidth(linkContent, fontMetrics); + + String tipContent = tipContent(); + + // 宽度已经小于省略号+超链的宽度 + boolean widthTooSmall = width < omitLength + linkLength; + if (StringUtils.isNotEmpty(tipContent)) { + char[] chars = tipContent.toCharArray(); + + int row = 1; + int off = 0; + for (int i = 0; i < chars.length; i++) { + // 当前行占的宽度 + int currRowWidth = fontMetrics.charsWidth(chars, off, i - off); + // 到最后一行 + if (row == MAX_LINES) { + // 计算省略号的位置 + if (currRowWidth + omitLength + linkLength > width) { + //拼上省略号 + htmlBuilder.append(OMIT_TEXT); + break; + } + } + + if (currRowWidth > width) { + // 换行 + htmlBuilder.append("
").append(divStart); + off = i; + row++; + } else if (widthTooSmall && simulateIfLastLineOverflows(fontMetrics, currRowWidth, row, (OMIT_TEXT + linkContent).toCharArray(), width)) { + //模拟到最后一行是否溢出;其实这么窄展示也没什么意义了 + //拼上省略号 + htmlBuilder.append(OMIT_TEXT); + break; + } + htmlBuilder.append(chars[i]); + } + } + htmlBuilder.append("").append(linkContent).append(""); + return htmlBuilder.toString(); + } + + private boolean simulateIfLastLineOverflows(FontMetrics fontMetrics, int currRowWidth, int row, char[] omitAndLink, double width) { + int off = 0; + for (int i = 0; i < omitAndLink.length; i++) { + boolean lastRow = row == MAX_LINES; + if (currRowWidth + fontMetrics.charsWidth(omitAndLink, off, i - off) > (lastRow ? width - 30 : width)) { // 最后一行留点冗余 + if (lastRow) { + return true; + } + off = i; + currRowWidth = 0; + row++; + } + } + return false; + } + + private int getStringWidth(String str, FontMetrics fontMetrics) { + return fontMetrics.charsWidth(str.toCharArray(), 0, str.length()); + } +}