Browse Source
* commit 'edc79325bef83cbefa1d6aa037d5bd943f727d20': (323 commits) REPORT-61276 决策报表-最新的release包,打开决策报表会打开一个空cpt;新建frm正常 REPORT-61205 标准色视觉修改 REPORT-61275 部分任务默认工具栏展开 REPORT-61249 视觉图变动 无JIRA任务 换一下svg图片,原来的svg图片有点变形 REPORT-61237【固定布局-原布局推荐4.1】决策报表-自适应布局-新建空白模板,切换到固定布局,此时无法往设计画布里拖入组件 REPORT-61055 【主题获取】主题导出插件几个小问题 REPORT-60991 导出参数设置面板添加无响应 REPORT-52578 10.0兼容新自适应插件的同步代码在11.0中需要改成异步的 REPORT-61105 & REPORT-61096 & REPORT-61012 控件联动及兼容老模板调整 REPORT-61177 解决引导页弹窗展示变成半透明的问题 REPORT-61207 行间距减小,放在window上文字截断 REPORT-61219 【主题获取】右键有主题的组件,鼠标从获取主题滑到详细说明,两个都被选中了 REPORT-60942 加载图视觉更新 REPORT-61125 交互优化更新 REPORT-60993 导出-导出事件-事件内容没有初始化 REPORT-58796 REPORT-60982 主题获取-点击获取主题后网络较慢时过渡不合理 REPORT-58796 REPORT-60942 引导页视觉更新 ...new-design
404 changed files with 18151 additions and 3610 deletions
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.design.beans; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
|
||||||
|
public interface ErrorMsgTextFieldAdapter { |
||||||
|
void setText(String str); |
||||||
|
|
||||||
|
String getText(); |
||||||
|
|
||||||
|
JComponent getErrorMsgTextField(); |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.fr.design.beans; |
||||||
|
|
||||||
|
import com.fr.design.gui.itextfield.UITextField; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.event.DocumentEvent; |
||||||
|
import javax.swing.event.DocumentListener; |
||||||
|
|
||||||
|
public class UITextFieldAdapter implements ErrorMsgTextFieldAdapter { |
||||||
|
private final UITextField uiTextField = new UITextField(); |
||||||
|
|
||||||
|
public UITextFieldAdapter(){ |
||||||
|
addDocumentListener(); |
||||||
|
} |
||||||
|
@Override |
||||||
|
public void setText(String str) { |
||||||
|
uiTextField.setText(str); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getText() { |
||||||
|
return uiTextField.getText(); |
||||||
|
} |
||||||
|
|
||||||
|
public void addDocumentListener() { |
||||||
|
uiTextField.getDocument().addDocumentListener(new DocumentListener() { |
||||||
|
|
||||||
|
public void changedUpdate(DocumentEvent e) { |
||||||
|
uiTextField.setToolTipText(uiTextField.getText()); |
||||||
|
} |
||||||
|
|
||||||
|
public void insertUpdate(DocumentEvent e) { |
||||||
|
uiTextField.setToolTipText(uiTextField.getText()); |
||||||
|
} |
||||||
|
|
||||||
|
public void removeUpdate(DocumentEvent e) { |
||||||
|
uiTextField.setToolTipText(uiTextField.getText()); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JComponent getErrorMsgTextField() { |
||||||
|
return uiTextField; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.design.border; |
||||||
|
|
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.border.TitledBorder; |
||||||
|
import java.awt.Color; |
||||||
|
|
||||||
|
public class UITitledMatteBorder extends TitledBorder { |
||||||
|
public static UITitledMatteBorder createTitledTopBorder(String title, Color color) { |
||||||
|
return new UITitledMatteBorder(title, 1, 0, 0, 0, color); |
||||||
|
} |
||||||
|
|
||||||
|
public static UITitledMatteBorder createTitledBorder(String title, Color color) { |
||||||
|
return new UITitledMatteBorder(title, 1, 1, 1, 1, color); |
||||||
|
} |
||||||
|
|
||||||
|
public static UITitledMatteBorder createTitledBorder(String title, int top, int left, int bottom, int right, Color color) { |
||||||
|
return new UITitledMatteBorder(title, top, left, bottom, right, color); |
||||||
|
} |
||||||
|
|
||||||
|
private UITitledMatteBorder(String title, int top, int left, int bottom, int right, Color color) { |
||||||
|
super( |
||||||
|
BorderFactory.createMatteBorder(top, left, bottom, right, UIConstants.TITLED_BORDER_COLOR), |
||||||
|
title, |
||||||
|
TitledBorder.LEADING, |
||||||
|
TitledBorder.TOP, |
||||||
|
null, |
||||||
|
color |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.fr.design.data; |
||||||
|
|
||||||
|
public class NameChangeBean { |
||||||
|
private String oldName; |
||||||
|
private String changedName; |
||||||
|
|
||||||
|
public NameChangeBean(String oldName, String changedName) { |
||||||
|
this.oldName = oldName; |
||||||
|
this.changedName = changedName; |
||||||
|
} |
||||||
|
|
||||||
|
public String getOldName() { |
||||||
|
return oldName; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOldName(String oldName) { |
||||||
|
this.oldName = oldName; |
||||||
|
} |
||||||
|
|
||||||
|
public String getChangedName() { |
||||||
|
return changedName; |
||||||
|
} |
||||||
|
|
||||||
|
public void setChangedName(String changedName) { |
||||||
|
this.changedName = changedName; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.fr.design.data.datapane.connect; |
||||||
|
|
||||||
|
import com.fr.design.dialog.BasicDialog; |
||||||
|
import com.fr.design.dialog.DialogActionAdapter; |
||||||
|
import com.fr.design.editlock.EditLockUtils; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.file.ConnectionConfig; |
||||||
|
import com.fr.report.LockItem; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2021/9/8 |
||||||
|
*/ |
||||||
|
public class ConnectionListDialogActionAdapter extends DialogActionAdapter { |
||||||
|
|
||||||
|
private final ConnectionManagerPane connectionManagerPane; |
||||||
|
private final BasicDialog connectionListDialog; |
||||||
|
private final ConnectionConfig connectionConfig; |
||||||
|
|
||||||
|
public ConnectionListDialogActionAdapter(ConnectionManagerPane connectionManagerPane, |
||||||
|
BasicDialog connectionListDialog, |
||||||
|
ConnectionConfig connectionConfig) { |
||||||
|
this.connectionManagerPane = connectionManagerPane; |
||||||
|
this.connectionListDialog = connectionListDialog; |
||||||
|
this.connectionConfig = connectionConfig; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void doOk() { |
||||||
|
if (!connectionManagerPane.isNamePermitted()) { |
||||||
|
connectionListDialog.setDoOKSucceed(false); |
||||||
|
return; |
||||||
|
} |
||||||
|
connectionManagerPane.update(connectionConfig); |
||||||
|
DesignerContext.getDesignerBean("databasename").refreshBeanElement(); |
||||||
|
// 关闭定义数据连接页面,为其解锁
|
||||||
|
EditLockUtils.unlock(LockItem.CONNECTION); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void doCancel() { |
||||||
|
// 关闭定义数据连接页面,为其解锁
|
||||||
|
super.doCancel(); |
||||||
|
EditLockUtils.unlock(LockItem.CONNECTION); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
package com.fr.design.formula; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.parser.FRLexer; |
||||||
|
import com.fr.parser.FRParser; |
||||||
|
import com.fr.script.checker.FunctionCheckerDispatcher; |
||||||
|
import com.fr.script.checker.exception.ConditionCheckWrongException; |
||||||
|
import com.fr.script.checker.exception.FunctionCheckWrongException; |
||||||
|
import com.fr.script.rules.FunctionParameterType; |
||||||
|
import com.fr.script.rules.FunctionRule; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.script.Expression; |
||||||
|
import com.fr.stable.script.Node; |
||||||
|
|
||||||
|
import java.io.StringReader; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Hoky |
||||||
|
* @date 2021/9/28 |
||||||
|
*/ |
||||||
|
public class FormulaChecker { |
||||||
|
private static final String VALID_FORMULA = Toolkit.i18nText("Fine-Design_Basic_FormulaD_Valid_Formula"); |
||||||
|
private static final String INVALID_FORMULA = Toolkit.i18nText("Fine-Design_Basic_FormulaD_Invalid_Formula"); |
||||||
|
public static final String COLON = ":"; |
||||||
|
|
||||||
|
public static String check(String formulaText) throws FormulaCheckerException { |
||||||
|
StringReader in = new StringReader(formulaText); |
||||||
|
|
||||||
|
FRLexer lexer = new FRLexer(in); |
||||||
|
FRParser parser = new FRParser(lexer); |
||||||
|
|
||||||
|
try { |
||||||
|
Expression expression = parser.parse(); |
||||||
|
Node node = expression.getConditionalExpression(); |
||||||
|
boolean valid = FunctionCheckerDispatcher.getInstance().getFunctionChecker(node).checkFunction(node); |
||||||
|
if (valid) { |
||||||
|
return VALID_FORMULA; |
||||||
|
} else { |
||||||
|
throw new FormulaCheckerException(INVALID_FORMULA); |
||||||
|
} |
||||||
|
} catch (ConditionCheckWrongException cce) { |
||||||
|
String functionName = cce.getFunctionName(); |
||||||
|
throw new FormulaCheckerException(functionName + Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Condition_Tips") + COLON); |
||||||
|
} catch (FunctionCheckWrongException ce) { |
||||||
|
List<FunctionRule> rules = ce.getRules(); |
||||||
|
String functionName = ce.getFunctionName(); |
||||||
|
StringBuilder errorMsg = new StringBuilder(functionName + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Error_Tips") + COLON); |
||||||
|
for (int i = 0; i < rules.size(); i++) { |
||||||
|
errorMsg.append("("); |
||||||
|
if (rules.get(i).getParameterList().isEmpty()) { |
||||||
|
errorMsg.append(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_No_Param")); |
||||||
|
} |
||||||
|
for (FunctionParameterType functionParameterType : rules.get(i).getParameterList()) { |
||||||
|
errorMsg.append(getTypeString(functionParameterType)).append(","); |
||||||
|
} |
||||||
|
if (",".equals(errorMsg.charAt(errorMsg.length() - 1) + "")) { |
||||||
|
errorMsg.deleteCharAt(errorMsg.length() - 1); |
||||||
|
} |
||||||
|
errorMsg.append(")"); |
||||||
|
if (i != rules.size() - 1) { |
||||||
|
errorMsg.append(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_Or")); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new FormulaCheckerException(errorMsg.toString()); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
throw new FormulaCheckerException(INVALID_FORMULA); |
||||||
|
// alex:继续往下面走,expression为null时告知不合法公式
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static String getTypeString(FunctionParameterType type) { |
||||||
|
switch (type) { |
||||||
|
case NUMBER: |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Number"); |
||||||
|
case STRING: |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_String"); |
||||||
|
case ANY: |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Any"); |
||||||
|
case DATE: |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Date"); |
||||||
|
case BOOLEAN: |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Boolean"); |
||||||
|
case ARRAY: |
||||||
|
return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Formula_Check_ParamType_Array"); |
||||||
|
} |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.design.formula; |
||||||
|
|
||||||
|
public class FormulaCheckerException extends Exception { |
||||||
|
public FormulaCheckerException() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public FormulaCheckerException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.design.fun; |
||||||
|
|
||||||
|
import com.fr.design.gui.frpane.RegFieldPane; |
||||||
|
import com.fr.stable.fun.mark.Immutable; |
||||||
|
|
||||||
|
public interface RegPaneProvider extends Immutable { |
||||||
|
int CURRENT_LEVEL = 1; |
||||||
|
String XML_TAG = "RegPaneProvider"; |
||||||
|
|
||||||
|
RegFieldPane createRegPane(); |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.design.fun; |
||||||
|
|
||||||
|
import com.fr.design.beans.ErrorMsgTextFieldAdapter; |
||||||
|
import com.fr.stable.fun.mark.Immutable; |
||||||
|
|
||||||
|
public interface TextFieldAdapterProvider extends Immutable { |
||||||
|
String XML_TAG = "TextFieldAdapterProvider"; |
||||||
|
int CURRENT_LEVEL = 1; |
||||||
|
|
||||||
|
ErrorMsgTextFieldAdapter createTextFieldAdapter(); |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.fr.design.fun.impl; |
||||||
|
|
||||||
|
import com.fr.design.fun.RegPaneProvider; |
||||||
|
import com.fr.stable.fun.mark.API; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Joe |
||||||
|
* 2021/10/8 15:19 |
||||||
|
*/ |
||||||
|
@API(level = RegPaneProvider.CURRENT_LEVEL) |
||||||
|
public abstract class AbstractRegPaneProvider implements RegPaneProvider { |
||||||
|
|
||||||
|
@Override |
||||||
|
public int currentAPILevel() { |
||||||
|
return CURRENT_LEVEL; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int layerIndex() { |
||||||
|
return DEFAULT_LAYER_INDEX; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.fr.design.fun.impl; |
||||||
|
|
||||||
|
import com.fr.design.fun.TextFieldAdapterProvider; |
||||||
|
import com.fr.stable.fun.mark.API; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Joe |
||||||
|
* 2021/10/8 15:17 |
||||||
|
*/ |
||||||
|
@API(level = TextFieldAdapterProvider.CURRENT_LEVEL) |
||||||
|
public abstract class AbstractTextFieldAdapterProvider implements TextFieldAdapterProvider { |
||||||
|
|
||||||
|
@Override |
||||||
|
public int currentAPILevel() { |
||||||
|
return CURRENT_LEVEL; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int layerIndex() { |
||||||
|
return DEFAULT_LAYER_INDEX; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package com.fr.design.gui.frpane; |
||||||
|
|
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Container; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2021/9/17 |
||||||
|
*/ |
||||||
|
public class AttributeChangeUtils { |
||||||
|
private static AbstractAttrNoScrollPane findNearestAttrNoScrollPaneAncestor(Component c) { |
||||||
|
for(Container p = c.getParent(); p != null; p = p.getParent()) { |
||||||
|
if (p instanceof AbstractAttrNoScrollPane) { |
||||||
|
return (AbstractAttrNoScrollPane) p; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public static void changeComposedUI(Component composedComponent, boolean fireMiddleStateChanged, UIChangeAction action) { |
||||||
|
AbstractAttrNoScrollPane attrPane = findNearestAttrNoScrollPaneAncestor(composedComponent); |
||||||
|
boolean oldAutoFire = true; |
||||||
|
|
||||||
|
if (!fireMiddleStateChanged) { |
||||||
|
// 禁止属性面板自动处理属性更新
|
||||||
|
if (attrPane != null) { |
||||||
|
oldAutoFire = attrPane.isAutoFireAttributesChanged(); |
||||||
|
attrPane.setAutoFireAttributesChanged(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
action.changeComposedUI(); |
||||||
|
|
||||||
|
if (!fireMiddleStateChanged) { |
||||||
|
// 恢复属性面板自动处理属性更新
|
||||||
|
if (attrPane != null) { |
||||||
|
attrPane.setAutoFireAttributesChanged(oldAutoFire); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public interface UIChangeAction { |
||||||
|
void changeComposedUI(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
package com.fr.design.gui.style; |
||||||
|
|
||||||
|
import com.fr.base.Style; |
||||||
|
import com.fr.design.constants.LayoutConstants; |
||||||
|
import com.fr.design.designer.IntervalConstants; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.layout.TableLayout; |
||||||
|
import com.fr.design.layout.TableLayoutHelper; |
||||||
|
import com.fr.design.mainframe.theme.edit.ui.LabelUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JTextArea; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2021/9/29 |
||||||
|
*/ |
||||||
|
public class TextFontTippedPane extends AbstractBasicStylePane { |
||||||
|
|
||||||
|
private FRFontPane fontPane; |
||||||
|
|
||||||
|
public TextFontTippedPane(boolean showFormatTip) { |
||||||
|
this.initializePane(showFormatTip); |
||||||
|
} |
||||||
|
|
||||||
|
private void initializePane(boolean showFormatTip) { |
||||||
|
setLayout(new BorderLayout()); |
||||||
|
setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, 0, 0)); |
||||||
|
|
||||||
|
fontPane = new FRFontPane(); |
||||||
|
this.add(createLabeledPane(Toolkit.i18nText("Fine-Design_Form_FR_Font"), fontPane), BorderLayout.NORTH); |
||||||
|
|
||||||
|
if (showFormatTip) { |
||||||
|
JPanel formatTipPane = createFormatTipPane(); |
||||||
|
this.add(formatTipPane, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createLabeledPane(String text, JPanel panel) { |
||||||
|
double f = TableLayout.FILL; |
||||||
|
double p = TableLayout.PREFERRED; |
||||||
|
double[] rowSize = { p }; |
||||||
|
double[] columnSize = {p, f}; |
||||||
|
|
||||||
|
UILabel uiLabel = new UILabel(text); |
||||||
|
JPanel uiLabelPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
uiLabelPane.add(uiLabel, BorderLayout.NORTH); |
||||||
|
|
||||||
|
return TableLayoutHelper.createGapTableLayoutPane(new Component[][]{ |
||||||
|
new Component[] { uiLabelPane, panel }, |
||||||
|
}, rowSize, columnSize, LayoutConstants.VGAP_LARGE, LayoutConstants.VGAP_MEDIUM); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createFormatTipPane() { |
||||||
|
JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
container.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, 0, 0)); |
||||||
|
JTextArea formatMigratedTip = LabelUtils.createAutoWrapLabel(Toolkit.i18nText("Fine-Design_Report_Format_Style_Migrated_Tip"), new Color(153, 153, 153)); |
||||||
|
|
||||||
|
container.add(formatMigratedTip, BorderLayout.NORTH); |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String title4PopupWindow() { |
||||||
|
return Toolkit.i18nText("Fine-Design_Report_Text"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void populateBean(Style style) { |
||||||
|
this.fontPane.populateBean(style); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Style update(Style style) { |
||||||
|
return this.fontPane.update(style); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,483 @@ |
|||||||
|
package com.fr.design.gui.style; |
||||||
|
|
||||||
|
import com.fr.base.CoreDecimalFormat; |
||||||
|
import com.fr.base.GraphHelper; |
||||||
|
import com.fr.base.Style; |
||||||
|
import com.fr.base.TextFormat; |
||||||
|
import com.fr.data.core.FormatField; |
||||||
|
import com.fr.data.core.FormatField.FormatContents; |
||||||
|
import com.fr.design.border.UIRoundedBorder; |
||||||
|
import com.fr.design.constants.LayoutConstants; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.event.GlobalNameListener; |
||||||
|
import com.fr.design.event.GlobalNameObserver; |
||||||
|
import com.fr.design.event.UIObserverListener; |
||||||
|
import com.fr.design.gui.icheckbox.UICheckBox; |
||||||
|
import com.fr.design.gui.icombobox.TextFontComboBox; |
||||||
|
import com.fr.design.gui.icombobox.UIComboBox; |
||||||
|
import com.fr.design.gui.icombobox.UIComboBoxRenderer; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.TableLayout; |
||||||
|
import com.fr.design.layout.TableLayoutHelper; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JLabel; |
||||||
|
import javax.swing.JList; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import javax.swing.UIManager; |
||||||
|
import javax.swing.border.Border; |
||||||
|
import javax.swing.border.TitledBorder; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FontMetrics; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.event.ItemEvent; |
||||||
|
import java.awt.event.ItemListener; |
||||||
|
import java.math.RoundingMode; |
||||||
|
import java.text.Format; |
||||||
|
import java.text.SimpleDateFormat; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2021/9/29 |
||||||
|
* 包含格式相关的设置 |
||||||
|
*/ |
||||||
|
public class TextFormatPane extends AbstractBasicStylePane implements GlobalNameObserver { |
||||||
|
private static final long serialVersionUID = 724330854437726751L; |
||||||
|
|
||||||
|
private static final int LABEL_X = 4; |
||||||
|
private static final int LABEL_Y = 18; |
||||||
|
private static final int LABEL_DELTA_WIDTH = 8; |
||||||
|
private static final int LABEL_HEIGHT = 15; //标签背景的范围
|
||||||
|
private static final int CURRENCY_FLAG_POINT = 6; |
||||||
|
|
||||||
|
private static final Integer[] TYPES = new Integer[]{ |
||||||
|
FormatContents.NULL, FormatContents.NUMBER, |
||||||
|
FormatContents.CURRENCY, FormatContents.PERCENT, |
||||||
|
FormatContents.SCIENTIFIC, FormatContents.DATE, |
||||||
|
FormatContents.TIME, FormatContents.TEXT}; |
||||||
|
|
||||||
|
private static final Integer[] DATE_TYPES = new Integer[]{FormatContents.NULL, FormatContents.DATE, FormatContents.TIME}; |
||||||
|
|
||||||
|
private Format format; |
||||||
|
|
||||||
|
private UIComboBox typeComboBox; |
||||||
|
private TextFontComboBox textField; |
||||||
|
private UILabel sampleLabel; |
||||||
|
private JPanel contentPane; |
||||||
|
private JPanel txtCenterPane; |
||||||
|
private JPanel centerPane; |
||||||
|
private JPanel optionPane; |
||||||
|
private UICheckBox roundingBox; |
||||||
|
private JPanel formatFontPane; |
||||||
|
private boolean isRightFormat; |
||||||
|
private boolean isDate = false; |
||||||
|
private GlobalNameListener globalNameListener = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public TextFormatPane() { |
||||||
|
this.initComponents(TYPES); |
||||||
|
} |
||||||
|
|
||||||
|
protected UIComboBox getTypeComboBox() { |
||||||
|
return typeComboBox; |
||||||
|
} |
||||||
|
|
||||||
|
protected void initComponents(Integer[] types) { |
||||||
|
this.setLayout(new BorderLayout(0, 4)); |
||||||
|
initSampleLabel(); |
||||||
|
contentPane = new JPanel(new BorderLayout(0, 4)) { |
||||||
|
@Override |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
return new Dimension(super.getPreferredSize().width, 65); |
||||||
|
} |
||||||
|
}; |
||||||
|
typeComboBox = new UIComboBox(types); |
||||||
|
UIComboBoxRenderer render = createComBoxRender(); |
||||||
|
typeComboBox.setRenderer(render); |
||||||
|
typeComboBox.addItemListener(itemListener); |
||||||
|
typeComboBox.setGlobalName("typeComboBox"); |
||||||
|
contentPane.add(sampleLabel, BorderLayout.NORTH); |
||||||
|
|
||||||
|
txtCenterPane = new JPanel(new BorderLayout()); |
||||||
|
textField = new TextFontComboBox(); |
||||||
|
textField.addItemListener(textFieldItemListener); |
||||||
|
textField.setEditable(true); |
||||||
|
textField.setGlobalName("textField"); |
||||||
|
txtCenterPane.add(textField, BorderLayout.NORTH); |
||||||
|
|
||||||
|
contentPane.add(txtCenterPane, BorderLayout.CENTER); |
||||||
|
|
||||||
|
centerPane = new JPanel(new CardLayout()); |
||||||
|
centerPane.add(new JPanel(), "hide"); |
||||||
|
centerPane.setPreferredSize(new Dimension(0, 0)); |
||||||
|
centerPane.add(contentPane, "show"); |
||||||
|
|
||||||
|
typeComboBox.setPreferredSize(new Dimension(155,20)); |
||||||
|
JPanel typePane = new JPanel(new BorderLayout()); |
||||||
|
typePane.add(typeComboBox, BorderLayout.CENTER); |
||||||
|
typePane.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
||||||
|
|
||||||
|
JPanel option = new JPanel(new BorderLayout()); |
||||||
|
option.add(new UILabel(Toolkit.i18nText("Fine-Design_Report_Base_Option"), SwingConstants.LEFT), BorderLayout.WEST); |
||||||
|
roundingBox = new UICheckBox(Toolkit.i18nText("Fine-Design_Report_Base_Option_Half_Up")); |
||||||
|
roundingBox.setBorder(BorderFactory.createEmptyBorder(0, 30, 0, 0)); |
||||||
|
roundingBox.addItemListener(new ItemListener() { |
||||||
|
@Override |
||||||
|
public void itemStateChanged(ItemEvent e) { |
||||||
|
} |
||||||
|
}); |
||||||
|
roundingBox.setGlobalName("roundingBox"); |
||||||
|
option.add(roundingBox, BorderLayout.CENTER); |
||||||
|
optionPane = new JPanel(new CardLayout()); |
||||||
|
optionPane.add(new JPanel(), "hide"); |
||||||
|
optionPane.setPreferredSize(new Dimension(0, 0)); |
||||||
|
optionPane.add(option, "show"); |
||||||
|
|
||||||
|
Component[][] components = getComponent(centerPane, typePane); |
||||||
|
this.add(createContentPane(components), BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
protected JPanel createContentPane (Component[][] components) { |
||||||
|
double f = TableLayout.FILL; |
||||||
|
double p = TableLayout.PREFERRED; |
||||||
|
double[] rowSize = {p, p, p, p, p}; |
||||||
|
double[] columnSize = {p, f}; |
||||||
|
int[][] rowCount = {{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}; |
||||||
|
return TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, LayoutConstants.VGAP_LARGE, LayoutConstants.VGAP_MEDIUM); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected Component[][] getComponent (JPanel centerPane, JPanel typePane) { |
||||||
|
return new Component[][]{ |
||||||
|
new Component[]{null, null}, |
||||||
|
new Component[]{new UILabel(Toolkit.i18nText("Fine-Design_Report_Base_Format"), SwingConstants.LEFT), typePane}, |
||||||
|
new Component[]{centerPane, null}, |
||||||
|
new Component[]{optionPane, null}, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
protected UIComboBoxRenderer createComBoxRender() { |
||||||
|
return new UIComboBoxRenderer() { |
||||||
|
@Override |
||||||
|
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
||||||
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); |
||||||
|
if (value instanceof Integer) { |
||||||
|
label.setText(" " + FormatField.getInstance().getName((Integer) value)); |
||||||
|
} |
||||||
|
return label; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private void initSampleLabel() { |
||||||
|
Border interBorder = new UIRoundedBorder(UIConstants.LINE_COLOR, 1, 4); |
||||||
|
String title = Toolkit.i18nText("Fine-Design_Report_Base_StyleFormat_Sample"); |
||||||
|
Border border = BorderFactory.createTitledBorder(interBorder, title, TitledBorder.LEFT, 0, null, UIConstants.LINE_COLOR); |
||||||
|
sampleLabel = new UILabel(FormatField.getInstance().getFormatValue()) { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
super.paint(g); |
||||||
|
int width = getWidth(); |
||||||
|
Color original = g.getColor(); |
||||||
|
g.setColor(getBackground()); |
||||||
|
g.fillRect(LABEL_X, LABEL_Y, width - LABEL_DELTA_WIDTH, LABEL_HEIGHT); |
||||||
|
g.setColor(UIConstants.LINE_COLOR); |
||||||
|
FontMetrics cellFM = g.getFontMetrics(); |
||||||
|
int textWidth = cellFM.stringWidth(getText()); |
||||||
|
GraphHelper.drawString(g, getText(), (width - textWidth) / 2, 26); |
||||||
|
g.setColor(original); |
||||||
|
} |
||||||
|
}; |
||||||
|
sampleLabel.setHorizontalAlignment(UILabel.CENTER); |
||||||
|
sampleLabel.setBorder(border); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
/** |
||||||
|
* 得到合适的大小 |
||||||
|
*/ |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
if (this.typeComboBox.getSelectedIndex() == FormatContents.NULL) { |
||||||
|
return typeComboBox.getPreferredSize(); |
||||||
|
} |
||||||
|
return super.getPreferredSize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 弹出框标题 |
||||||
|
* |
||||||
|
* @return 标题 |
||||||
|
*/ |
||||||
|
public String title4PopupWindow() { |
||||||
|
return Toolkit.i18nText("Fine-Design_Report_Text"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Populate |
||||||
|
*/ |
||||||
|
public void populateBean(Format format) { |
||||||
|
this.format = format; |
||||||
|
|
||||||
|
if (format == null) { |
||||||
|
this.typeComboBox.setSelectedIndex(FormatContents.NULL); |
||||||
|
} else { |
||||||
|
if (format instanceof CoreDecimalFormat) { |
||||||
|
// check all value
|
||||||
|
String pattern = ((CoreDecimalFormat) format).toPattern(); |
||||||
|
if (isCurrencyFormatStyle(pattern)) { |
||||||
|
setPatternComboBoxAndList(FormatContents.CURRENCY, pattern); |
||||||
|
} else if (pattern.indexOf("%") > 0) { |
||||||
|
setPatternComboBoxAndList(FormatContents.PERCENT, pattern); |
||||||
|
this.roundingBox.setSelected(((CoreDecimalFormat) format).getRoundingMode().equals(RoundingMode.HALF_UP)); |
||||||
|
} else if (pattern.indexOf("E") > 0) { |
||||||
|
setPatternComboBoxAndList(FormatContents.SCIENTIFIC, pattern); |
||||||
|
} else { |
||||||
|
setPatternComboBoxAndList(FormatContents.NUMBER, pattern); |
||||||
|
} |
||||||
|
} else if (format instanceof SimpleDateFormat) { // date and time
|
||||||
|
String pattern = ((SimpleDateFormat) format).toPattern(); |
||||||
|
if (!isTimeType(pattern)) { |
||||||
|
setPatternComboBoxAndList(FormatContents.DATE, pattern); |
||||||
|
} else { |
||||||
|
setPatternComboBoxAndList(FormatContents.TIME, pattern); |
||||||
|
} |
||||||
|
} else if (format instanceof TextFormat) { // Text
|
||||||
|
this.typeComboBox.setSelectedItem(FormatContents.TEXT); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isCurrencyFormatStyle(String pattern) { |
||||||
|
if (pattern.length() == 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (pattern.charAt(0) == '¤' || pattern.charAt(0) == '$') { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return pattern.length() > CURRENCY_FLAG_POINT && pattern.startsWith("#,##0;"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否是数组有模式 |
||||||
|
* |
||||||
|
* @param stringArray 字符串数组 |
||||||
|
* @param pattern 格式 |
||||||
|
* @return 是否是数组有模式 |
||||||
|
*/ |
||||||
|
public static int isArrayContainPattern(String[] stringArray, String pattern) { |
||||||
|
for (int i = 0; i < stringArray.length; i++) { |
||||||
|
if (ComparatorUtils.equals(stringArray[i], pattern)) { |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
private void setPatternComboBoxAndList(int formatStyle, String pattern) { |
||||||
|
this.typeComboBox.setSelectedItem(formatStyle); |
||||||
|
this.textField.setSelectedItem(pattern); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isTimeType(String pattern) { |
||||||
|
return pattern.matches(".*[Hhmsa].*"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* update |
||||||
|
*/ |
||||||
|
public Format update() { |
||||||
|
String patternString = String.valueOf(textField.getSelectedItem()); |
||||||
|
if (getFormatContents() == FormatContents.TEXT) { |
||||||
|
return FormatField.getInstance().getFormat(getFormatContents(), patternString); |
||||||
|
} |
||||||
|
if (isRightFormat) { |
||||||
|
if (StringUtils.isNotEmpty(patternString)) { |
||||||
|
RoundingMode roundingMode = roundingBox.isSelected() ? RoundingMode.HALF_UP : RoundingMode.HALF_EVEN; |
||||||
|
return FormatField.getInstance().getFormat(getFormatContents(), patternString, roundingMode); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private int getFormatContents() { |
||||||
|
return (Integer) typeComboBox.getSelectedItem(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Refresh preview label. |
||||||
|
*/ |
||||||
|
private void refreshPreviewLabel() { |
||||||
|
this.sampleLabel.setText(FormatField.getInstance().getFormatValue()); |
||||||
|
this.sampleLabel.setForeground(UIManager.getColor("Label.foreground")); |
||||||
|
try { |
||||||
|
isRightFormat = true; |
||||||
|
if (StringUtils.isEmpty(String.valueOf(textField.getSelectedItem()))) { |
||||||
|
return; |
||||||
|
} |
||||||
|
this.sampleLabel.setText(FormatField.getInstance().getFormatValue(getFormatContents(), String.valueOf(textField.getSelectedItem()))); |
||||||
|
} catch (Exception e) { |
||||||
|
this.sampleLabel.setForeground(Color.red); |
||||||
|
this.sampleLabel.setText(e.getMessage()); |
||||||
|
isRightFormat = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isTextOrNull() { |
||||||
|
int contents = getFormatContents(); |
||||||
|
return contents == FormatContents.TEXT || contents == FormatContents.NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Radio selection listener. |
||||||
|
*/ |
||||||
|
ItemListener itemListener = new ItemListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void itemStateChanged(ItemEvent e) { |
||||||
|
if (e.getStateChange() == ItemEvent.SELECTED) { |
||||||
|
int contents = getFormatContents(); |
||||||
|
String[] items = FormatField.getInstance().getFormatArray(contents, false); |
||||||
|
CardLayout cardLayout = (CardLayout) centerPane.getLayout(); |
||||||
|
|
||||||
|
if (isTextOrNull()) { |
||||||
|
centerPane.setPreferredSize(new Dimension(0, 0)); |
||||||
|
cardLayout.show(centerPane, "hide"); |
||||||
|
} else { |
||||||
|
textField.removeAllItems(); |
||||||
|
textField.setItemArray(items); |
||||||
|
textField.setSelectedIndex(0); |
||||||
|
centerPane.setPreferredSize(new Dimension(270, 65)); |
||||||
|
cardLayout.show(centerPane, "show"); |
||||||
|
} |
||||||
|
CardLayout optionLayout = ((CardLayout) optionPane.getLayout()); |
||||||
|
if (getFormatContents() == FormatContents.PERCENT) { |
||||||
|
optionPane.setPreferredSize(new Dimension(100, 20)); |
||||||
|
optionLayout.show(optionPane, "show"); |
||||||
|
} else { |
||||||
|
optionPane.setPreferredSize(new Dimension(0, 0)); |
||||||
|
optionLayout.show(optionPane, "hide"); |
||||||
|
roundingBox.setSelected(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
ItemListener textFieldItemListener = new ItemListener() { |
||||||
|
@Override |
||||||
|
public void itemStateChanged(ItemEvent e) { |
||||||
|
if (e.getStateChange() == ItemEvent.SELECTED) { |
||||||
|
refreshPreviewLabel(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
@Override |
||||||
|
/** |
||||||
|
* populate |
||||||
|
*/ |
||||||
|
public void populateBean(Style style) { |
||||||
|
this.populateBean(style.getFormat()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
/** |
||||||
|
* update |
||||||
|
*/ |
||||||
|
public Style update(Style style) { |
||||||
|
if (ComparatorUtils.equals(globalNameListener.getGlobalName(), "textField") |
||||||
|
|| ComparatorUtils.equals(globalNameListener.getGlobalName(), "typeComboBox") |
||||||
|
|| ComparatorUtils.equals(globalNameListener.getGlobalName(), "roundingBox")) { |
||||||
|
return style.deriveFormat(this.update()); |
||||||
|
} |
||||||
|
return style; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认只显示百分比的编辑下拉. |
||||||
|
*/ |
||||||
|
public void justUsePercentFormat() { |
||||||
|
typeComboBox.setEnabled(false); |
||||||
|
this.typeComboBox.setSelectedItem(FormatContents.PERCENT); |
||||||
|
} |
||||||
|
|
||||||
|
public void setForDataSheet() { |
||||||
|
Integer[] otherTypes = new Integer[]{FormatContents.NULL, FormatContents.NUMBER, FormatContents.CURRENCY, FormatContents.PERCENT, FormatContents.SCIENTIFIC,}; |
||||||
|
this.typeComboBox = new UIComboBox(otherTypes); |
||||||
|
UIComboBoxRenderer render = new UIComboBoxRenderer() { |
||||||
|
@Override |
||||||
|
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
||||||
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); |
||||||
|
if (value instanceof Integer) { |
||||||
|
label.setText(" " + FormatField.getInstance().getName((Integer) value)); |
||||||
|
} |
||||||
|
return label; |
||||||
|
} |
||||||
|
}; |
||||||
|
typeComboBox.setRenderer(render); |
||||||
|
typeComboBox.addItemListener(itemListener); |
||||||
|
setTypeComboBoxPane(typeComboBox); |
||||||
|
} |
||||||
|
|
||||||
|
protected void setTypeComboBoxPane (UIComboBox typeComboBox) { |
||||||
|
this.add(typeComboBox, BorderLayout.NORTH); |
||||||
|
} |
||||||
|
|
||||||
|
public void setComboBoxModel(boolean isDate) { |
||||||
|
if (this.isDate != isDate) { |
||||||
|
this.isDate = isDate; |
||||||
|
this.typeComboBox.setSelectedIndex(0); |
||||||
|
if (isDate) { |
||||||
|
for (int i = 0; i < DATE_TYPES.length; i++) { |
||||||
|
this.typeComboBox.addItem(DATE_TYPES[i]); |
||||||
|
} |
||||||
|
for (int i = 0; i < TYPES.length; i++) { |
||||||
|
this.typeComboBox.removeItemAt(1); |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0; i < TYPES.length; i++) { |
||||||
|
this.typeComboBox.addItem(TYPES[i]); |
||||||
|
} |
||||||
|
for (int i = 0; i < DATE_TYPES.length; i++) { |
||||||
|
this.typeComboBox.removeItemAt(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void registerNameListener(GlobalNameListener listener) { |
||||||
|
globalNameListener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
public void registerChangeListener(UIObserverListener listener) { |
||||||
|
typeComboBox.registerChangeListener(listener); |
||||||
|
textField.registerChangeListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean shouldResponseNameListener() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setGlobalName(String name) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.report.cell.cellattr.core.group.DSColumn; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
public class DSColumnAuthorityChecker extends ElementAuthorityChecker<DSColumn> { |
||||||
|
|
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(DSColumn dsColumn, Set<String> authDatasetNames) { |
||||||
|
if (!authDatasetNames.contains(dsColumn.getDSName())) { |
||||||
|
return new HashSet<>(Arrays.asList(dsColumn.getDSName())); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; |
||||||
|
|
||||||
|
import java.lang.reflect.Type; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
|
||||||
|
public abstract class ElementAuthorityChecker<T> { |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @Description 获取越权的数据连接 |
||||||
|
* @param: t 待检查的对象 |
||||||
|
* @param: authConnectionNames 有权限的数据连接名 |
||||||
|
* @return 如果有返回名称,没有返回null |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthConnectionNames(T t, Set<String> authConnectionNames) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @Description 获取越权的服务器数据集 |
||||||
|
* @param: t 待检查的对象 |
||||||
|
* @param: authDatasetNames 有权限的服务器数据集名 |
||||||
|
* @return 如果有返回名称,没有返回null |
||||||
|
*/ |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(T t, Set<String> authDatasetNames) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description 要检查对象的className |
||||||
|
* @return className |
||||||
|
*/ |
||||||
|
String getCheckClassName() { |
||||||
|
ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) this.getClass().getGenericSuperclass(); |
||||||
|
Type type = parameterizedType.getActualTypeArguments()[0]; |
||||||
|
return type.getTypeName(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.base.Formula; |
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import com.fr.parser.FunctionCall; |
||||||
|
import com.fr.parser.StringLiteral; |
||||||
|
import com.fr.script.Calculator; |
||||||
|
import com.fr.stable.script.Node; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
public class FormulaAuthorityChecker extends ElementAuthorityChecker<Formula> { |
||||||
|
private static final Set<FormulaParser> CONNECTION_NAME_FORMULA_PARSER = new HashSet<>(); |
||||||
|
private static final Set<FormulaParser> DATASET_NAME_FORMULA_PARSER = new HashSet<>(); |
||||||
|
|
||||||
|
static { |
||||||
|
CONNECTION_NAME_FORMULA_PARSER.add(new FormulaParser("SQL", 0)); |
||||||
|
DATASET_NAME_FORMULA_PARSER.add(new FormulaParser("VALUE", 0)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
public Set<String> getNoAuthConnectionNames(Formula formula, Set<String> authConnectionNames) { |
||||||
|
return getNoAuthNames(formula, CONNECTION_NAME_FORMULA_PARSER, authConnectionNames); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(Formula formula, Set<String> authDatasetNames) { |
||||||
|
return getNoAuthNames(formula, DATASET_NAME_FORMULA_PARSER, authDatasetNames); |
||||||
|
} |
||||||
|
|
||||||
|
private Set<String> getNoAuthNames(Formula formula, Set<FormulaParser> formulaParsers, Set<String> authNames) { |
||||||
|
Set<String> noAuthNames = new HashSet<>(); |
||||||
|
try { |
||||||
|
FunctionCall functionCall = (FunctionCall) Calculator.createCalculator().parse(formula.getContent()).getConditionalExpression(); |
||||||
|
handleNoAuthNames(functionCall, formulaParsers, authNames, noAuthNames); |
||||||
|
} catch (Exception ignore) { |
||||||
|
|
||||||
|
} finally { |
||||||
|
return noAuthNames; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void handleNoAuthNames(FunctionCall functionCall, Set<FormulaParser> formulaParsers, Set<String> authNames, Set<String> noAuthNames) { |
||||||
|
for (FormulaParser formulaPattern : formulaParsers) { |
||||||
|
String noAuthName = formulaPattern.getNoAuthName(functionCall, authNames); |
||||||
|
if (noAuthName != null) { |
||||||
|
noAuthNames.add(noAuthName); |
||||||
|
} |
||||||
|
} |
||||||
|
Node[] nodes = functionCall.getArguments(); |
||||||
|
if (nodes != null) { |
||||||
|
for (int i = 0; i < nodes.length; i++) { |
||||||
|
Node node = nodes[i]; |
||||||
|
if (node instanceof FunctionCall) { |
||||||
|
handleNoAuthNames((FunctionCall) node, formulaParsers, authNames, noAuthNames); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static class FormulaParser { |
||||||
|
//函数的名称
|
||||||
|
public String name; |
||||||
|
//要检测的位置
|
||||||
|
public int index; |
||||||
|
|
||||||
|
|
||||||
|
FormulaParser(String name, int index) { |
||||||
|
this.name = name; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
String getNoAuthName(FunctionCall functionCall, Set<String> authNames) { |
||||||
|
if (functionCall.getName() != null && ComparatorUtils.equals(functionCall.getName().toUpperCase(), name)) { |
||||||
|
Node node = functionCall.getArguments()[index]; |
||||||
|
if (node instanceof StringLiteral) { |
||||||
|
String stringLiteral = node.toString(); |
||||||
|
if (stringLiteral.length() > 2) { |
||||||
|
String value = stringLiteral.substring(1, stringLiteral.length() - 1); |
||||||
|
if (!authNames.contains(value)) { |
||||||
|
return value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,188 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.JTemplate; |
||||||
|
import com.fr.design.mod.ModClassFilter; |
||||||
|
import com.fr.file.ConnectionConfig; |
||||||
|
import com.fr.file.TableDataConfig; |
||||||
|
import com.fr.invoke.ClassHelper; |
||||||
|
|
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.Filter; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import com.fr.workspace.server.authority.user.UserAuthority; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
|
||||||
|
import static javax.swing.JOptionPane.WARNING_MESSAGE; |
||||||
|
|
||||||
|
|
||||||
|
public class JTemplateAuthorityChecker { |
||||||
|
JTemplate<?, ?> jTemplate; |
||||||
|
Set<String> allConnectionNames; |
||||||
|
Set<String> authConnectionNames; |
||||||
|
Set<String> allDatasetNames; |
||||||
|
Set<String> authDatasetNames; |
||||||
|
Map<String, ElementAuthorityChecker> checkerMap = new HashMap<>(); |
||||||
|
Set<String> authFailConnectionNames = new HashSet<>(); |
||||||
|
Set<String> authFailDatasetNames = new HashSet<>(); |
||||||
|
|
||||||
|
|
||||||
|
public JTemplateAuthorityChecker(JTemplate<?, ?> jTemplate) { |
||||||
|
long s = System.currentTimeMillis(); |
||||||
|
this.jTemplate = jTemplate; |
||||||
|
this.initAuthNames(); |
||||||
|
this.initChecker(); |
||||||
|
FineLoggerFactory.getLogger().info("JTemplateAuthorityChecker init time consume:" + (System.currentTimeMillis() - s)); |
||||||
|
} |
||||||
|
|
||||||
|
private void initAuthNames() { |
||||||
|
allDatasetNames = new HashSet<>(); |
||||||
|
for (String authServerDataSetName : TableDataConfig.getInstance().getTableDatas().keySet()) { |
||||||
|
allDatasetNames.add(authServerDataSetName); |
||||||
|
} |
||||||
|
|
||||||
|
allConnectionNames = ConnectionConfig.getInstance().getConnections().keySet(); |
||||||
|
|
||||||
|
UserAuthority templateAuthority = WorkContext.getCurrent().get(UserAuthority.class); |
||||||
|
Map<String, Set<String>> authNamesMap = templateAuthority.getAuthServerDataSetAndConnectionNames(); |
||||||
|
if (authNamesMap != null) { |
||||||
|
//有权限的数据连接名称
|
||||||
|
authConnectionNames = authNamesMap.get(UserAuthority.AUTH_CONNECTION_NAMES); |
||||||
|
//有权限的数据集名称
|
||||||
|
authDatasetNames = authNamesMap.get(UserAuthority.AUTH_SERVER_DATASET_NAMES); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initChecker() { |
||||||
|
registerChecker(new NameDatabaseConnectionAuthorityChecker()); |
||||||
|
registerChecker(new DSColumnAuthorityChecker()); |
||||||
|
registerChecker(new FormulaAuthorityChecker()); |
||||||
|
registerChecker(new NameTableDataAuthorityChecker()); |
||||||
|
} |
||||||
|
|
||||||
|
private void registerChecker(ElementAuthorityChecker checker) { |
||||||
|
checkerMap.put(checker.getCheckClassName(), checker); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public boolean isAuthority() { |
||||||
|
long s = System.currentTimeMillis(); |
||||||
|
//遍历模板对象,根据checkerMap.keySet()把感兴趣的对象找出来
|
||||||
|
Map<String, Collection<Object>> targetObjects = ClassHelper.searchObject(jTemplate.getTarget(), checkerMap.keySet(), ClassFilter.getInstance()); |
||||||
|
|
||||||
|
//找到对应的checker,对对象进行检查
|
||||||
|
for (String name : targetObjects.keySet()) { |
||||||
|
ElementAuthorityChecker checker = checkerMap.get(name); |
||||||
|
for (Object object : targetObjects.get(name)) { |
||||||
|
if (authConnectionNames != null) { |
||||||
|
Set<String> noAuthName = checker.getNoAuthConnectionNames(object, authConnectionNames); |
||||||
|
if (noAuthName != null) { |
||||||
|
authFailConnectionNames.addAll(noAuthName); |
||||||
|
} |
||||||
|
} |
||||||
|
if (authDatasetNames != null) { |
||||||
|
Set<String> noAuthName = checker.getNoAuthDatasetNames(object, authDatasetNames); |
||||||
|
if (noAuthName != null) { |
||||||
|
authFailDatasetNames.addAll(noAuthName); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
authFailConnectionNames.retainAll(allConnectionNames); |
||||||
|
authFailDatasetNames.retainAll(allDatasetNames); |
||||||
|
FineLoggerFactory.getLogger().info("JTemplateAuthorityChecker check time consume:" + (System.currentTimeMillis() - s)); |
||||||
|
return authFailConnectionNames.size() == 0 && authFailDatasetNames.size() == 0; |
||||||
|
} |
||||||
|
|
||||||
|
public void showAuthorityFailPromptDialog() { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design-Basic_Save_Failure")); |
||||||
|
stringBuffer.append("\n"); |
||||||
|
stringBuffer.append(getPromptInfo(authFailDatasetNames, |
||||||
|
Toolkit.i18nText("Fine-Design_Template_Authority_Check_Server_Dataset_Authority"))); |
||||||
|
stringBuffer.append(getPromptInfo(authFailConnectionNames, |
||||||
|
Toolkit.i18nText("Fine-Design_Template_Authority_Check_Data_Connection_Authority"))); |
||||||
|
FineJOptionPane.showMessageDialog( |
||||||
|
DesignerContext.getDesignerFrame(), |
||||||
|
stringBuffer.toString(), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Alert"), |
||||||
|
WARNING_MESSAGE); |
||||||
|
} |
||||||
|
|
||||||
|
private String getPromptInfo(Set<String> authFailNames, String message) { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
if (authFailNames.size() > 0) { |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design_Template_Authority_Check_Current_Operator_Miss")); |
||||||
|
stringBuffer.append(authFailNames.size()); |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design_Report_Ge")); |
||||||
|
stringBuffer.append(message); |
||||||
|
stringBuffer.append("\n"); |
||||||
|
stringBuffer.append(getNoAuthNameSequence(authFailNames)); |
||||||
|
} |
||||||
|
return stringBuffer.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private String getNoAuthNameSequence(Set<String> names) { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
int showMaxCount = 3; |
||||||
|
int count = 0; |
||||||
|
for (String name : names) { |
||||||
|
if (count == showMaxCount) { |
||||||
|
stringBuffer.append(Toolkit.i18nText("Fine-Design_Template_Authority_Check_Etc")); |
||||||
|
break; |
||||||
|
} |
||||||
|
stringBuffer.append(name); |
||||||
|
if (count != names.size() - 1 && count != showMaxCount - 1) { |
||||||
|
stringBuffer.append(";"); |
||||||
|
} |
||||||
|
count++; |
||||||
|
} |
||||||
|
stringBuffer.append("\n"); |
||||||
|
return stringBuffer.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
static class ClassFilter implements Filter<String> { |
||||||
|
|
||||||
|
private static final Set<String> FILTER_SET = new HashSet<>(); |
||||||
|
private static final Set<String> START_WITH_SET = new HashSet<>(); |
||||||
|
private static final Filter<String> INSTANCE = new ModClassFilter(); |
||||||
|
|
||||||
|
public static Filter<String> getInstance() { |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
static { |
||||||
|
FILTER_SET.add("java.awt.image.BufferedImage"); |
||||||
|
FILTER_SET.add("sun.awt.AppContext"); |
||||||
|
FILTER_SET.add("com.fr.poly.creator.ECBlockCreator"); |
||||||
|
FILTER_SET.add("io.netty.channel.nio.SelectedSelectionKeySet"); |
||||||
|
FILTER_SET.add("com.fr.form.ui.ElementCaseImage"); |
||||||
|
FILTER_SET.add("this$0"); |
||||||
|
START_WITH_SET.add("com.fr.design"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean accept(String s) { |
||||||
|
if (FILTER_SET.contains(s)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
for (String start : START_WITH_SET) { |
||||||
|
if (s.startsWith(start)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,22 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.data.impl.NameDatabaseConnection; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
public class NameDatabaseConnectionAuthorityChecker extends ElementAuthorityChecker<NameDatabaseConnection> { |
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthConnectionNames(NameDatabaseConnection nameDatabaseConnection, Set<String> authConnectionNames) { |
||||||
|
String name = nameDatabaseConnection.getName(); |
||||||
|
if (!authConnectionNames.contains(name)) { |
||||||
|
return new HashSet<>(Arrays.asList(name)); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.fr.design.mainframe.authority; |
||||||
|
|
||||||
|
import com.fr.data.impl.NameTableData; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
public class NameTableDataAuthorityChecker extends ElementAuthorityChecker<NameTableData> { |
||||||
|
@Override |
||||||
|
@Nullable |
||||||
|
Set<String> getNoAuthDatasetNames(NameTableData nameTableData, Set<String> authDatasetNames) { |
||||||
|
if (!authDatasetNames.contains(nameTableData.getName())) { |
||||||
|
return new HashSet<>(Arrays.asList(nameTableData.getName())); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,189 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.guide.collect.GuideCollector; |
||||||
|
import com.fr.design.mainframe.guide.scene.GuideScene; |
||||||
|
import com.fr.design.mainframe.guide.ui.GuideCompleteDialog; |
||||||
|
import com.fr.design.mainframe.guide.ui.GuideManageDialog; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import javax.swing.SwingWorker; |
||||||
|
|
||||||
|
public class Guide { |
||||||
|
private String id; |
||||||
|
private String name; |
||||||
|
private String description; |
||||||
|
private String completeMessage; |
||||||
|
private GuideView guideView; |
||||||
|
private GuideLifecycle lifecycle; |
||||||
|
private boolean isComplete; |
||||||
|
private GuideScene scene; |
||||||
|
|
||||||
|
public Guide() { |
||||||
|
this(null, null, null); |
||||||
|
} |
||||||
|
|
||||||
|
public Guide(String id, String name, String description) { |
||||||
|
this.id = id; |
||||||
|
this.name = name; |
||||||
|
this.description = description; |
||||||
|
} |
||||||
|
|
||||||
|
public String getId() { |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
public void setId(String id) { |
||||||
|
this.id = id; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDescription(String description) { |
||||||
|
this.description = description; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDescription() { |
||||||
|
if (StringUtils.isNotEmpty(description)) { |
||||||
|
return description; |
||||||
|
} |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getCompleteMessage() { |
||||||
|
return completeMessage; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCompleteMessage(String completeMessage) { |
||||||
|
this.completeMessage = completeMessage; |
||||||
|
} |
||||||
|
|
||||||
|
public void setGuideView(GuideView guideView) { |
||||||
|
this.guideView = guideView; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideView getGuideView() { |
||||||
|
return guideView; |
||||||
|
} |
||||||
|
|
||||||
|
public void setComplete(boolean complete) { |
||||||
|
isComplete = complete; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isComplete() { |
||||||
|
return isComplete; |
||||||
|
} |
||||||
|
|
||||||
|
public void setScene(GuideScene scene) { |
||||||
|
this.scene = scene; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideScene getScene() { |
||||||
|
return scene; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 开启引导流程 |
||||||
|
*/ |
||||||
|
public void go() { |
||||||
|
DesignerContext.getDesignerFrame().setExtendedState(JFrame.MAXIMIZED_BOTH); |
||||||
|
// 同时只能启动一个引导
|
||||||
|
if (GuideManager.getInstance().getCurrentGuide() != null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
GuideManager.getInstance().setCurrentGuide(this); |
||||||
|
guideView.showLoading(); |
||||||
|
|
||||||
|
new SwingWorker<Boolean, Void>() { |
||||||
|
@Override |
||||||
|
protected Boolean doInBackground() { |
||||||
|
try { |
||||||
|
if (lifecycle != null && !lifecycle.prepared()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
guideView.hideLoading(); |
||||||
|
terminate(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void done() { |
||||||
|
try { |
||||||
|
if (get()) { |
||||||
|
start(); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
guideView.hideLoading(); |
||||||
|
terminate(); |
||||||
|
} |
||||||
|
} |
||||||
|
}.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
public void start() { |
||||||
|
guideView.hideLoading(); |
||||||
|
if (scene != null) { |
||||||
|
guideView.setScene(scene); |
||||||
|
guideView.showGuide(); |
||||||
|
if (lifecycle != null) { |
||||||
|
lifecycle.onStart(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
complete(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void complete() { |
||||||
|
if (lifecycle != null) { |
||||||
|
lifecycle.onComplete(); |
||||||
|
} |
||||||
|
setComplete(true); |
||||||
|
GuideCollector.getInstance().saveInfo(); |
||||||
|
guideView.dismissGuide(); |
||||||
|
GuideCompleteDialog.getInstance().showDialog(getCompleteMessage()); |
||||||
|
end(); |
||||||
|
} |
||||||
|
|
||||||
|
public void terminate() { |
||||||
|
if (lifecycle != null) { |
||||||
|
lifecycle.onTerminate(); |
||||||
|
} |
||||||
|
end(); |
||||||
|
} |
||||||
|
|
||||||
|
public void end() { |
||||||
|
guideView.dismissGuide(); |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (lifecycle != null) { |
||||||
|
lifecycle.onEnd(); |
||||||
|
} |
||||||
|
GuideManager.getInstance().setCurrentGuide(null); |
||||||
|
GuideManageDialog.getInstance().showDialog(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void registerLifecycle(GuideLifecycle lifecycle) { |
||||||
|
this.lifecycle = lifecycle; |
||||||
|
} |
||||||
|
|
||||||
|
public void removeGuideLifecycle() { |
||||||
|
this.lifecycle = null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.guide.scene.GuideScene; |
||||||
|
|
||||||
|
public class GuideBuilder { |
||||||
|
private Guide guide; |
||||||
|
|
||||||
|
public static GuideBuilder newInstance() { |
||||||
|
return new GuideBuilder(); |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder() { |
||||||
|
guide = new Guide(); |
||||||
|
guide.setGuideView(new GuideView(DesignerContext.getDesignerFrame(), guide)); |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder setID(String id) { |
||||||
|
guide.setId(id); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder setName(String name) { |
||||||
|
guide.setName(name); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder setDescription(String description) { |
||||||
|
guide.setDescription(description); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder setCompleteMessage(String message) { |
||||||
|
guide.setCompleteMessage(message); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder addScene(GuideScene scene) { |
||||||
|
guide.setScene(scene); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideBuilder registerLifecycle(GuideLifecycle lifecycle) { |
||||||
|
guide.registerLifecycle(lifecycle); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Guide getGuide() { |
||||||
|
return guide; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class GuideGroup { |
||||||
|
private List<Guide> guideList; |
||||||
|
private String id; |
||||||
|
private String name; |
||||||
|
|
||||||
|
public GuideGroup(String id, String name) { |
||||||
|
this.id = id; |
||||||
|
this.name = name; |
||||||
|
guideList = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getId() { |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
public void setId(String id) { |
||||||
|
this.id = id; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void addGuide(Guide guide) { |
||||||
|
guideList.add(guide); |
||||||
|
} |
||||||
|
|
||||||
|
public List<Guide> getGuideList() { |
||||||
|
return guideList; |
||||||
|
} |
||||||
|
|
||||||
|
public List<Guide> getCompleteGuideList() { |
||||||
|
List<Guide> complete = new ArrayList<>(); |
||||||
|
for (Guide guide : getGuideList()) { |
||||||
|
if (guide.isComplete()) { |
||||||
|
complete.add(guide); |
||||||
|
} |
||||||
|
} |
||||||
|
return complete; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isCompleteAll() { |
||||||
|
return getCompleteGuideList().size() == getGuideList().size(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isCompleteSome() { |
||||||
|
List<Guide> completeGuides = getCompleteGuideList(); |
||||||
|
return completeGuides.size() > 0 && completeGuides.size() < getGuideList().size(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
public interface GuideLifecycle{ |
||||||
|
/** |
||||||
|
* 准备环境 |
||||||
|
* @return 返回true引导可继续执行 |
||||||
|
*/ |
||||||
|
boolean prepared(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 开始引导教程 |
||||||
|
*/ |
||||||
|
void onStart(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 完成引导教程 |
||||||
|
*/ |
||||||
|
|
||||||
|
void onComplete(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 终止引导教程 |
||||||
|
*/ |
||||||
|
|
||||||
|
void onTerminate(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 结束引导教程 |
||||||
|
*/ |
||||||
|
void onEnd(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
public abstract class GuideLifecycleAdaptor implements GuideLifecycle{ |
||||||
|
@Override |
||||||
|
public boolean prepared() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStart() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onComplete() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onTerminate() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onEnd() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.guide.collect.GuideCollector; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class GuideManager { |
||||||
|
private static GuideManager guideManager; |
||||||
|
|
||||||
|
private Guide currentGuide; |
||||||
|
private List<GuideVersion> guideVersionList; |
||||||
|
|
||||||
|
public static GuideManager getInstance() { |
||||||
|
if (guideManager == null) { |
||||||
|
guideManager = new GuideManager(); |
||||||
|
} |
||||||
|
return guideManager; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideManager() { |
||||||
|
guideVersionList = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public Guide getCurrentGuide() { |
||||||
|
return currentGuide; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentGuide(Guide currentGuide) { |
||||||
|
this.currentGuide = currentGuide; |
||||||
|
} |
||||||
|
|
||||||
|
public void addGuideGroup(String version, GuideGroup guideGroup) { |
||||||
|
GuideVersion guideVersion = getGuideVersion(version); |
||||||
|
if (guideVersion == null) { |
||||||
|
guideVersion = new GuideVersion(version); |
||||||
|
guideVersionList.add(guideVersion); |
||||||
|
} |
||||||
|
guideVersion.addGuideGroup(guideGroup); |
||||||
|
} |
||||||
|
|
||||||
|
public void addGuide(String groupId, Guide guide) { |
||||||
|
GuideGroup guideGroup = getGuideGroup(groupId); |
||||||
|
if (guideGroup != null) { |
||||||
|
guideGroup.addGuide(guide); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public List<GuideVersion> getGuideVersionList() { |
||||||
|
return guideVersionList; |
||||||
|
} |
||||||
|
|
||||||
|
public List<Guide> getAllGuide() { |
||||||
|
List<Guide> guideList = new ArrayList<>(); |
||||||
|
for (GuideVersion version : guideVersionList) { |
||||||
|
for (GuideGroup group : version.getGuideGroupList()) { |
||||||
|
for (Guide guide : group.getGuideList()) { |
||||||
|
guideList.add(guide); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return guideList; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideVersion getGuideVersion(String version) { |
||||||
|
for (GuideVersion guideVersion : guideVersionList) { |
||||||
|
if (StringUtils.equals(version, guideVersion.getVersion())) { |
||||||
|
return guideVersion; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideGroup getGuideGroup(String groupId) { |
||||||
|
for (GuideVersion version : guideVersionList) { |
||||||
|
for (GuideGroup group : version.getGuideGroupList()) { |
||||||
|
if (StringUtils.equals(groupId, group.getId())) { |
||||||
|
return group; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public Guide getGuide(String guideId) { |
||||||
|
for (GuideVersion version : guideVersionList) { |
||||||
|
for (GuideGroup group : version.getGuideGroupList()) { |
||||||
|
for (Guide guide : group.getGuideList()) { |
||||||
|
if (StringUtils.equals(guideId, guide.getId())) { |
||||||
|
return guide; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public void completeAll() { |
||||||
|
for (GuideVersion version : guideVersionList) { |
||||||
|
for (GuideGroup group : version.getGuideGroupList()) { |
||||||
|
for (Guide guide : group.getGuideList()) { |
||||||
|
guide.setComplete(true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
GuideCollector.getInstance().saveInfo(); |
||||||
|
} |
||||||
|
|
||||||
|
public void clearAll() { |
||||||
|
guideVersionList.clear(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class GuideVersion { |
||||||
|
private String version; |
||||||
|
private List<GuideGroup> guideGroupList; |
||||||
|
public GuideVersion(String version) { |
||||||
|
guideGroupList = new ArrayList<>(); |
||||||
|
this.version = version; |
||||||
|
} |
||||||
|
|
||||||
|
public String getVersion() { |
||||||
|
return version; |
||||||
|
} |
||||||
|
|
||||||
|
public void setVersion(String version) { |
||||||
|
this.version = version; |
||||||
|
} |
||||||
|
|
||||||
|
public List<GuideGroup> getGuideGroupList() { |
||||||
|
return guideGroupList; |
||||||
|
} |
||||||
|
|
||||||
|
public void addGuideGroup(GuideGroup guideGroup) { |
||||||
|
guideGroupList.add(guideGroup); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
package com.fr.design.mainframe.guide.base; |
||||||
|
|
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.guide.scene.AbstractGuideScene; |
||||||
|
import com.fr.design.mainframe.guide.scene.GuideScene; |
||||||
|
import com.fr.design.mainframe.guide.ui.GuideLoadingGlassPane; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
|
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.event.WindowAdapter; |
||||||
|
import java.awt.event.WindowEvent; |
||||||
|
|
||||||
|
public class GuideView extends JDialog { |
||||||
|
private static GuideView guideView; |
||||||
|
private Guide invoker; |
||||||
|
private GuideScene scene; |
||||||
|
private Color modalColor; |
||||||
|
private float modalOpacity; |
||||||
|
private Window window; |
||||||
|
|
||||||
|
public static GuideView getInstance(Guide guide) { |
||||||
|
if (guideView == null) { |
||||||
|
guideView = new GuideView(DesignerContext.getDesignerFrame(), guide); |
||||||
|
} |
||||||
|
return guideView; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideView(Window window) { |
||||||
|
super(window); |
||||||
|
this.setUndecorated(true); |
||||||
|
this.window = window; |
||||||
|
this.modalColor = Color.BLACK; |
||||||
|
this.modalOpacity = 0.6f; |
||||||
|
this.setPreferredSize(window.getSize()); |
||||||
|
this.setSize(window.getSize()); |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
setBg(); |
||||||
|
DesignerContext.getDesignerFrame().addWindowListener(new WindowAdapter() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void windowDeiconified(WindowEvent e) { |
||||||
|
if (isVisible()) { |
||||||
|
updateGuideViewLocation(); |
||||||
|
// window 带透明的dialog在窗口最小化后再打开会不渲染,这边试了下重新设置visible可行
|
||||||
|
setVisible(false); |
||||||
|
setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public GuideView(Window window, Guide guide) { |
||||||
|
this(window); |
||||||
|
this.invoker = guide; |
||||||
|
} |
||||||
|
|
||||||
|
public void setModalColor(Color color) { |
||||||
|
modalColor = color; |
||||||
|
setBg(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setModalOpacity(float opacity){ |
||||||
|
modalOpacity = opacity; |
||||||
|
setBg(); |
||||||
|
} |
||||||
|
|
||||||
|
private void setBg() { |
||||||
|
Color newColor = new Color(modalColor.getRed(), modalColor.getGreen(), modalColor.getBlue(), (int) (255 * modalOpacity)); |
||||||
|
this.setBackground(newColor); |
||||||
|
} |
||||||
|
|
||||||
|
public void setScene(GuideScene scene) { |
||||||
|
if (scene instanceof AbstractGuideScene) { |
||||||
|
((AbstractGuideScene) scene).setContainer(this); |
||||||
|
} |
||||||
|
this.scene = scene; |
||||||
|
} |
||||||
|
|
||||||
|
public void showGuide() { |
||||||
|
updateGuideViewLocation(); |
||||||
|
this.setVisible(true); |
||||||
|
if (scene != null) { |
||||||
|
scene.start(); |
||||||
|
} else { |
||||||
|
GuideManager.getInstance().getCurrentGuide().complete(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void dismissGuide() { |
||||||
|
this.getLayeredPane().removeAll(); |
||||||
|
revalidate(); |
||||||
|
repaint(); |
||||||
|
setVisible(false); |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
super.paint(g); |
||||||
|
} |
||||||
|
|
||||||
|
public void showLoading() { |
||||||
|
this.setGlassPane(GuideLoadingGlassPane.getInstance()); |
||||||
|
GuideLoadingGlassPane.getInstance().startLoading(); |
||||||
|
updateGuideViewLocation(); |
||||||
|
this.setVisible(true); |
||||||
|
this.invalidate(); |
||||||
|
this.getGlassPane().setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
public void hideLoading() { |
||||||
|
GuideLoadingGlassPane.getInstance().stopLoading(); |
||||||
|
this.getGlassPane().setVisible(false); |
||||||
|
repaint(); |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
setVisible(false); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void updateGuideViewLocation() { |
||||||
|
GUICoreUtils.centerWindow(window, this); |
||||||
|
this.setBounds(window.getBounds()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,155 @@ |
|||||||
|
package com.fr.design.mainframe.guide.collect; |
||||||
|
|
||||||
|
import com.fr.base.io.XMLReadHelper; |
||||||
|
import com.fr.design.mainframe.guide.base.Guide; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideManager; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ProductConstants; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.xml.XMLPrintWriter; |
||||||
|
import com.fr.stable.xml.XMLReadable; |
||||||
|
import com.fr.stable.xml.XMLTools; |
||||||
|
import com.fr.stable.xml.XMLable; |
||||||
|
import com.fr.stable.xml.XMLableReader; |
||||||
|
import com.fr.third.javax.xml.stream.XMLStreamException; |
||||||
|
import com.fr.third.org.apache.commons.io.FileUtils; |
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class GuideCollector implements XMLable { |
||||||
|
private static final String ROOT_XML = "GuideCollector"; |
||||||
|
private static final String GUIDE_XML = "Guide"; |
||||||
|
private static final String FILE_NAME = "guide.info"; |
||||||
|
private static GuideCollector collector; |
||||||
|
|
||||||
|
private boolean showHint; |
||||||
|
private List<Guide> cacheGuides; |
||||||
|
|
||||||
|
public static GuideCollector getInstance() { |
||||||
|
if (collector == null) { |
||||||
|
collector = new GuideCollector(); |
||||||
|
} |
||||||
|
return collector; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideCollector() { |
||||||
|
cacheGuides = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isShowHint() { |
||||||
|
return showHint; |
||||||
|
} |
||||||
|
|
||||||
|
public void setShowHint(boolean showHint) { |
||||||
|
this.showHint = showHint; |
||||||
|
saveInfo(); |
||||||
|
} |
||||||
|
|
||||||
|
public void load() { |
||||||
|
for(Guide cacheGuide : cacheGuides) { |
||||||
|
Guide guide = GuideManager.getInstance().getGuide(cacheGuide.getId()); |
||||||
|
if (guide != null) { |
||||||
|
guide.setComplete(cacheGuide.isComplete()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void saveInfo() { |
||||||
|
cacheGuides = GuideManager.getInstance().getAllGuide(); |
||||||
|
try { |
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||||
|
XMLTools.writeOutputStreamXML(this, out); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
String fileContent = new String(out.toByteArray(), StandardCharsets.UTF_8); |
||||||
|
FileUtils.writeStringToFile(getInfoFile(), fileContent, StandardCharsets.UTF_8); |
||||||
|
} catch (Exception ex) { |
||||||
|
FineLoggerFactory.getLogger().error(ex.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private File getInfoFile() { |
||||||
|
File file = new File(StableUtils.pathJoin(ProductConstants.getEnvHome(), FILE_NAME)); |
||||||
|
try { |
||||||
|
if (!file.exists()) { |
||||||
|
file.createNewFile(); |
||||||
|
} |
||||||
|
} catch (Exception ex) { |
||||||
|
FineLoggerFactory.getLogger().error(ex.getMessage(), ex); |
||||||
|
} |
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
public void loadFromFile() { |
||||||
|
if (!getInfoFile().exists()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
XMLableReader reader = null; |
||||||
|
try (InputStream in = new FileInputStream(getInfoFile())) { |
||||||
|
reader = XMLReadHelper.createXMLableReader(in, XMLPrintWriter.XML_ENCODER); |
||||||
|
if (reader == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
reader.readXMLObject(this); |
||||||
|
} catch (FileNotFoundException e) { |
||||||
|
} catch (XMLStreamException | IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (reader != null) { |
||||||
|
reader.close(); |
||||||
|
} |
||||||
|
} catch (XMLStreamException e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readXML(XMLableReader xmLableReader) { |
||||||
|
String tagName = xmLableReader.getTagName(); |
||||||
|
if (tagName.equals(ROOT_XML)) { |
||||||
|
showHint = xmLableReader.getAttrAsBoolean("showHint", false); |
||||||
|
xmLableReader.readXMLObject(new XMLReadable() { |
||||||
|
public void readXML(XMLableReader reader) { |
||||||
|
String tagName = reader.getTagName(); |
||||||
|
if (tagName.equals(GUIDE_XML)) { |
||||||
|
Guide guide = new Guide(); |
||||||
|
guide.setId(reader.getAttrAsString("id", null)); |
||||||
|
guide.setComplete(reader.getAttrAsBoolean("isComplete", false)); |
||||||
|
cacheGuides.add(guide); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeXML(XMLPrintWriter writer) { |
||||||
|
writer.startTAG(ROOT_XML); |
||||||
|
writer.attr("showHint", showHint); |
||||||
|
for(Guide guide : GuideManager.getInstance().getAllGuide()) { |
||||||
|
writer.startTAG(GUIDE_XML); |
||||||
|
writer.attr("id", guide.getId()); |
||||||
|
writer.attr("isComplete", guide.isComplete()); |
||||||
|
writer.end(); |
||||||
|
} |
||||||
|
writer.end(); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object clone() throws CloneNotSupportedException { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,493 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideManager; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideView; |
||||||
|
import com.fr.design.mainframe.guide.utils.ScreenImage; |
||||||
|
import com.fr.design.mainframe.guide.tip.BubbleTip; |
||||||
|
import com.fr.design.mainframe.guide.tip.GuideTip; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.ImageIcon; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.AWTException; |
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Composite; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Insets; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public abstract class AbstractGuideScene extends JPanel implements GuideScene { |
||||||
|
private static final int DEFAULT_ARROW_HEIGHT = 12; |
||||||
|
private static final int DEFAULT_ARROW_WIDTH = 18; |
||||||
|
public static final Insets DEFAULT_HIGHLIGHT_INSETS = new Insets(5, 5, 5, 5); |
||||||
|
|
||||||
|
private GuideScene nextScene; |
||||||
|
private SceneFilter sceneFilter; |
||||||
|
private GuideSceneLifecycle lifecycle; |
||||||
|
private GuideView container; |
||||||
|
private List<Component> targetList; |
||||||
|
private List<Component> highlightList; |
||||||
|
private Component nextButton; |
||||||
|
private List<Point[]> pointsList; |
||||||
|
|
||||||
|
|
||||||
|
public AbstractGuideScene() { |
||||||
|
this.setLayout(null); |
||||||
|
this.setOpaque(false); |
||||||
|
targetList = new ArrayList<>(); |
||||||
|
highlightList = new ArrayList<>(); |
||||||
|
pointsList = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据设计器上组件添加高亮区域视图, |
||||||
|
* JComponent 采用组件绘制方式 |
||||||
|
* Component 采用屏幕截图方式 |
||||||
|
* @param component 设计器组件对象 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean addTarget(Component component) { |
||||||
|
try { |
||||||
|
if (component == null || container == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
Point point = SwingUtilities.convertPoint(component,0,0, container.getRootPane()); |
||||||
|
Rectangle rectangle = new Rectangle(point, component.getSize()); |
||||||
|
BufferedImage image; |
||||||
|
|
||||||
|
if (component instanceof JComponent) { |
||||||
|
JComponent jComponent = (JComponent) component; |
||||||
|
image = ScreenImage.createImage(jComponent); |
||||||
|
} else if (component instanceof Window) { |
||||||
|
image = ScreenImage.createImage(component); |
||||||
|
} else { |
||||||
|
image = captureImage(component); |
||||||
|
} |
||||||
|
targetList.add(component); |
||||||
|
highlightList.add(getTargetComponentWithImage(image, rectangle)); |
||||||
|
return true; |
||||||
|
} catch (AWTException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
GuideManager.getInstance().getCurrentGuide().terminate(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据设计器上选定区域获取高亮视图,采用截屏的方式 |
||||||
|
* @param rectangle 选定区域,相对屏幕 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean addTarget(Rectangle rectangle) { |
||||||
|
try { |
||||||
|
targetList.add(null); |
||||||
|
BufferedImage image = captureImage(rectangle); |
||||||
|
highlightList.add(getTargetComponentWithImage(image, rectangle)); |
||||||
|
return true; |
||||||
|
} catch (AWTException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
GuideManager.getInstance().getCurrentGuide().terminate(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据设计器组件,选定其中某个区域,添加高亮视图 |
||||||
|
* @param component 设计器组件 |
||||||
|
* @param origin 相对组件的区域 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean addTarget(Component component, Rectangle origin) { |
||||||
|
try { |
||||||
|
if (component == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
Point point = SwingUtilities.convertPoint(component,0,0, container.getRootPane()); |
||||||
|
|
||||||
|
origin = origin.intersection(new Rectangle(0,0,component.getWidth(), component.getHeight())); |
||||||
|
Rectangle rectangle = new Rectangle(point.x + origin.x, point.y + origin.y, origin.width, origin.height); |
||||||
|
|
||||||
|
BufferedImage image; |
||||||
|
|
||||||
|
if (component instanceof JComponent) { |
||||||
|
JComponent jComponent = (JComponent) component; |
||||||
|
image = ScreenImage.createImage(jComponent, origin); |
||||||
|
} else { |
||||||
|
image = ScreenImage.createImage(component).getSubimage(origin.x, origin.y, origin.width, origin.height); |
||||||
|
} |
||||||
|
targetList.add(component); |
||||||
|
highlightList.add(getTargetComponentWithImage(image, rectangle)); |
||||||
|
return true; |
||||||
|
} catch (AWTException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
GuideManager.getInstance().getCurrentGuide().terminate(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 添加自定义组件 |
||||||
|
* @param component 自定义组件 |
||||||
|
* @param rectangle 相对引导页的区域 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean addCustomTarget(Component component, Rectangle rectangle) { |
||||||
|
if (component == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
component.setBounds(rectangle); |
||||||
|
targetList.add(component); |
||||||
|
highlightList.add(component); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean addCustomTarget(Component component, Point location) { |
||||||
|
return addCustomTarget(component, new Rectangle(location, component.getPreferredSize())); |
||||||
|
} |
||||||
|
|
||||||
|
protected List<Component> getTargetList() { |
||||||
|
return targetList; |
||||||
|
} |
||||||
|
|
||||||
|
public List<Component> getHighlightList() { |
||||||
|
return highlightList; |
||||||
|
} |
||||||
|
|
||||||
|
private UILabel getTargetComponentWithImage(BufferedImage image, Rectangle rectangle) { |
||||||
|
ImageIcon ic = new ImageIcon(image); |
||||||
|
UILabel label = new UILabel(ic){ |
||||||
|
@Override |
||||||
|
public Insets getInsets() { |
||||||
|
return DEFAULT_HIGHLIGHT_INSETS; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
label.setBorder(BorderFactory.createMatteBorder(DEFAULT_HIGHLIGHT_INSETS.top, DEFAULT_HIGHLIGHT_INSETS.left, DEFAULT_HIGHLIGHT_INSETS.bottom, DEFAULT_HIGHLIGHT_INSETS.right, Color.WHITE)); |
||||||
|
|
||||||
|
label.setOpaque(true); |
||||||
|
label.setBounds(new Rectangle( |
||||||
|
rectangle.x - DEFAULT_HIGHLIGHT_INSETS.left, |
||||||
|
rectangle.y - DEFAULT_HIGHLIGHT_INSETS.top, |
||||||
|
rectangle.width + DEFAULT_HIGHLIGHT_INSETS.left + DEFAULT_HIGHLIGHT_INSETS.right, |
||||||
|
rectangle.height + DEFAULT_HIGHLIGHT_INSETS.top + DEFAULT_HIGHLIGHT_INSETS.bottom |
||||||
|
)); |
||||||
|
return label; |
||||||
|
} |
||||||
|
|
||||||
|
private BufferedImage captureImage(Rectangle rectangle) throws AWTException { |
||||||
|
container.setVisible(false); |
||||||
|
BufferedImage image = ScreenImage.createImage(rectangle); |
||||||
|
showContainer(); |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
private BufferedImage captureImage(Component component) throws AWTException { |
||||||
|
container.setVisible(false); |
||||||
|
BufferedImage image = ScreenImage.createImage(component); |
||||||
|
showContainer(); |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
private void showContainer() { |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
container.setVisible(true); |
||||||
|
container.toFront(); |
||||||
|
container.requestFocus(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义位置添加气泡提示组件 |
||||||
|
* @param title 提示标题 |
||||||
|
* @param content 提示内容 |
||||||
|
* @param direction 气泡窗口位置方向 |
||||||
|
* @param anchor 气泡窗口箭头坐标 |
||||||
|
* @param bubbleTailStart 气泡窗口箭头坐标相对于位置方向交叉轴的比例(从左到右,从上到下) |
||||||
|
*/ |
||||||
|
public void addBubbleTip(String title, String content, GuideTip.Direction direction, Point anchor, float bubbleTailStart) { |
||||||
|
BubbleTip bt = new BubbleTip(title, content, direction, bubbleTailStart); |
||||||
|
bt.setAnchor(anchor); |
||||||
|
this.add(bt.getTip()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 以上一个添加的引导目标窗口为基础,添加对应的气泡窗 |
||||||
|
* @param title 提示标题 |
||||||
|
* @param content 提示内容 |
||||||
|
* @param direction |
||||||
|
* @param anchorStart |
||||||
|
* @param bubbleTailStart |
||||||
|
*/ |
||||||
|
public void addBubbleTip(String title, String content, GuideTip.Direction direction, float anchorStart, float bubbleTailStart) { |
||||||
|
if (highlightList.size() == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
Component lastTarget = highlightList.get(highlightList.size() - 1); |
||||||
|
Rectangle bounds = lastTarget.getBounds(); |
||||||
|
Point anchor = new Point(0,0); |
||||||
|
if (direction == GuideTip.Direction.TOP) { |
||||||
|
anchor = new Point(bounds.x + (int)(bounds.width * anchorStart), bounds.y); |
||||||
|
} else if (direction == GuideTip.Direction.BOTTOM) { |
||||||
|
anchor = new Point(bounds.x + (int)(bounds.width * anchorStart), bounds.y + bounds.height); |
||||||
|
} else if (direction == GuideTip.Direction.LEFT) { |
||||||
|
anchor = new Point(bounds.x, bounds.y + (int)(bounds.height * anchorStart)); |
||||||
|
} else if (direction == GuideTip.Direction.RIGHT) { |
||||||
|
anchor = new Point(bounds.x + bounds.width, bounds.y + (int) (bounds.height * anchorStart)); |
||||||
|
} |
||||||
|
addBubbleTip(title, content, direction, anchor, bubbleTailStart); |
||||||
|
} |
||||||
|
|
||||||
|
public void addBubbleTip(String title, String content, GuideTip.Direction direction) { |
||||||
|
addBubbleTip(title, content, direction, 0.5f, 0.5f); |
||||||
|
} |
||||||
|
|
||||||
|
public void addBubbleTip(String title, GuideTip.Direction direction) { |
||||||
|
addBubbleTip(title, StringUtils.EMPTY, direction); |
||||||
|
} |
||||||
|
|
||||||
|
public void addTip(GuideTip tip) { |
||||||
|
this.add(tip.getTip()); |
||||||
|
} |
||||||
|
|
||||||
|
public void addLineArrow(Point... points) { |
||||||
|
pointsList.add(points); |
||||||
|
} |
||||||
|
|
||||||
|
public void setContainer(GuideView container) { |
||||||
|
this.container = container; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideView getContainer() { |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public GuideScene nextScene(GuideScene scene) { |
||||||
|
nextScene = scene; |
||||||
|
return nextScene; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addSceneFilter(SceneFilter filter) { |
||||||
|
sceneFilter = filter; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
clear(); |
||||||
|
if (lifecycle != null && !lifecycle.prepared()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
showScene(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showScene() { |
||||||
|
if (container != null) { |
||||||
|
container.setContentPane(AbstractGuideScene.this); |
||||||
|
setBounds(0, 0 , getSceneWidth(), getSceneWidth()); |
||||||
|
|
||||||
|
// show target
|
||||||
|
for (int index = highlightList.size() - 1; index >= 0; index--) { |
||||||
|
add(highlightList.get(index)); |
||||||
|
} |
||||||
|
// show next button
|
||||||
|
if (nextButton != null) { |
||||||
|
nextButton.setBounds((getSceneWidth() - 60) / 2, getSceneHeight() - 100, 60, 30); |
||||||
|
add(nextButton); |
||||||
|
} |
||||||
|
} |
||||||
|
showContainer(); |
||||||
|
if (lifecycle != null) { |
||||||
|
lifecycle.onShow(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void complete() { |
||||||
|
clear(); |
||||||
|
if (lifecycle != null && !lifecycle.onComplete()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (sceneFilter != null) { |
||||||
|
nextScene = sceneFilter.getFilterScene(); |
||||||
|
} |
||||||
|
if (nextScene != null) { |
||||||
|
if (nextScene instanceof AbstractGuideScene) { |
||||||
|
((AbstractGuideScene) nextScene).setContainer(container); |
||||||
|
} |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
nextScene.start(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
GuideManager.getInstance().getCurrentGuide().complete(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void registerLifecycle(GuideSceneLifecycle lifecycle) { |
||||||
|
this.lifecycle = lifecycle; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeLifecycle() { |
||||||
|
this.lifecycle = null; |
||||||
|
} |
||||||
|
|
||||||
|
public void showNextButton() { |
||||||
|
UIButton nextButton = new UIButton("Next"); |
||||||
|
nextButton.setPreferredSize(new Dimension(60, 30)); |
||||||
|
nextButton.setOpaque(false); |
||||||
|
nextButton.setFont(nextButton.getFont().deriveFont(20)); |
||||||
|
nextButton.setRoundBorder(true, 8); |
||||||
|
nextButton.setForeground(Color.WHITE); |
||||||
|
nextButton.setNormalPainted(false); |
||||||
|
nextButton.setPressedPainted(false); |
||||||
|
nextButton.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
complete(); |
||||||
|
} |
||||||
|
}); |
||||||
|
this.nextButton = nextButton; |
||||||
|
} |
||||||
|
|
||||||
|
private int getSceneWidth() { |
||||||
|
if (container == null) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return container.getLayeredPane().getWidth(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private int getSceneHeight() { |
||||||
|
if (container == null) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return container.getLayeredPane().getHeight(); |
||||||
|
} |
||||||
|
|
||||||
|
public void clear() { |
||||||
|
targetList = new ArrayList<>(); |
||||||
|
highlightList = new ArrayList<>(); |
||||||
|
this.nextButton = null; |
||||||
|
if (this.getComponentCount() > 0) { |
||||||
|
this.removeAll(); |
||||||
|
invalidate(); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
super.paint(g); |
||||||
|
if (pointsList.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
Graphics2D g2d = (Graphics2D) g; |
||||||
|
Composite oldComposite = g2d.getComposite(); |
||||||
|
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); |
||||||
|
g2d.setColor(Color.WHITE); |
||||||
|
for (Point[] points : pointsList) { |
||||||
|
if (points.length <= 1) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
Point startPoint = points[0]; |
||||||
|
Point endPoint = startPoint; |
||||||
|
for (int index = 1; index < points.length; index++) { |
||||||
|
startPoint = endPoint; |
||||||
|
endPoint = points[index]; |
||||||
|
g2d.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y); |
||||||
|
} |
||||||
|
|
||||||
|
drawArrow(g2d, startPoint, endPoint); |
||||||
|
} |
||||||
|
g2d.setComposite(oldComposite); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据最后两点坐标计算出三角箭头的三个点坐标,绘制箭头 |
||||||
|
* 这里实现以终点坐标为坐标轴圆点计算 |
||||||
|
* @param start 起始点坐标 |
||||||
|
* @param end 重点坐标 |
||||||
|
*/ |
||||||
|
private void drawArrow(Graphics2D g2d, Point start, Point end) { |
||||||
|
try{ |
||||||
|
double dealtPointX = start.x - end.x; |
||||||
|
double dealtPointY = -(start.y - end.y); |
||||||
|
|
||||||
|
double pointDistance = calDistance(dealtPointX, dealtPointY); |
||||||
|
double triangleHeight = Math.min(DEFAULT_ARROW_HEIGHT, pointDistance); |
||||||
|
double triangleWidth = triangleHeight * (DEFAULT_ARROW_WIDTH / 2) / DEFAULT_ARROW_WIDTH; |
||||||
|
if (triangleHeight < 1 || triangleWidth < 1 || pointDistance < 1) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
double pointAngle; |
||||||
|
double abs = 1; |
||||||
|
if (dealtPointX == 0) { |
||||||
|
pointAngle = Math.PI / 2; |
||||||
|
} else { |
||||||
|
pointAngle = Math.atan(dealtPointY / dealtPointX); |
||||||
|
} |
||||||
|
if (dealtPointY < 0) { |
||||||
|
pointAngle += Math.PI; |
||||||
|
abs = -1; |
||||||
|
} |
||||||
|
|
||||||
|
double deltaAngle = Math.atan(triangleWidth / triangleHeight); |
||||||
|
double triangleDistance = calDistance(triangleWidth, triangleHeight); |
||||||
|
|
||||||
|
Point p1 =calPoint(end, triangleDistance, pointAngle - deltaAngle, abs); |
||||||
|
Point p2 = calPoint(end, triangleDistance, pointAngle + deltaAngle, abs); |
||||||
|
|
||||||
|
int xPoints[] = {end.x, p1.x, p2.x}; |
||||||
|
int yPoints[] = {end.y, p1.y, p2.y}; |
||||||
|
|
||||||
|
g2d.fillPolygon(xPoints, yPoints, 3); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
GuideManager.getInstance().getCurrentGuide().terminate(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private double calDistance(double x, double y) { |
||||||
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); |
||||||
|
} |
||||||
|
|
||||||
|
private Point calPoint(Point relativePoint, double distance, double angle, double abs) { |
||||||
|
int x = (int)(relativePoint.x + abs * distance * Math.cos(angle)); |
||||||
|
int y = (int)(relativePoint.y - abs * distance * Math.sin(angle)); |
||||||
|
return new Point(x, y); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,136 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
import com.fr.design.gui.ibutton.UIButtonGroup; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideManager; |
||||||
|
|
||||||
|
import javax.swing.AbstractButton; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
public class ClickScene extends AbstractGuideScene{ |
||||||
|
public enum ClickType { |
||||||
|
LEFT, LEFT_DOUBLE, RIGHT |
||||||
|
} |
||||||
|
|
||||||
|
public void addClickTarget(Component component, ClickType clickType) { |
||||||
|
addClickTarget(component, clickType, false); |
||||||
|
} |
||||||
|
|
||||||
|
public void addClickTarget(Component component, ClickType clickType, boolean isDispatch) { |
||||||
|
if (super.addTarget(component)) { |
||||||
|
addTargetClickListener(clickType, isDispatch); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addClickTarget(Rectangle rectangle, ClickType clickType) { |
||||||
|
if (super.addTarget(rectangle)) { |
||||||
|
addTargetClickListener(clickType,false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addClickTarget(Component component, Rectangle rectangle, ClickType clickType) { |
||||||
|
if (super.addTarget(component, rectangle)) { |
||||||
|
addTargetClickListener(clickType, false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addCustomClickTarget(Component component, Rectangle rectangle, ClickType clickType) { |
||||||
|
if (super.addCustomTarget(component, rectangle)) { |
||||||
|
addTargetClickListener(clickType, false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addCustomClickTarget(Component component, Point location, ClickType clickType) { |
||||||
|
if (super.addCustomTarget(component, location)) { |
||||||
|
addTargetClickListener(clickType, false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void addTargetClickListener(ClickType clickType, boolean isDispatch) { |
||||||
|
Component highlight = getHighlightList().get(getHighlightList().size() - 1); |
||||||
|
Component target = getTargetList().get(getTargetList().size() - 1); |
||||||
|
highlight.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
if (e.getButton() == MouseEvent.BUTTON1) { |
||||||
|
if ((e.getClickCount() == 1 && clickType == ClickType.LEFT) || (e.getClickCount() == 2 && clickType == ClickType.LEFT_DOUBLE)) { |
||||||
|
dealWithDispatchLeftClick(target, e, isDispatch); |
||||||
|
} |
||||||
|
} else if (e.getButton() == MouseEvent.BUTTON3 && clickType == ClickType.RIGHT) { |
||||||
|
clear(); |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (isDispatch) { |
||||||
|
redispatchMouseEvent(e, target); |
||||||
|
} |
||||||
|
complete(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
if ((target instanceof AbstractButton) && (target.getParent() instanceof UIButtonGroup)) { |
||||||
|
if (isDispatch) { |
||||||
|
redispatchMouseEvent(e, target); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void dealWithDispatchLeftClick(Component target, MouseEvent e, boolean isDispatch) { |
||||||
|
clear(); |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (isDispatch) { |
||||||
|
if (target instanceof AbstractButton) { |
||||||
|
AbstractButton button = (AbstractButton) target; |
||||||
|
ActionListener[] actionListeners= button.getActionListeners(); |
||||||
|
for(int i = 0; i < actionListeners.length; i++) { |
||||||
|
ActionListener actionListener = actionListeners[i]; |
||||||
|
actionListener.actionPerformed(new ActionEvent( |
||||||
|
button, |
||||||
|
ActionEvent.ACTION_PERFORMED, |
||||||
|
button.getActionCommand(), |
||||||
|
e.getWhen(), |
||||||
|
e.getModifiers() |
||||||
|
)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
redispatchMouseEvent(e, target); |
||||||
|
} |
||||||
|
} |
||||||
|
complete(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void redispatchMouseEvent(MouseEvent e, Component component) { |
||||||
|
component.dispatchEvent(new MouseEvent(component, e.getID(), e |
||||||
|
.getWhen(), e.getModifiers(), e.getX(), |
||||||
|
e.getY(), e.getClickCount(), e.isPopupTrigger())); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showScene() { |
||||||
|
// 交互类的 scene 如果没有高亮内容块载,需要及时终止Guide,否则就没法去掉模态框影响到设计器主功能的使用了
|
||||||
|
if (this.getComponentCount() == 0 && getHighlightList().isEmpty()) { |
||||||
|
GuideManager.getInstance().getCurrentGuide().terminate(); |
||||||
|
} else { |
||||||
|
super.showScene(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
|
||||||
|
public class DisplayScene extends AbstractGuideScene { |
||||||
|
private long delay; |
||||||
|
private static final long DEFAULT_DELAY = 1000; |
||||||
|
private static Timer displayTimer = new Timer(); |
||||||
|
|
||||||
|
public DisplayScene() { |
||||||
|
this(DEFAULT_DELAY); |
||||||
|
} |
||||||
|
|
||||||
|
public DisplayScene(long delay) { |
||||||
|
super(); |
||||||
|
this.delay = delay; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDelay(long delay) { |
||||||
|
this.delay = delay; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void showScene() { |
||||||
|
super.showScene(); |
||||||
|
// 实例化Timer类
|
||||||
|
displayTimer.schedule(new TimerTask() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
complete(); |
||||||
|
displayTimer.purge(); |
||||||
|
} |
||||||
|
}, delay); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
import com.fr.design.mainframe.guide.scene.drag.DragAndDropDragGestureListener; |
||||||
|
|
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.dnd.DnDConstants; |
||||||
|
import java.awt.dnd.DragSourceDropEvent; |
||||||
|
import java.awt.dnd.DropTarget; |
||||||
|
import java.awt.dnd.DropTargetDropEvent; |
||||||
|
|
||||||
|
public class DragScene extends AbstractGuideScene{ |
||||||
|
public enum DragType{ |
||||||
|
NONE, FROM, TO |
||||||
|
} |
||||||
|
|
||||||
|
public void addDragTarget(Component component, DragType type) { |
||||||
|
if (super.addTarget(component)) { |
||||||
|
addDragTargetListener(type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addDragTarget(Rectangle rectangle, DragType type) { |
||||||
|
if (super.addTarget(rectangle)) { |
||||||
|
addDragTargetListener(type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addDragTarget(Component component, Rectangle rectangle, DragType type) { |
||||||
|
if (super.addTarget(component, rectangle)) { |
||||||
|
addDragTargetListener(type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addCustomDragTarget(Component component, Rectangle rectangle, DragType type) { |
||||||
|
if (super.addCustomTarget(component, rectangle)) { |
||||||
|
addDragTargetListener(type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void addDragTargetListener(DragType dragType) { |
||||||
|
Component target = getHighlightList().get(getHighlightList().size() - 1); |
||||||
|
|
||||||
|
if (dragType == DragType.FROM) { |
||||||
|
new DragAndDropDragGestureListener(target, DnDConstants.ACTION_COPY_OR_MOVE){ |
||||||
|
@Override |
||||||
|
public void dragDropEnd(DragSourceDropEvent dsde) { |
||||||
|
if (dsde.getDropSuccess()) { |
||||||
|
complete(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} else if (dragType == DragType.TO) { |
||||||
|
target.setDropTarget(new DropSceneTarget()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class DropSceneTarget extends DropTarget { |
||||||
|
@Override |
||||||
|
public synchronized void drop(DropTargetDropEvent dtde) { |
||||||
|
dtde.dropComplete(true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
|
||||||
|
public interface GuideScene { |
||||||
|
GuideScene nextScene(GuideScene scene); |
||||||
|
|
||||||
|
void addSceneFilter(SceneFilter filter); |
||||||
|
|
||||||
|
void start(); |
||||||
|
|
||||||
|
void showScene(); |
||||||
|
|
||||||
|
void complete(); |
||||||
|
|
||||||
|
void registerLifecycle(GuideSceneLifecycle lifecycle); |
||||||
|
|
||||||
|
void removeLifecycle(); |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
public interface GuideSceneLifecycle { |
||||||
|
/** |
||||||
|
* 引导场景准备工作 |
||||||
|
* 给 scene 添加 target 应该在这个阶段处理 |
||||||
|
* @return 返回true自动执行scene, 返回false需要手动触发 |
||||||
|
*/ |
||||||
|
boolean prepared(); |
||||||
|
|
||||||
|
/** |
||||||
|
* scene 显示后 |
||||||
|
*/ |
||||||
|
void onShow(); |
||||||
|
|
||||||
|
/** |
||||||
|
* scene 交互完成后处理 |
||||||
|
* @return 返回true自动进入下一个scene,返回false需要手动触发 |
||||||
|
*/ |
||||||
|
boolean onComplete(); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
public abstract class GuideSceneLifecycleAdaptor implements GuideSceneLifecycle { |
||||||
|
@Override |
||||||
|
public boolean prepared() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onShow() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onComplete() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene; |
||||||
|
|
||||||
|
public interface SceneFilter { |
||||||
|
GuideScene getFilterScene(); |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.fr.design.mainframe.guide.scene.drag; |
||||||
|
|
||||||
|
import com.fr.general.ComparatorUtils; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.datatransfer.DataFlavor; |
||||||
|
import java.awt.datatransfer.Transferable; |
||||||
|
import java.awt.dnd.DragGestureEvent; |
||||||
|
import java.awt.dnd.DragGestureListener; |
||||||
|
import java.awt.dnd.DragSource; |
||||||
|
import java.awt.dnd.DragSourceAdapter; |
||||||
|
|
||||||
|
public class DragAndDropDragGestureListener extends DragSourceAdapter implements DragGestureListener { |
||||||
|
private Component component; |
||||||
|
|
||||||
|
public DragAndDropDragGestureListener(Component component, int actions) { |
||||||
|
this.component = component; |
||||||
|
DragSource source = new DragSource(); |
||||||
|
source.createDefaultDragGestureRecognizer(component, actions, this); |
||||||
|
} |
||||||
|
|
||||||
|
public void dragGestureRecognized(DragGestureEvent dge) { |
||||||
|
if (component != null) { |
||||||
|
try { |
||||||
|
DragAndDropTransferable dragAndDropTransferable = new DragAndDropTransferable(component); |
||||||
|
dge.startDrag(DragSource.DefaultCopyDrop, dragAndDropTransferable, this); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class DragAndDropTransferable implements Transferable { |
||||||
|
private Component component; |
||||||
|
|
||||||
|
public DragAndDropTransferable(Component component) { |
||||||
|
this.component = component; |
||||||
|
} |
||||||
|
|
||||||
|
DataFlavor[] flavors = {new DataFlavor(Component.class, "Component")}; |
||||||
|
|
||||||
|
public DataFlavor[] getTransferDataFlavors() { |
||||||
|
return flavors; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isDataFlavorSupported(DataFlavor flavor) { |
||||||
|
for (DataFlavor df : flavors) { |
||||||
|
if (ComparatorUtils.equals(df, flavor)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
@NotNull |
||||||
|
public Object getTransferData(DataFlavor df) { |
||||||
|
return component; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
package com.fr.design.mainframe.guide.tip; |
||||||
|
|
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.mainframe.guide.base.Guide; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideManager; |
||||||
|
import com.fr.design.mainframe.guide.ui.bubble.Bubble; |
||||||
|
import com.fr.design.mainframe.guide.ui.bubble.BubbleWithClose; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JOptionPane; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
public class BubbleTip implements GuideTip { |
||||||
|
private static final int GAP = 5; |
||||||
|
private BubbleWithClose bubbleBox; |
||||||
|
private Point anchor; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @param title 标题 |
||||||
|
* @param content 内容 |
||||||
|
* @param direction 气泡提示相对anchor的方向,这里方向会和气泡组件箭头方向相反 |
||||||
|
* @param tailStart 气泡尾巴相对当前边垂直方向偏移位置比例,值在0-1范围 |
||||||
|
*/ |
||||||
|
public BubbleTip(String title, String content, Direction direction, float tailStart) { |
||||||
|
bubbleBox = new BubbleWithClose(title, content, getBubbleBoxTailDirection(direction), tailStart); |
||||||
|
bubbleBox.addCloseActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
Guide currentGuide = GuideManager.getInstance().getCurrentGuide(); |
||||||
|
if (currentGuide != null) { |
||||||
|
|
||||||
|
int returnVal = FineJOptionPane.showConfirmDialog( |
||||||
|
currentGuide.getGuideView(), |
||||||
|
Toolkit.i18nText("Fine-Design_Guide_Option_Warning_Terminal"), |
||||||
|
Toolkit.i18nText("Fine-Design_Basic_Confirm"), |
||||||
|
JOptionPane.YES_NO_OPTION, |
||||||
|
JOptionPane.QUESTION_MESSAGE); |
||||||
|
if (returnVal == JOptionPane.YES_OPTION) { |
||||||
|
currentGuide.terminate(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
@Override |
||||||
|
public JComponent getTip() { |
||||||
|
if(anchor == null) { |
||||||
|
return new BubbleWithClose(bubbleBox.getTitle(), bubbleBox.getContent(), Bubble.TailDirection.TOP, 0); |
||||||
|
} else { |
||||||
|
setBubbleBoxBound(); |
||||||
|
return bubbleBox; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置锚点坐标 |
||||||
|
* 这里可以指定气泡组件箭头坐标的箭头坐标 |
||||||
|
* @param anchor |
||||||
|
*/ |
||||||
|
public void setAnchor(Point anchor) { |
||||||
|
this.anchor = anchor; |
||||||
|
} |
||||||
|
|
||||||
|
private void setBubbleBoxBound() { |
||||||
|
int bubbleW = bubbleBox.getPreferredSize().width; |
||||||
|
int bubbleH = bubbleBox.getPreferredSize().height; |
||||||
|
if (bubbleBox.isTailHorizontal()) { |
||||||
|
int x = bubbleBox.isTailLeft() ? anchor.x + GAP : anchor.x - bubbleW - GAP; |
||||||
|
int y = anchor.y - (int) (bubbleH * bubbleBox.getTailStart()); |
||||||
|
bubbleBox.setBounds(x, y, bubbleW, bubbleH); |
||||||
|
} else if (bubbleBox.isTailVertical()) { |
||||||
|
int x = anchor.x - (int) (bubbleW * bubbleBox.getTailStart()); |
||||||
|
int y = bubbleBox.isTailTop() ? anchor.y + GAP : anchor.y - bubbleH - GAP; |
||||||
|
bubbleBox.setBounds(x, y, bubbleW, bubbleH); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Bubble.TailDirection getBubbleBoxTailDirection(Direction direction) { |
||||||
|
switch (direction) { |
||||||
|
case TOP: |
||||||
|
return Bubble.TailDirection.BOTTOM; |
||||||
|
case BOTTOM: |
||||||
|
return Bubble.TailDirection.TOP; |
||||||
|
case LEFT: |
||||||
|
return Bubble.TailDirection.RIGHT; |
||||||
|
case RIGHT: |
||||||
|
default: |
||||||
|
return Bubble.TailDirection.LEFT; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.fr.design.mainframe.guide.tip; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import java.awt.Point; |
||||||
|
|
||||||
|
public interface GuideTip { |
||||||
|
enum Direction { |
||||||
|
TOP, BOTTOM, LEFT, RIGHT |
||||||
|
} |
||||||
|
|
||||||
|
JComponent getTip(); |
||||||
|
|
||||||
|
void setAnchor(Point anchor); |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui; |
||||||
|
|
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.layout.VerticalFlowLayout; |
||||||
|
import com.fr.design.mainframe.guide.ui.bubble.Bubble; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
public class BubbleHint extends Bubble { |
||||||
|
private UIButton confirmButton; |
||||||
|
private static final Color CONTENT_TEXT_COLOR = new Color(51, 51, 52); |
||||||
|
|
||||||
|
public BubbleHint() { |
||||||
|
super(TailDirection.TOP, 0.9f); |
||||||
|
initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponent() { |
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
this.setBorder(BorderFactory.createEmptyBorder(10 + Bubble.TAIL_HEIGHT, 15, 10, 15)); |
||||||
|
this.setPreferredSize(new Dimension(220, 140)); |
||||||
|
|
||||||
|
JPanel contentPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(false, VerticalFlowLayout.CENTER, 0, 0); |
||||||
|
contentPane.setOpaque(false); |
||||||
|
|
||||||
|
UILabel title = new UILabel(Toolkit.i18nText("Fine-Design_Guide_Tips_Title")); |
||||||
|
title.setFont(title.getFont().deriveFont(16.0f)); |
||||||
|
title.setForeground(new Color(62, 155, 249)); |
||||||
|
title.setPreferredSize(new Dimension(190, 30)); |
||||||
|
title.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
title.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); |
||||||
|
|
||||||
|
UILabel content1 = createContentLabel(Toolkit.i18nText("Fine-Design_Guide_Tips_Content1")); |
||||||
|
|
||||||
|
UILabel content2 = createContentLabel(Toolkit.i18nText("Fine-Design_Guide_Tips_Content2")); |
||||||
|
|
||||||
|
JPanel buttonContainer= FRGUIPaneFactory.createCenterFlowZeroGapBorderPane(); |
||||||
|
buttonContainer.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); |
||||||
|
buttonContainer.setPreferredSize(new Dimension(190,40)); |
||||||
|
buttonContainer.setOpaque(false); |
||||||
|
|
||||||
|
confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Guide_Tips_Know")) { |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
return new Dimension(78, 24); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
buttonContainer.add(confirmButton); |
||||||
|
|
||||||
|
contentPane.add(title); |
||||||
|
contentPane.add(content1); |
||||||
|
contentPane.add(content2); |
||||||
|
contentPane.add(buttonContainer); |
||||||
|
|
||||||
|
this.add(contentPane, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
public void addConfirmAction(ActionListener listener) { |
||||||
|
confirmButton.addActionListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
public void removeConfirmAction(ActionListener listener) { |
||||||
|
confirmButton.removeActionListener(listener); |
||||||
|
} |
||||||
|
|
||||||
|
private UILabel createContentLabel(String text) { |
||||||
|
UILabel content = new UILabel(text); |
||||||
|
content.setPreferredSize(new Dimension(190,20)); |
||||||
|
content.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
content.setFont(content.getFont().deriveFont(14.0f)); |
||||||
|
content.setForeground(CONTENT_TEXT_COLOR); |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui; |
||||||
|
|
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.layout.VerticalFlowLayout; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
public class ExpandPane extends JPanel { |
||||||
|
private static final Icon downIcon = IOUtils.readIcon("/com/fr/design/mainframe/guide/arrow_down.png"); |
||||||
|
private static final Icon rightIcon = IOUtils.readIcon("/com/fr/design/mainframe/guide/arrow_right.png"); |
||||||
|
private String title; |
||||||
|
private boolean expand; |
||||||
|
private UILabel arrow; |
||||||
|
private JPanel headerPane; |
||||||
|
private JPanel contentPane; |
||||||
|
|
||||||
|
public ExpandPane(String title, JPanel contentPane) { |
||||||
|
this.title = title; |
||||||
|
this.expand = true; |
||||||
|
this.contentPane = contentPane; |
||||||
|
initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponent() { |
||||||
|
VerticalFlowLayout layout = new VerticalFlowLayout(VerticalFlowLayout.TOP, 10, 0); |
||||||
|
layout.setAlignLeft(true); |
||||||
|
this.setLayout(layout); |
||||||
|
|
||||||
|
JPanel headerPane = createHeader(); |
||||||
|
this.add(headerPane); |
||||||
|
this.add(contentPane); |
||||||
|
headerPane.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
setExpand(!expand); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createHeader() { |
||||||
|
headerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
headerPane.setPreferredSize(new Dimension(200, 16)); |
||||||
|
UILabel headerTitle = new UILabel(this.title); |
||||||
|
headerTitle.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); |
||||||
|
arrow = new UILabel(downIcon); |
||||||
|
arrow.setOpaque(false); |
||||||
|
arrow.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||||
|
headerPane.add(headerTitle, BorderLayout.CENTER); |
||||||
|
headerPane.add(arrow, BorderLayout.WEST); |
||||||
|
return headerPane; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setPreferredSize(Dimension preferredSize) { |
||||||
|
super.setPreferredSize(preferredSize); |
||||||
|
headerPane.setPreferredSize(new Dimension(preferredSize.width, headerPane.getPreferredSize().height)); |
||||||
|
contentPane.setPreferredSize(new Dimension(preferredSize.width, contentPane.getPreferredSize().height)); |
||||||
|
} |
||||||
|
|
||||||
|
public void setExpand(boolean isExpand) { |
||||||
|
this.expand = isExpand; |
||||||
|
arrow.setIcon(isExpand ? downIcon : rightIcon); |
||||||
|
contentPane.setVisible(isExpand); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isExpand() { |
||||||
|
return expand; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,151 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui; |
||||||
|
|
||||||
|
import com.fr.design.gui.frpane.UITextPane; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.ibutton.UIButtonUI; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.utils.gui.GUIPaintUtils; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JTextPane; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import javax.swing.text.MutableAttributeSet; |
||||||
|
import javax.swing.text.SimpleAttributeSet; |
||||||
|
import javax.swing.text.StyleConstants; |
||||||
|
import javax.swing.text.StyledDocument; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Font; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
public class GuideCompleteDialog extends JDialog { |
||||||
|
private static final int DIALOG_WIDTH = 340; |
||||||
|
private static final int DIALOG_HEIGHT = 367; |
||||||
|
private static final Icon SUCCESS_ICON = IOUtils.readIcon("/com/fr/design/mainframe/guide/success.png"); |
||||||
|
private static final int ICON_HEIGHT = 182; |
||||||
|
private static final Color FONT_COLOR = new Color(51, 51, 52); |
||||||
|
private static final Color BUTTON_BG_COLOR = new Color(65, 155,249); |
||||||
|
private static final Font TITLE_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 22); |
||||||
|
private static final Font CONTENT_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 18); |
||||||
|
private static final Font BUTTON_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 14); |
||||||
|
private static GuideCompleteDialog dialog; |
||||||
|
|
||||||
|
public static GuideCompleteDialog getInstance() { |
||||||
|
if (dialog == null) { |
||||||
|
dialog = new GuideCompleteDialog(DesignerContext.getDesignerFrame()); |
||||||
|
} |
||||||
|
dialog = new GuideCompleteDialog(DesignerContext.getDesignerFrame()); |
||||||
|
return dialog; |
||||||
|
} |
||||||
|
|
||||||
|
private UITextPane textArea; |
||||||
|
|
||||||
|
public GuideCompleteDialog(Window window) { |
||||||
|
super(window); |
||||||
|
setSize(DIALOG_WIDTH, DIALOG_HEIGHT); |
||||||
|
setUndecorated(true); |
||||||
|
setModal(true); |
||||||
|
setLocationRelativeTo(window); |
||||||
|
this.setContentPane(getContentPane()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Container getContentPane() { |
||||||
|
JPanel contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
|
||||||
|
UILabel completeImage = new UILabel(SUCCESS_ICON); |
||||||
|
completeImage.setPreferredSize(new Dimension(DIALOG_WIDTH, ICON_HEIGHT)); |
||||||
|
|
||||||
|
JPanel container = new JPanel(new BorderLayout(0, 10)); |
||||||
|
container.setBorder(BorderFactory.createEmptyBorder(15, 52, 25, 52)); |
||||||
|
|
||||||
|
UILabel title = new UILabel(Toolkit.i18nText("Fine-Design_Guide_Complete_Confirm")); |
||||||
|
title.setFont(TITLE_FONT); |
||||||
|
title.setPreferredSize(new Dimension(190, 30)); |
||||||
|
title.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
title.setForeground(FONT_COLOR); |
||||||
|
|
||||||
|
textArea = new UITextPane(); |
||||||
|
changeLineSpacing(textArea, 0.15f,false); |
||||||
|
textArea.setEnabled(false); |
||||||
|
textArea.setOpaque(false); |
||||||
|
textArea.setFont(CONTENT_FONT); |
||||||
|
textArea.setPreferredSize(new Dimension(236, 52)); |
||||||
|
textArea.setBorder(null); |
||||||
|
textArea.setDisabledTextColor(FONT_COLOR); |
||||||
|
StyledDocument doc = textArea.getStyledDocument(); |
||||||
|
SimpleAttributeSet center = new SimpleAttributeSet(); |
||||||
|
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER); |
||||||
|
doc.setParagraphAttributes(0, doc.getLength(), center, false); |
||||||
|
|
||||||
|
JPanel buttonContainer= FRGUIPaneFactory.createCenterFlowZeroGapBorderPane(); |
||||||
|
buttonContainer.setPreferredSize(new Dimension(190,43)); |
||||||
|
buttonContainer.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); |
||||||
|
buttonContainer.setOpaque(false); |
||||||
|
|
||||||
|
UIButton button = new UIButton(Toolkit.i18nText("Fine-Design_Guide_Complete_End")){ |
||||||
|
@Override |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
return new Dimension(122, 38); |
||||||
|
} |
||||||
|
}; |
||||||
|
button.setFont(BUTTON_FONT); |
||||||
|
button.setUI(confirmButtonUI); |
||||||
|
button.setRoundBorder(true); |
||||||
|
button.setForeground(Color.WHITE); |
||||||
|
button.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
setVisible(false); |
||||||
|
dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
buttonContainer.add(button); |
||||||
|
container.add(title, BorderLayout.NORTH); |
||||||
|
container.add(textArea, BorderLayout.CENTER); |
||||||
|
container.add(buttonContainer,BorderLayout.SOUTH); |
||||||
|
|
||||||
|
contentPane.add(completeImage, BorderLayout.NORTH); |
||||||
|
contentPane.add(container, BorderLayout.CENTER); |
||||||
|
|
||||||
|
return contentPane; |
||||||
|
} |
||||||
|
|
||||||
|
public void showDialog(String str) { |
||||||
|
textArea.setText(str); |
||||||
|
repaint(); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
private UIButtonUI confirmButtonUI = new UIButtonUI() { |
||||||
|
protected void doExtraPainting(UIButton b, Graphics2D g2d, int w, int h, String selectedRoles) { |
||||||
|
if (isPressed(b) && b.isPressedPainted()) { |
||||||
|
GUIPaintUtils.fillPressed(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), BUTTON_BG_COLOR); |
||||||
|
} else if (isRollOver(b)) { |
||||||
|
GUIPaintUtils.fillRollOver(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), BUTTON_BG_COLOR); |
||||||
|
} else if (b.isNormalPainted()) { |
||||||
|
GUIPaintUtils.fillNormal(g2d, 0, 0, w, h, b.isRoundBorder(), b.getRectDirection(), b.isDoneAuthorityEdited(selectedRoles), b.isPressedPainted(), BUTTON_BG_COLOR); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private void changeLineSpacing(JTextPane pane, float factor, boolean replace) { |
||||||
|
pane.selectAll(); |
||||||
|
MutableAttributeSet set = new SimpleAttributeSet(pane.getParagraphAttributes()); |
||||||
|
StyleConstants.setLineSpacing(set, factor); |
||||||
|
pane.setParagraphAttributes(set, replace); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui; |
||||||
|
|
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Font; |
||||||
|
import java.awt.GridBagConstraints; |
||||||
|
import java.awt.GridBagLayout; |
||||||
|
|
||||||
|
public class GuideLoadingGlassPane extends JPanel { |
||||||
|
private static GuideLoadingGlassPane loadingPane; |
||||||
|
|
||||||
|
public static GuideLoadingGlassPane getInstance() { |
||||||
|
if (loadingPane == null) { |
||||||
|
loadingPane = new GuideLoadingGlassPane(); |
||||||
|
} |
||||||
|
return loadingPane; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideLoadingGlassPane() { |
||||||
|
this.setLayout(new GridBagLayout()); |
||||||
|
this.setOpaque(false); |
||||||
|
initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
public void initComponent() { |
||||||
|
JPanel loadingView = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
loadingView.setOpaque(false); |
||||||
|
loadingView.setPreferredSize(new Dimension(150, 90)); |
||||||
|
|
||||||
|
JPanel imageContainer = FRGUIPaneFactory.createCenterFlowInnerContainer_S_Pane(); |
||||||
|
imageContainer.setOpaque(false); |
||||||
|
|
||||||
|
imageContainer.add(GuideLoadingPane.getInstance()); |
||||||
|
|
||||||
|
UILabel hintLabel = new UILabel(Toolkit.i18nText("Fine-Design_Guide_Loading_Wait")); |
||||||
|
hintLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 14)); |
||||||
|
hintLabel.setHorizontalAlignment(SwingConstants.CENTER); |
||||||
|
hintLabel.setForeground(Color.WHITE); |
||||||
|
|
||||||
|
loadingView.add(imageContainer, BorderLayout.NORTH); |
||||||
|
loadingView.add(hintLabel, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
this.add(loadingView, new GridBagConstraints()); |
||||||
|
} |
||||||
|
|
||||||
|
public void startLoading() { |
||||||
|
GuideLoadingPane.getInstance().start(); |
||||||
|
} |
||||||
|
|
||||||
|
public void stopLoading() { |
||||||
|
GuideLoadingPane.getInstance().stop(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.Timer; |
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.BasicStroke; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Composite; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Image; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.RenderingHints; |
||||||
|
import java.awt.Stroke; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by kerry on 2020-10-23 |
||||||
|
*/ |
||||||
|
public class GuideLoadingPane extends JPanel { |
||||||
|
private static final BasicStroke STROKE = new BasicStroke(4); |
||||||
|
private static final int FPS = 30; |
||||||
|
private static final int START_ANGLE = 90; |
||||||
|
private static GuideLoadingPane imagePanel; |
||||||
|
private Image image; |
||||||
|
private int angle; |
||||||
|
private Timer timer; |
||||||
|
|
||||||
|
public static GuideLoadingPane getInstance() { |
||||||
|
if (imagePanel == null) { |
||||||
|
imagePanel = new GuideLoadingPane(); |
||||||
|
} |
||||||
|
return imagePanel; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideLoadingPane() { |
||||||
|
this.setOpaque(false); |
||||||
|
this.setPreferredSize(new Dimension(50, 50)); |
||||||
|
timer = new Timer(1000 / FPS, new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
repaint(); |
||||||
|
angle -= 180 / FPS; // 5 degrees per 100 ms = 50 degrees/second
|
||||||
|
if (angle > -270) { |
||||||
|
angle += 2 * 360; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void start() { |
||||||
|
angle = START_ANGLE; |
||||||
|
timer.start(); |
||||||
|
} |
||||||
|
|
||||||
|
public void stop() { |
||||||
|
timer.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paintComponent(Graphics g) { |
||||||
|
super.paintComponent(g); |
||||||
|
Graphics2D g2 = (Graphics2D) g; |
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||||
|
|
||||||
|
Composite oldComposite = g2.getComposite(); |
||||||
|
Stroke oldStroke = g2.getStroke(); |
||||||
|
|
||||||
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); |
||||||
|
|
||||||
|
int d = Math.min(getWidth(), getHeight()); |
||||||
|
int r = d / 2; |
||||||
|
Point circlePoint = new Point(getWidth() / 2, getHeight() / 2); |
||||||
|
|
||||||
|
g2.setColor(Color.WHITE); |
||||||
|
g2.fillOval(circlePoint.x - r, circlePoint.y - r, d, d); |
||||||
|
|
||||||
|
g2.setColor(Color.BLACK); |
||||||
|
int waitCircleD = d / 10; |
||||||
|
int waitCircleR = waitCircleD / 2; |
||||||
|
|
||||||
|
g2.fillOval(circlePoint.x - r / 3 - waitCircleR, circlePoint.y - waitCircleR, waitCircleD, waitCircleD); |
||||||
|
g2.fillOval(circlePoint.x - waitCircleR, circlePoint.y - waitCircleR, waitCircleD, waitCircleD); |
||||||
|
g2.fillOval(circlePoint.x + r / 3 - waitCircleR, circlePoint.y - waitCircleR, waitCircleD, waitCircleD); |
||||||
|
|
||||||
|
|
||||||
|
g2.setStroke(STROKE); |
||||||
|
g2.setColor(Color.decode("#419BF9")); |
||||||
|
|
||||||
|
int lineWidth = (int) STROKE.getLineWidth(); |
||||||
|
g2.drawArc(circlePoint.x - r + lineWidth / 2 , circlePoint.y - r + lineWidth / 2, d - lineWidth, d - lineWidth, angle, -90); |
||||||
|
|
||||||
|
g2.setStroke(oldStroke); |
||||||
|
g2.setComposite(oldComposite); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,233 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.base.svg.IconUtils; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
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.mainframe.DesignerContext; |
||||||
|
import com.fr.design.mainframe.guide.base.Guide; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideGroup; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideManager; |
||||||
|
import com.fr.design.mainframe.guide.base.GuideVersion; |
||||||
|
import com.fr.design.mainframe.guide.collect.GuideCollector; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JDialog; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.border.Border; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.event.ActionEvent; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
|
||||||
|
public class GuideManageDialog extends JDialog { |
||||||
|
private static final int DEFAULT_HEIGHT = 400; |
||||||
|
private static final int DEFAULT_WIDTH = 600; |
||||||
|
private static final Icon GROUP_COMPLETE_NONE = IconUtils.readIcon("/com/fr/design/mainframe/guide/complete_none.svg"); |
||||||
|
private static final Icon GROUP_COMPLETE_SOME = IconUtils.readIcon("/com/fr/design/mainframe/guide/complete_some.svg"); |
||||||
|
private static final Icon GROUP_COMPLETE_ALL = IconUtils.readIcon("/com/fr/design/mainframe/guide/complete_all.svg"); |
||||||
|
private static final Color BORDER_COLOR = new Color(224, 224, 225); |
||||||
|
private static final Color UNCOMPLETE_FONT_COLOR = new Color(51, 51, 52); |
||||||
|
private static final Color COMPLETE_FONT_COLOR = new Color(51,51,52,128); |
||||||
|
private static GuideManageDialog dialog; |
||||||
|
|
||||||
|
private JPanel scrollContent; |
||||||
|
|
||||||
|
public static GuideManageDialog getInstance() { |
||||||
|
if (dialog == null) { |
||||||
|
dialog = new GuideManageDialog(DesignerContext.getDesignerFrame()); |
||||||
|
} |
||||||
|
return dialog; |
||||||
|
} |
||||||
|
|
||||||
|
public GuideManageDialog(Window parent) { |
||||||
|
super(parent); |
||||||
|
GuideCollector.getInstance().load(); |
||||||
|
initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponent() { |
||||||
|
this.setTitle(Toolkit.i18nText("Fine-Design_Guide_Manager_Dialog_Title")); |
||||||
|
setResizable(false); |
||||||
|
setModal(true); |
||||||
|
setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
JPanel contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
contentPane.add(createContentPanel(), BorderLayout.CENTER); |
||||||
|
contentPane.add(createActionsPane(), BorderLayout.SOUTH); |
||||||
|
setContentPane(contentPane); |
||||||
|
} |
||||||
|
|
||||||
|
private UIScrollPane createContentPanel() { |
||||||
|
scrollContent = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 5); |
||||||
|
UIScrollPane scrollPane = new UIScrollPane(scrollContent); |
||||||
|
return scrollPane; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createGuideVersionPane(GuideVersion guideVersion) { |
||||||
|
JPanel expandContent = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 10); |
||||||
|
for (GuideGroup guideGroup : guideVersion.getGuideGroupList()) { |
||||||
|
JPanel guideGroupCard = createGuideGroupCard(guideGroup); |
||||||
|
expandContent.add(guideGroupCard); |
||||||
|
} |
||||||
|
ExpandPane expandPane = new ExpandPane(Toolkit.i18nText("Fine-Design_Guide_Manager_Dialog_Version_Title", guideVersion.getVersion()), expandContent); |
||||||
|
return expandPane; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private JPanel createGuideGroupCard(GuideGroup guideGroup) { |
||||||
|
JPanel card = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
card.setBorder(BorderFactory.createLineBorder(BORDER_COLOR)); |
||||||
|
card.setBackground(Color.WHITE); |
||||||
|
|
||||||
|
JPanel cardContainer = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
cardContainer.setOpaque(false); |
||||||
|
cardContainer.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); |
||||||
|
card.add(cardContainer, BorderLayout.CENTER); |
||||||
|
|
||||||
|
cardContainer.add(createGroupTitlePane(guideGroup), BorderLayout.NORTH); |
||||||
|
cardContainer.add(createGroupContentPane(guideGroup), BorderLayout.CENTER); |
||||||
|
return card; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createGroupTitlePane(GuideGroup guideGroup) { |
||||||
|
JPanel titleContainer = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.CENTER, 0, 0); |
||||||
|
titleContainer.setBorder(getBottomBorder()); |
||||||
|
titleContainer.setOpaque(false); |
||||||
|
|
||||||
|
titleContainer.setPreferredSize(new Dimension(500, 40)); |
||||||
|
JPanel titlePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(0, 8,0 ); |
||||||
|
titlePane.setOpaque(false); |
||||||
|
|
||||||
|
UILabel iconLabel = new UILabel(getGroupStateIcon(guideGroup)); |
||||||
|
iconLabel.setPreferredSize(new Dimension(16, 16)); |
||||||
|
iconLabel.setOpaque(false); |
||||||
|
|
||||||
|
UILabel titleLabel = new UILabel(guideGroup.getName()); |
||||||
|
titleLabel.setPreferredSize(new Dimension(200, 16)); |
||||||
|
titleLabel.setForeground(new Color(65, 155, 249)); |
||||||
|
titleLabel.setOpaque(false); |
||||||
|
|
||||||
|
titlePane.add(iconLabel); |
||||||
|
titlePane.add(titleLabel); |
||||||
|
titleContainer.add(titlePane); |
||||||
|
return titleContainer; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createGroupContentPane(GuideGroup guideGroup) { |
||||||
|
JPanel groupContainer = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); |
||||||
|
groupContainer.setBorder(BorderFactory.createEmptyBorder(0,10,0,0)); |
||||||
|
groupContainer.setOpaque(false); |
||||||
|
for (int index = 0; index < guideGroup.getGuideList().size(); index++) { |
||||||
|
JPanel guidePane = createGuidePane(guideGroup.getGuideList().get(index), index); |
||||||
|
if (index != guideGroup.getGuideList().size() - 1) { |
||||||
|
guidePane.setBorder(getBottomBorder()); |
||||||
|
} |
||||||
|
groupContainer.add(guidePane); |
||||||
|
} |
||||||
|
return groupContainer; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private JPanel createGuidePane(Guide guide, int index) { |
||||||
|
JPanel guidePaneContainer = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.CENTER, 0, 0); |
||||||
|
guidePaneContainer.setPreferredSize(new Dimension(540, 40)); |
||||||
|
guidePaneContainer.setOpaque(false); |
||||||
|
|
||||||
|
JPanel guidePane = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(0, 50,0 ); |
||||||
|
guidePane.setOpaque(false); |
||||||
|
|
||||||
|
UILabel title = new UILabel((index + 1) + "、" + guide.getDescription()); |
||||||
|
title.setPreferredSize(new Dimension(440, 20)); |
||||||
|
title.setForeground(guide.isComplete() ? COMPLETE_FONT_COLOR : UNCOMPLETE_FONT_COLOR); |
||||||
|
title.setOpaque(false); |
||||||
|
|
||||||
|
|
||||||
|
UIButton button = new UIButton(guide.isComplete() ? Toolkit.i18nText("Fine-Design_Guide_Manager_Dialog_Retry") : Toolkit.i18nText("Fine-Design_Guide_Manager_Dialog_Show")); |
||||||
|
button.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
setVisible(false); |
||||||
|
guide.go(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
button.setPreferredSize(new Dimension(48, 20)); |
||||||
|
guidePane.add(title); |
||||||
|
guidePane.add(button); |
||||||
|
|
||||||
|
guidePaneContainer.add(guidePane); |
||||||
|
return guidePaneContainer; |
||||||
|
} |
||||||
|
|
||||||
|
private Border getBottomBorder() { |
||||||
|
return BorderFactory.createMatteBorder( |
||||||
|
0,0,1, 0, BORDER_COLOR |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public void showDialog() { |
||||||
|
resetScrollContent(); |
||||||
|
this.setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
private Icon getGroupStateIcon(GuideGroup guideGroup) { |
||||||
|
if (guideGroup.isCompleteAll()) { |
||||||
|
return GROUP_COMPLETE_ALL; |
||||||
|
} else if (guideGroup.isCompleteSome()) { |
||||||
|
return GROUP_COMPLETE_SOME; |
||||||
|
} else { |
||||||
|
return GROUP_COMPLETE_NONE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private UIButton createCompleteAllButton() { |
||||||
|
UIButton button = new UIButton(Toolkit.i18nText(Toolkit.i18nText("Fine-Design_Guide_Manager_Dialog_Complete_All"))); |
||||||
|
button.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
GuideManager.getInstance().completeAll(); |
||||||
|
resetScrollContent(); |
||||||
|
} |
||||||
|
}); |
||||||
|
return button; |
||||||
|
} |
||||||
|
|
||||||
|
private UIButton createCloseButton() { |
||||||
|
UIButton button = new UIButton(Toolkit.i18nText(Toolkit.i18nText("Fine-Design_Guide_Manager_Dialog_Close"))); |
||||||
|
button.addActionListener(new ActionListener() { |
||||||
|
@Override |
||||||
|
public void actionPerformed(ActionEvent e) { |
||||||
|
setVisible(false); |
||||||
|
} |
||||||
|
}); |
||||||
|
return button; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createActionsPane() { |
||||||
|
JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
container.setBorder(BorderFactory.createEmptyBorder(7, 10, 7, 10)); |
||||||
|
container.add(createCompleteAllButton(), BorderLayout.WEST); |
||||||
|
container.add(createCloseButton(), BorderLayout.EAST); |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
private void resetScrollContent() { |
||||||
|
scrollContent.removeAll(); |
||||||
|
for (GuideVersion guideVersion : GuideManager.getInstance().getGuideVersionList()) { |
||||||
|
scrollContent.add(createGuideVersionPane(guideVersion)); |
||||||
|
} |
||||||
|
revalidate(); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,152 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui.bubble; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
|
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Composite; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Rectangle; |
||||||
|
|
||||||
|
public class Bubble extends JComponent { |
||||||
|
public enum TailDirection { |
||||||
|
TOP, BOTTOM, LEFT, RIGHT |
||||||
|
} |
||||||
|
protected static final int TAIL_HEIGHT = 7; |
||||||
|
protected static final int TAIL_WIDTH = 14; |
||||||
|
protected static final int BUBBLE_WIDTH = 170; |
||||||
|
protected static final Color BG_COLOR = new Color(240,240,241); |
||||||
|
protected static final int BORDER_RADIUS = 10; |
||||||
|
|
||||||
|
|
||||||
|
private TailDirection tailDirection; |
||||||
|
private float tailStart; |
||||||
|
|
||||||
|
public Bubble() { |
||||||
|
this(TailDirection.LEFT, 0.5f); |
||||||
|
} |
||||||
|
|
||||||
|
public Bubble(TailDirection tailDirection, float tailStart) { |
||||||
|
this.tailDirection = tailDirection; |
||||||
|
this.tailStart = tailStart; |
||||||
|
this.setOpaque(false); |
||||||
|
} |
||||||
|
|
||||||
|
public void setTailDirection(TailDirection tailDirection) { |
||||||
|
this.tailDirection = tailDirection; |
||||||
|
} |
||||||
|
|
||||||
|
public TailDirection getTailDirection() { |
||||||
|
return tailDirection; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTailStart(float tailStart) { |
||||||
|
this.tailStart = tailStart; |
||||||
|
} |
||||||
|
|
||||||
|
public float getTailStart() { |
||||||
|
return tailStart; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
if (isPreferredSizeSet()) { |
||||||
|
return super.getPreferredSize(); |
||||||
|
} |
||||||
|
return new Dimension(getDefaultWidth(), getDefaultHeight()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
protected int getDefaultWidth() { |
||||||
|
return (isTailHorizontal() ? TAIL_HEIGHT : 0) + BUBBLE_WIDTH; |
||||||
|
} |
||||||
|
|
||||||
|
protected int getDefaultHeight() { |
||||||
|
return (isTailVertical() ? TAIL_HEIGHT : 0); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2 = (Graphics2D) g; |
||||||
|
Composite oldComposite = g2.getComposite(); |
||||||
|
paintBubbleBg(g2); |
||||||
|
g2.setComposite(oldComposite); |
||||||
|
super.paintComponent(g); |
||||||
|
} |
||||||
|
|
||||||
|
protected void paintBubbleBg(Graphics2D g2) { |
||||||
|
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); |
||||||
|
paintRectBg(g2); |
||||||
|
paintTailBg(g2); |
||||||
|
} |
||||||
|
|
||||||
|
protected void paintRectBg(Graphics2D g2) { |
||||||
|
g2.setColor(BG_COLOR); |
||||||
|
Rectangle bounds = getRectBounds(); |
||||||
|
g2.fillRoundRect(bounds.x, bounds.y, bounds.width, bounds.height, BORDER_RADIUS, BORDER_RADIUS); |
||||||
|
} |
||||||
|
|
||||||
|
protected void paintTailBg(Graphics2D g2) { |
||||||
|
int width = getWidth(); |
||||||
|
int height = getHeight(); |
||||||
|
if (isTailVertical()) { |
||||||
|
int tailX = (int) (width * tailStart); |
||||||
|
int startX = Math.max(tailX - TAIL_WIDTH / 2, 0); |
||||||
|
int endX = startX + TAIL_WIDTH; |
||||||
|
int[] xPoints = {startX, endX, tailX}; |
||||||
|
int[] yPoints = isTailTop() ? new int[]{TAIL_HEIGHT, TAIL_HEIGHT, 0} : new int[]{height - TAIL_HEIGHT, height - TAIL_HEIGHT, height}; |
||||||
|
g2.fillPolygon(xPoints, yPoints, 3); |
||||||
|
} else if (isTailHorizontal()) { |
||||||
|
int tailY = (int) (height * tailStart); |
||||||
|
int startY = Math.max(tailY - TAIL_WIDTH / 2, 0); |
||||||
|
int endY = startY + TAIL_WIDTH; |
||||||
|
int[] xPoints = isTailLeft() ? new int[]{TAIL_HEIGHT, TAIL_HEIGHT, 0} : new int[]{width - TAIL_HEIGHT, width - TAIL_HEIGHT, width}; |
||||||
|
int[] yPoints = {startY, endY, tailY}; |
||||||
|
g2.fillPolygon(xPoints, yPoints, 3); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Rectangle getRectBounds() { |
||||||
|
int width = getWidth(); |
||||||
|
int height = getHeight(); |
||||||
|
switch (tailDirection) { |
||||||
|
case TOP: |
||||||
|
return new Rectangle(0, TAIL_HEIGHT, width, height - TAIL_HEIGHT); |
||||||
|
case LEFT: |
||||||
|
return new Rectangle(TAIL_HEIGHT, 0, width - TAIL_HEIGHT, height); |
||||||
|
case RIGHT: |
||||||
|
return new Rectangle(0, 0 , width - TAIL_HEIGHT, height); |
||||||
|
case BOTTOM: |
||||||
|
return new Rectangle(0, 0, width, height - TAIL_HEIGHT); |
||||||
|
default: |
||||||
|
return new Rectangle(0,0,0,0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTailHorizontal() { |
||||||
|
return isTailLeft() || isTailRight(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTailVertical() { |
||||||
|
return isTailTop() || isTailBottom(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTailLeft() { |
||||||
|
return tailDirection == TailDirection.LEFT; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTailRight() { |
||||||
|
return tailDirection == TailDirection.RIGHT; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTailTop() { |
||||||
|
return tailDirection == TailDirection.TOP; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isTailBottom() { |
||||||
|
return tailDirection == TailDirection.BOTTOM; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,297 @@ |
|||||||
|
package com.fr.design.mainframe.guide.ui.bubble; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.base.GraphHelper; |
||||||
|
import com.fr.design.gui.frpane.UITextPane; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.general.IOUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
import javax.swing.JTextPane; |
||||||
|
import javax.swing.text.MutableAttributeSet; |
||||||
|
import javax.swing.text.SimpleAttributeSet; |
||||||
|
import javax.swing.text.StyleConstants; |
||||||
|
import javax.swing.text.StyledDocument; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Font; |
||||||
|
import java.awt.FontMetrics; |
||||||
|
import java.awt.Insets; |
||||||
|
import java.awt.LayoutManager; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.event.ActionListener; |
||||||
|
import java.awt.event.ComponentAdapter; |
||||||
|
import java.awt.event.ComponentEvent; |
||||||
|
import java.awt.font.FontRenderContext; |
||||||
|
import java.awt.font.LineBreakMeasurer; |
||||||
|
import java.awt.font.TextAttribute; |
||||||
|
import java.text.AttributedCharacterIterator; |
||||||
|
import java.text.AttributedString; |
||||||
|
|
||||||
|
public class BubbleWithClose extends Bubble { |
||||||
|
private static final Font TITLE_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 14); |
||||||
|
private static final Font CONTENT_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 12); |
||||||
|
private static final int TITLE_LINE_HEIGHT = 22; |
||||||
|
private static final int CONTENT_LINE_HEIGHT = 18; |
||||||
|
|
||||||
|
private static final Icon ICON = IOUtils.readIcon("/com/fr/design/mainframe/guide/close.png"); |
||||||
|
private static final Color TITLE_COLOR = new Color(51, 51, 52); |
||||||
|
private static final Color CONTENT_COLOR = new Color(51,51,52,128); |
||||||
|
private static final Color HIGHLIGHT_COLOR = new Color(65, 155, 249); |
||||||
|
private static final Insets DEFAULT_INSET = new Insets(15, 15, 15, 15); |
||||||
|
private static final int MIN_WIDTH = 140; |
||||||
|
private static final int MAX_WIDTH = 275; |
||||||
|
private static final int ICON_GAP = 10; |
||||||
|
private static final int ICON_SIZE = 10; |
||||||
|
private static final int TEXT_GAP = 5; |
||||||
|
|
||||||
|
|
||||||
|
private String title; |
||||||
|
private String content; |
||||||
|
private UITextPane titleTextArea; |
||||||
|
private UITextPane contentTextArea; |
||||||
|
private UIButton closeButton; |
||||||
|
private Dimension titleSize; |
||||||
|
private Dimension contentSize; |
||||||
|
|
||||||
|
public BubbleWithClose(String title, String content) { |
||||||
|
this(title, content, TailDirection.LEFT, 0.5f); |
||||||
|
} |
||||||
|
|
||||||
|
public BubbleWithClose(String title, String content, TailDirection tailDirection, float tailStart) { |
||||||
|
super(tailDirection, tailStart); |
||||||
|
this.title = title; |
||||||
|
this.content = content; |
||||||
|
this.initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getTitle() { |
||||||
|
return title; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTitle(String title) { |
||||||
|
this.title = title; |
||||||
|
} |
||||||
|
|
||||||
|
public String getContent() { |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
public void setContent(String content) { |
||||||
|
this.content = content; |
||||||
|
} |
||||||
|
|
||||||
|
protected void initComponent() { |
||||||
|
this.setLayout(getLayoutManager()); |
||||||
|
createCloseButton(); |
||||||
|
createContentPane(); |
||||||
|
this.addComponentListener(new ComponentAdapter() { |
||||||
|
@Override |
||||||
|
public void componentShown(ComponentEvent e) { |
||||||
|
super.componentShown(e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void componentResized(ComponentEvent e) { |
||||||
|
Rectangle rectBounds = getRectBounds(); |
||||||
|
|
||||||
|
closeButton.setBounds(rectBounds.x + rectBounds.width - DEFAULT_INSET.right - ICON_SIZE, rectBounds.y + 21, ICON_SIZE, ICON_SIZE); |
||||||
|
if (titleTextArea != null) { |
||||||
|
int x = rectBounds.x + DEFAULT_INSET.left; |
||||||
|
int y = rectBounds.y + DEFAULT_INSET.top; |
||||||
|
titleTextArea.setBounds(x, y, titleSize.width, titleSize.height); |
||||||
|
} |
||||||
|
|
||||||
|
if (contentTextArea != null) { |
||||||
|
int x = rectBounds.x + DEFAULT_INSET.left; |
||||||
|
int y = rectBounds.y + DEFAULT_INSET.top + (titleSize.height == 0 ? 0 : titleSize.height + getTextGap()); |
||||||
|
contentTextArea.setBounds(x,y, contentSize.width, contentSize.height); |
||||||
|
} |
||||||
|
setSize(getPreferredSize().width, getPreferredSize().height); |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void createCloseButton() { |
||||||
|
closeButton = new UIButton(); |
||||||
|
closeButton.setIcon(ICON); |
||||||
|
closeButton.set4ToolbarButton(); |
||||||
|
closeButton.setPreferredSize(new Dimension(ICON_SIZE, ICON_SIZE)); |
||||||
|
closeButton.setRolloverEnabled(false); |
||||||
|
closeButton.setPressedPainted(false); |
||||||
|
this.add(closeButton); |
||||||
|
} |
||||||
|
|
||||||
|
public void addCloseActionListener(ActionListener actionListener) { |
||||||
|
closeButton.addActionListener(actionListener); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void removeCloseActionListener(ActionListener actionListener){ |
||||||
|
closeButton.removeActionListener(actionListener); |
||||||
|
} |
||||||
|
|
||||||
|
private void createContentPane() { |
||||||
|
createTitleTextArea(); |
||||||
|
createContentTextArea(); |
||||||
|
} |
||||||
|
|
||||||
|
private void createTitleTextArea() { |
||||||
|
if (StringUtils.isNotEmpty(title)) { |
||||||
|
titleTextArea = createTextArea(title, TITLE_FONT, TITLE_LINE_HEIGHT, TITLE_COLOR); |
||||||
|
this.add(titleTextArea); |
||||||
|
} |
||||||
|
titleSize = calTextSize(titleTextArea, TITLE_LINE_HEIGHT, getTextAreaWidth(MAX_WIDTH), getTextAreaWidth(MIN_WIDTH)); |
||||||
|
} |
||||||
|
|
||||||
|
private void createContentTextArea() { |
||||||
|
if (StringUtils.isNotEmpty(content)) { |
||||||
|
contentTextArea = createTextArea(content, CONTENT_FONT, CONTENT_LINE_HEIGHT, CONTENT_COLOR); |
||||||
|
this.add(contentTextArea); |
||||||
|
} |
||||||
|
contentSize = calTextSize(contentTextArea, CONTENT_LINE_HEIGHT, getTextAreaWidth(MAX_WIDTH), getTextAreaWidth(MIN_WIDTH)); |
||||||
|
} |
||||||
|
|
||||||
|
private UITextPane createTextArea(String str, Font font, int lineHeight, Color foreground) { |
||||||
|
int lineSpace = lineHeight - font.getSize(); |
||||||
|
UITextPane textArea= new UITextPane(){ |
||||||
|
@Override |
||||||
|
public Insets getInsets() { |
||||||
|
return new Insets(lineSpace / 2 - 1, 0, lineSpace / 2, 0); |
||||||
|
} |
||||||
|
}; |
||||||
|
textArea.setFont(font); |
||||||
|
changeLineSpacing(textArea, lineHeight, true); |
||||||
|
textArea.setEditable(false); |
||||||
|
textArea.setForeground(foreground); |
||||||
|
textArea.setDisabledTextColor(foreground); |
||||||
|
textArea.setBorder(null); |
||||||
|
textArea.setOpaque(false); |
||||||
|
textArea.setText(str); |
||||||
|
highlightText(textArea); |
||||||
|
return textArea; |
||||||
|
} |
||||||
|
|
||||||
|
private void changeLineSpacing(JTextPane pane, int lineHeight, boolean replace) { |
||||||
|
pane.selectAll(); |
||||||
|
MutableAttributeSet set = new SimpleAttributeSet(pane.getParagraphAttributes()); |
||||||
|
FontMetrics fontMetrics = GraphHelper.getFontMetrics(pane.getFont()); |
||||||
|
StyleConstants.setLineSpacing(set, (float)(lineHeight - fontMetrics.getHeight()) / fontMetrics.getHeight()); |
||||||
|
pane.setParagraphAttributes(set, replace); |
||||||
|
} |
||||||
|
|
||||||
|
private void highlightText(JTextPane textArea) { |
||||||
|
SimpleAttributeSet sas = new SimpleAttributeSet(); |
||||||
|
StyleConstants.setForeground(sas, HIGHLIGHT_COLOR); |
||||||
|
StyledDocument doc = textArea.getStyledDocument(); |
||||||
|
String text = textArea.getText(); |
||||||
|
int startPos = -1; |
||||||
|
for (int i = 0; i < text.length(); i ++) { |
||||||
|
char ch = text.charAt(i); |
||||||
|
if (ch == '【') { |
||||||
|
startPos = i; |
||||||
|
} |
||||||
|
if (ch == '】') { |
||||||
|
if (startPos > 0) { |
||||||
|
try { |
||||||
|
doc.setCharacterAttributes(startPos, (i - startPos + 1), sas, false); |
||||||
|
startPos = -1; |
||||||
|
} catch (Exception e) { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getDefaultWidth() { |
||||||
|
return Math.max(titleSize.width, contentSize.width) + getHorizontalInsets() + ICON_GAP + ICON_SIZE + (isTailHorizontal() ? TAIL_HEIGHT : 0) ; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getDefaultHeight() { |
||||||
|
return titleSize.height + contentSize.height + getTextGap() + getVerticalInsets() + (isTailVertical() ? TAIL_HEIGHT : 0); |
||||||
|
} |
||||||
|
|
||||||
|
private int getTextGap() { |
||||||
|
return (titleSize.height != 0 && contentSize.height != 0) ? TEXT_GAP : 0; |
||||||
|
} |
||||||
|
|
||||||
|
private LayoutManager getLayoutManager() { |
||||||
|
return new LayoutManager() { |
||||||
|
@Override |
||||||
|
public void addLayoutComponent(String name, Component comp) { |
||||||
|
} |
||||||
|
|
||||||
|
@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) { |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private int countLines(JTextPane textArea, int max_width) { |
||||||
|
AttributedString text = new AttributedString(textArea.getText()); |
||||||
|
text.addAttribute(TextAttribute.FONT, textArea.getFont()); |
||||||
|
FontRenderContext frc = textArea.getFontMetrics(textArea.getFont()) |
||||||
|
.getFontRenderContext(); |
||||||
|
AttributedCharacterIterator charIt = text.getIterator(); |
||||||
|
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc); |
||||||
|
lineMeasurer.setPosition(charIt.getBeginIndex()); |
||||||
|
int lines = 0; |
||||||
|
while (lineMeasurer.getPosition() < charIt.getEndIndex()) { |
||||||
|
lineMeasurer.nextLayout(max_width); |
||||||
|
lines++; |
||||||
|
} |
||||||
|
return lines; |
||||||
|
} |
||||||
|
|
||||||
|
private Dimension calTextSize(JTextPane textArea, int lineHeight, int maxWidth, int minWidth) { |
||||||
|
if (textArea == null) { |
||||||
|
return new Dimension(minWidth, 0); |
||||||
|
} |
||||||
|
FontMetrics fontMetrics = GraphHelper.getFontMetrics(textArea.getFont()); |
||||||
|
int line = countLines(textArea, maxWidth); |
||||||
|
int width = maxWidth; |
||||||
|
if (line == 1) { |
||||||
|
width = Math.max(fontMetrics.stringWidth(textArea.getText()), minWidth); |
||||||
|
} |
||||||
|
int height = lineHeight * line; |
||||||
|
return new Dimension(width, height); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private int getTextAreaWidth(int bubbleWidth) { |
||||||
|
return bubbleWidth - getHorizontalInsets() - ICON_SIZE - ICON_GAP; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private int getHorizontalInsets() { |
||||||
|
return DEFAULT_INSET.left + DEFAULT_INSET.right; |
||||||
|
} |
||||||
|
|
||||||
|
private int getVerticalInsets() { |
||||||
|
return DEFAULT_INSET.top + DEFAULT_INSET.bottom; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
package com.fr.design.mainframe.guide.utils; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.AWTException; |
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Point; |
||||||
|
import java.awt.Rectangle; |
||||||
|
import java.awt.Robot; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
|
||||||
|
public class ScreenImage { |
||||||
|
|
||||||
|
public static BufferedImage createImage(JComponent component) { |
||||||
|
return ScreenImage.createImage(component, getRegion(component)); |
||||||
|
} |
||||||
|
|
||||||
|
public static BufferedImage createImage(JComponent component, Rectangle region) { |
||||||
|
if (! component.isDisplayable()) { |
||||||
|
Dimension d = component.getSize(); |
||||||
|
|
||||||
|
if (d.width == 0 || d.height == 0) { |
||||||
|
d = component.getPreferredSize(); |
||||||
|
component.setSize( d ); |
||||||
|
} |
||||||
|
|
||||||
|
layoutComponent( component ); |
||||||
|
} |
||||||
|
|
||||||
|
BufferedImage image = new BufferedImage(region.width, region.height, BufferedImage.TYPE_INT_RGB); |
||||||
|
Graphics2D g2d = image.createGraphics(); |
||||||
|
|
||||||
|
if (! component.isOpaque()) { |
||||||
|
g2d.setColor( component.getBackground() ); |
||||||
|
g2d.fillRect(region.x, region.y, region.width, region.height); |
||||||
|
} |
||||||
|
|
||||||
|
g2d.translate(-region.x, -region.y); |
||||||
|
component.print( g2d ); |
||||||
|
g2d.dispose(); |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
public static BufferedImage createImage(Component component) |
||||||
|
throws AWTException { |
||||||
|
Point p = new Point(0, 0); |
||||||
|
SwingUtilities.convertPointToScreen(p, component); |
||||||
|
Rectangle region = component.getBounds(); |
||||||
|
region.x = p.x; |
||||||
|
region.y = p.y; |
||||||
|
return ScreenImage.createImage(region); |
||||||
|
} |
||||||
|
|
||||||
|
public static BufferedImage createImageWithModal(JComponent component) { |
||||||
|
Rectangle region = getRegion(component); |
||||||
|
BufferedImage image = new BufferedImage(region.width, region.height, BufferedImage.TYPE_INT_RGB); |
||||||
|
Graphics2D g2d = image.createGraphics(); |
||||||
|
g2d.drawImage(createImage(component), 0, 0, null); |
||||||
|
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); |
||||||
|
g2d.setColor(Color.BLACK); |
||||||
|
g2d.fillRect(0, 0, region.width, region.height); |
||||||
|
g2d.dispose(); |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
public static BufferedImage createImage(Rectangle region) |
||||||
|
throws AWTException { |
||||||
|
BufferedImage image = new Robot().createScreenCapture( region ); |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
private static Rectangle getRegion(Component component) { |
||||||
|
Dimension d = component.getSize(); |
||||||
|
if (d.width == 0 || d.height == 0) { |
||||||
|
d = component.getPreferredSize(); |
||||||
|
component.setSize( d ); |
||||||
|
} |
||||||
|
return new Rectangle(0, 0, d.width, d.height); |
||||||
|
} |
||||||
|
|
||||||
|
static void layoutComponent(Component component) { |
||||||
|
synchronized (component.getTreeLock()) { |
||||||
|
component.doLayout(); |
||||||
|
|
||||||
|
if (component instanceof Container) { |
||||||
|
for (Component child : ((Container)component).getComponents()) { |
||||||
|
layoutComponent(child); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,301 @@ |
|||||||
|
package com.fr.design.mainframe.theme; |
||||||
|
|
||||||
|
import com.fr.base.theme.FineColorFlushUtils; |
||||||
|
import com.fr.base.theme.FineColorManager; |
||||||
|
import com.fr.base.theme.TemplateTheme; |
||||||
|
import com.fr.base.theme.TemplateThemeConfig; |
||||||
|
import com.fr.base.theme.settings.ThemedCellStyleList; |
||||||
|
import com.fr.base.theme.settings.ThemedColorScheme; |
||||||
|
import com.fr.design.designer.IntervalConstants; |
||||||
|
import com.fr.design.gui.frpane.AbstractAttrNoScrollPane; |
||||||
|
import com.fr.design.gui.frpane.AttributeChangeListener; |
||||||
|
import com.fr.design.gui.frpane.UITabbedPane; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.layout.TableLayout; |
||||||
|
import com.fr.design.layout.TableLayoutHelper; |
||||||
|
import com.fr.design.mainframe.theme.edit.CellStyleListEditPane; |
||||||
|
import com.fr.design.mainframe.theme.edit.ChartStyleEditPane; |
||||||
|
import com.fr.design.mainframe.theme.edit.ui.ColorListExtendedPane; |
||||||
|
import com.fr.design.mainframe.theme.edit.ui.ColorListPane; |
||||||
|
import com.fr.design.mainframe.theme.edit.ui.LabelUtils; |
||||||
|
import com.fr.design.mainframe.theme.ui.AutoCheckTextField; |
||||||
|
import com.fr.design.mainframe.theme.ui.AutoCheckThemeNameTextField; |
||||||
|
import com.fr.design.mainframe.theme.ui.BorderUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.event.ChangeEvent; |
||||||
|
import javax.swing.event.ChangeListener; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Starryi |
||||||
|
* @version 1.0 |
||||||
|
* Created by Starryi on 2021/8/13 |
||||||
|
*/ |
||||||
|
public abstract class TemplateThemeEditorPane<T extends TemplateTheme> extends JPanel { |
||||||
|
public static final int LEFT_TITLE_PANE_HEIGHT = 539; |
||||||
|
|
||||||
|
public static final int RIGHT_PANE_WIDTH = 362; |
||||||
|
public static final int RIGHT_PANE_HEIGHT = LEFT_TITLE_PANE_HEIGHT; |
||||||
|
|
||||||
|
protected AutoCheckThemeNameTextField<T> nameTextField; |
||||||
|
private UILabel nameErrorLabel; |
||||||
|
protected ColorListPane colorListPane; |
||||||
|
protected CellStyleListEditPane cellStyleSettingPane; |
||||||
|
protected ChartStyleEditPane chartStyleSettingPane; |
||||||
|
|
||||||
|
protected boolean isPopulating = false; |
||||||
|
protected UITabbedPane uiTabbedPane; |
||||||
|
|
||||||
|
private final TemplateThemeConfig<T> config; |
||||||
|
|
||||||
|
private boolean refreshingThemedColor = false; |
||||||
|
private T theme; |
||||||
|
|
||||||
|
private AttributeChangeListener changeListener; |
||||||
|
private AutoCheckTextField.CheckListener themeNameCheckListener; |
||||||
|
|
||||||
|
public TemplateThemeEditorPane(TemplateThemeConfig<T> config) { |
||||||
|
super(); |
||||||
|
this.config = config; |
||||||
|
theme = config.createNewTheme(); |
||||||
|
initializePane(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initializePane() { |
||||||
|
setLayout(new BorderLayout(0, 0)); |
||||||
|
setPreferredSize(new Dimension(RIGHT_PANE_WIDTH, RIGHT_PANE_HEIGHT)); |
||||||
|
JPanel nameEditPane = createNameEditPane(); |
||||||
|
add(nameEditPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
JPanel settingPane = new JPanel(new BorderLayout(0, IntervalConstants.INTERVAL_L1)); |
||||||
|
settingPane.add(createColorSchemeEditPane(), BorderLayout.NORTH); |
||||||
|
settingPane.add(createCustomEditorsPane(), BorderLayout.CENTER); |
||||||
|
|
||||||
|
add(settingPane, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createNameEditPane() { |
||||||
|
nameErrorLabel = LabelUtils.createLabel(StringUtils.EMPTY, Color.RED); |
||||||
|
nameErrorLabel.setVisible(false); |
||||||
|
|
||||||
|
nameTextField = new AutoCheckThemeNameTextField<>(); |
||||||
|
nameTextField.setThemeConfig(config); |
||||||
|
nameTextField.setEditable(false); |
||||||
|
nameTextField.setEnabled(false); |
||||||
|
nameTextField.setPreferredSize(new Dimension(165, 20)); |
||||||
|
nameTextField.setNameCheckListener(new AutoCheckTextField.CheckListener() { |
||||||
|
@Override |
||||||
|
public void onChecked(String error, boolean valid) { |
||||||
|
nameErrorLabel.setVisible(StringUtils.isNotEmpty(error)); |
||||||
|
nameErrorLabel.setText(error); |
||||||
|
|
||||||
|
themeNameCheckListener.onChecked(error, valid); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
double p = TableLayout.PREFERRED; |
||||||
|
|
||||||
|
JPanel container = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{ |
||||||
|
new Component[] { LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Name")), nameTextField }, |
||||||
|
new Component[] { null, nameErrorLabel }, |
||||||
|
}, new double[] { 20, 20}, new double[] { p, 165}, IntervalConstants.INTERVAL_L1, IntervalConstants.INTERVAL_W0); |
||||||
|
|
||||||
|
container.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createColorSchemeEditPane() { |
||||||
|
colorListPane = new ColorListPane(); |
||||||
|
|
||||||
|
double p = TableLayout.PREFERRED; |
||||||
|
double[] rowSize = new double[]{p, p, p}; |
||||||
|
double[] columnSize = {p, p}; |
||||||
|
|
||||||
|
JPanel colorListContainerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
colorListContainerPane.add(colorListPane, BorderLayout.WEST); |
||||||
|
|
||||||
|
UILabel tipLabel = LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Edit_Tip"), new Color(153, 153, 153)); |
||||||
|
|
||||||
|
JPanel content = TableLayoutHelper.createGapTableLayoutPane(new JComponent[][]{ |
||||||
|
{LabelUtils.createLabel(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Edit_Label")), colorListContainerPane}, |
||||||
|
{null, tipLabel}, |
||||||
|
}, |
||||||
|
rowSize, columnSize, 18, 7); |
||||||
|
content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
||||||
|
|
||||||
|
JPanel borderContainer = new JPanel(new BorderLayout()); |
||||||
|
borderContainer.setBorder(BorderUtils.createTitleBorder(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Color_Scheme_Title"))); |
||||||
|
borderContainer.add(content); |
||||||
|
|
||||||
|
JPanel container = new JPanel(new BorderLayout()); |
||||||
|
container.add(borderContainer, BorderLayout.CENTER); |
||||||
|
|
||||||
|
colorListPane.addColorChangeListener(new ChangeListener() { |
||||||
|
@Override |
||||||
|
public void stateChanged(ChangeEvent e) { |
||||||
|
if (refreshingThemedColor) { |
||||||
|
return; |
||||||
|
} |
||||||
|
List<Color> colors = colorListPane.update(); |
||||||
|
refreshingThemedColor = true; |
||||||
|
onColorSchemeChanged(colors); |
||||||
|
refreshingThemedColor = false; |
||||||
|
|
||||||
|
fireAttributeChange(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return container; |
||||||
|
} |
||||||
|
private void onColorSchemeChanged(List<Color> colors) { |
||||||
|
FineColorManager.FineColorReplaceByColorScheme replaceByColorScheme = new FineColorManager.FineColorReplaceByColorScheme(colors); |
||||||
|
T theme = updateBean(); |
||||||
|
FineColorFlushUtils.replaceCacheObject(theme, replaceByColorScheme); |
||||||
|
FineColorManager.traverse(theme, replaceByColorScheme); |
||||||
|
populateBean4CustomEditors(theme); |
||||||
|
//图表渐变色
|
||||||
|
chartStyleSettingPane.populateGradientBar(colors); |
||||||
|
this.repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
protected JPanel createCustomEditorsPane() { |
||||||
|
JPanel container = new JPanel(new BorderLayout()); |
||||||
|
container.setBorder(BorderUtils.createTitleBorder(Toolkit.i18nText("Fine-Design_Basic_Template_Theme_Edit_Pane_Custom_Settings_Title"))); |
||||||
|
|
||||||
|
uiTabbedPane = new UITabbedPane(); |
||||||
|
uiTabbedPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 1)); |
||||||
|
container.add(uiTabbedPane, BorderLayout.CENTER); |
||||||
|
|
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
public void addCustomEditorPane(String title, final Component component) { |
||||||
|
AbstractAttrNoScrollPane settingPane = new NoBorderAbstractAttrNoScrollPane() { |
||||||
|
@Override |
||||||
|
protected JPanel createContentPane() { |
||||||
|
JPanel contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
contentPane.add(component, BorderLayout.CENTER); |
||||||
|
return contentPane; |
||||||
|
} |
||||||
|
}; |
||||||
|
settingPane.addAttributeChangeListener(new AttributeChangeListener() { |
||||||
|
@Override |
||||||
|
public void attributeChange() { |
||||||
|
fireAttributeChange(); |
||||||
|
} |
||||||
|
}); |
||||||
|
uiTabbedPane.addTab(title, settingPane); |
||||||
|
} |
||||||
|
protected JPanel createCellStyleSettingPane() { |
||||||
|
JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
cellStyleSettingPane = new CellStyleListEditPane(); |
||||||
|
cellStyleSettingPane.registerAttrChangeListener(new AttributeChangeListener() { |
||||||
|
@Override |
||||||
|
public void attributeChange() { |
||||||
|
fireAttributeChange(); |
||||||
|
} |
||||||
|
}); |
||||||
|
container.add(cellStyleSettingPane); |
||||||
|
return container; |
||||||
|
} |
||||||
|
protected JPanel createChartStyleSettingPane() { |
||||||
|
JPanel container = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
chartStyleSettingPane = new ChartStyleEditPane(); |
||||||
|
container.add(chartStyleSettingPane); |
||||||
|
return container; |
||||||
|
} |
||||||
|
|
||||||
|
public void populateBean(T theme) { |
||||||
|
this.theme = theme; |
||||||
|
isPopulating = true; |
||||||
|
|
||||||
|
String name = theme.getName(); |
||||||
|
setThemeNameEditable(StringUtils.isEmpty(name)); |
||||||
|
|
||||||
|
nameTextField.setText(name); |
||||||
|
|
||||||
|
colorListPane.populate(theme.getColorScheme().getColors()); |
||||||
|
|
||||||
|
populateBean4CustomEditors(theme); |
||||||
|
isPopulating = false; |
||||||
|
} |
||||||
|
|
||||||
|
protected void populateBean4CustomEditors(T theme) { |
||||||
|
cellStyleSettingPane.populateBean(theme.getCellStyleList()); |
||||||
|
chartStyleSettingPane.populateBean(theme.getChartStyle()); |
||||||
|
} |
||||||
|
|
||||||
|
public T updateBean() { |
||||||
|
if (theme == null) { |
||||||
|
theme = config.createNewTheme(); |
||||||
|
} |
||||||
|
|
||||||
|
theme.setName(this.nameTextField.getText()); |
||||||
|
|
||||||
|
ThemedColorScheme colorScheme = theme.getColorScheme(); |
||||||
|
colorScheme.setColors(this.colorListPane.update()); |
||||||
|
theme.setColorScheme(colorScheme); |
||||||
|
|
||||||
|
updateBean4CustomEditors(theme); |
||||||
|
|
||||||
|
return theme; |
||||||
|
} |
||||||
|
|
||||||
|
protected void updateBean4CustomEditors(T theme) { |
||||||
|
ThemedCellStyleList cellStyleConfig = this.cellStyleSettingPane.updateBean(); |
||||||
|
theme.setCellStyleList(cellStyleConfig); |
||||||
|
|
||||||
|
theme.setChartStyle(this.chartStyleSettingPane.updateBean()); |
||||||
|
} |
||||||
|
|
||||||
|
public void setThemeNameEditable(boolean editable) { |
||||||
|
nameTextField.setEditable(editable); |
||||||
|
nameTextField.setEnabled(editable); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean checkNameValid() { |
||||||
|
return nameTextField.checkValid(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getThemeName() { |
||||||
|
return nameTextField.getText(); |
||||||
|
} |
||||||
|
|
||||||
|
public List<Color> getCurrentColorScheme() { |
||||||
|
return colorListPane.update(); |
||||||
|
} |
||||||
|
|
||||||
|
private void fireAttributeChange() { |
||||||
|
if (!isPopulating && !refreshingThemedColor && changeListener != null) { |
||||||
|
changeListener.attributeChange(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addAttributeChangeListener(AttributeChangeListener changeListener) { |
||||||
|
this.changeListener = changeListener; |
||||||
|
} |
||||||
|
public void addThemeNameCheckListener(AutoCheckTextField.CheckListener checkListener) { |
||||||
|
this.themeNameCheckListener = checkListener; |
||||||
|
} |
||||||
|
|
||||||
|
private static abstract class NoBorderAbstractAttrNoScrollPane extends AbstractAttrNoScrollPane { |
||||||
|
@Override |
||||||
|
protected void initContentPane() { |
||||||
|
super.initContentPane(); |
||||||
|
if (leftContentPane != null) { |
||||||
|
// 修正 AbstractAttrNoScrollPane 的默认行为
|
||||||
|
leftContentPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue