* commit 'c6a4b0d75d211b5ddaa2d11015879c1d9e54b30b': (58 commits) REPORT-38529 && REPORT-38527 REPORT-38534 设计器界面中的剪切和复制按钮灰化不可用 1. bug原因:对于工具栏上的工具条combineUp,当处于编辑sheet的时候,是剪切+复制+粘贴+格式刷,其它情况都是剪切+复制+粘贴+删除,而带删除的这种,是会有两种状态,选中了某个组件时,剪切、复制、删除就都是可用的,未选中时,这几个都不可用,所以这里需要有个逻辑来切换这几个按钮的状态,而对于带格式刷的,不论有没有选中都会走到这个逻辑,然后被无脑置为不可用 2. 修改方案:添加一个判断,如果当前的状态是WORK_SHEET的时候,即编辑sheet时,直接返回,其它情况正常走逻辑 REPORT-38821 修改获得扩展路径的逻辑 REPORT-38645 【JDK11】设计器右上角,fs插件管理点击登录无反应 REPORT-38889 tomcat 8.5.x 请求参数带特殊字符问题 CHART-15362 属性切换保留功能开发 REPORT-38647 && REPORT-38357 REPORT-36985 改了一点代码 REPORT-38632【无用代码删除】设计器启动页 动态画面相关代码 REPORT-36985 删除无用依赖,格式化 REPORT-36985 删除无用依赖,格式化 KERNEL-4069 jdk11 macos的jdk里面移除了windows相关的LAF REPORT-36985 删除无用依赖 REPORT-36985 代码提交 组件树删除优化和选择组件时让其浮于顶层 REPORT-36985 代码提交 组件树删除优化和选择组件时让其浮于顶层 REPORT-37821 frm绝对布局框选多个组件有问题 合丢了 REPORT-36619 fix REPORT-36619 报表设计界面拖选无法选中拖选区域 REPORT-37915 报表分栏弹窗显示未适配国际化 1. 为每种语言都适配了报表分栏弹窗中的示例图片 2. 修改对应国际化文件中代表此示例图片的文件名 CHART-15451 国际化 ...research/10.0
@ -0,0 +1,106 @@ |
|||||||
|
package com.fr.design.gui.ipasswordfield; |
||||||
|
|
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.text.Document; |
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Yvan |
||||||
|
* @version 10.0 |
||||||
|
* Created by Yvan on 2020-08-11 |
||||||
|
* 有固定长度的"*"回显的密码框,避免泄露密码长度 |
||||||
|
*/ |
||||||
|
public class UIPasswordFieldWithFixedLength extends UIPassWordField { |
||||||
|
/** |
||||||
|
* 展示密码,为固定8位长度的特殊字符"*"组成 |
||||||
|
*/ |
||||||
|
private static final String DISPLAY_PASSWORD = "********"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 实际密码 |
||||||
|
*/ |
||||||
|
private String realPassword; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于判断是否清空密码 |
||||||
|
*/ |
||||||
|
private boolean clearPassword; |
||||||
|
|
||||||
|
public UIPasswordFieldWithFixedLength() { |
||||||
|
this(null, null, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public UIPasswordFieldWithFixedLength(String text) { |
||||||
|
this(null, text, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public UIPasswordFieldWithFixedLength(int columns) { |
||||||
|
this(null, null, columns); |
||||||
|
} |
||||||
|
|
||||||
|
public UIPasswordFieldWithFixedLength(String text, int columns) { |
||||||
|
this(null, text, columns); |
||||||
|
} |
||||||
|
|
||||||
|
public UIPasswordFieldWithFixedLength(Document doc, String txt, int columns) { |
||||||
|
super(doc, txt, columns); |
||||||
|
initRealPassword(txt); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 为realPassword赋初值并添加一个鼠标单击事件 |
||||||
|
*/ |
||||||
|
public void initRealPassword(String text) { |
||||||
|
this.realPassword = text == null ? StringUtils.EMPTY : text; |
||||||
|
this.clearPassword = true; |
||||||
|
addShowFixedLengthPasswordListener(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当鼠标点击密码框,第一次做出键入动作时,清空显示密码与实际密码,用户需要重新输入密码 |
||||||
|
*/ |
||||||
|
private void addShowFixedLengthPasswordListener() { |
||||||
|
this.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
UIPasswordFieldWithFixedLength.this.clearPassword = true; |
||||||
|
} |
||||||
|
}); |
||||||
|
this.addKeyListener(new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (clearPassword) { |
||||||
|
UIPasswordFieldWithFixedLength.this.setText(StringUtils.EMPTY); |
||||||
|
UIPasswordFieldWithFixedLength.this.clearPassword = false; |
||||||
|
UIPasswordFieldWithFixedLength.this.updateUI(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setText(@NotNull String t) { |
||||||
|
this.realPassword = t; |
||||||
|
// 看到代码中有些场景是将密码置为空字符串的,所以在这里加个判断
|
||||||
|
if (StringUtils.isEmpty(t)) { |
||||||
|
super.setText(t); |
||||||
|
} else { |
||||||
|
super.setText(DISPLAY_PASSWORD); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public char[] getPassword() { |
||||||
|
//如果用户刚清空密码框,并输入了新密码,则返回输入内容,否则返回realPassword
|
||||||
|
String text = new String(super.getPassword()); |
||||||
|
if (!StringUtils.isEmpty(text) && StringUtils.isEmpty(realPassword)) { |
||||||
|
return text.toCharArray(); |
||||||
|
} |
||||||
|
return realPassword.toCharArray(); |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
@ -0,0 +1,69 @@ |
|||||||
|
package com.fr.design.chartx.data; |
||||||
|
|
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.layout.TableLayout; |
||||||
|
import com.fr.design.layout.TableLayoutHelper; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingConstants; |
||||||
|
import java.awt.Component; |
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2020/7/22 |
||||||
|
*/ |
||||||
|
public class DataLayoutHelper { |
||||||
|
|
||||||
|
public static int WIDTH = 150; |
||||||
|
public static int LABEL_HEIGHT = 20; |
||||||
|
public static int LABEL_WIDTH = 65; |
||||||
|
|
||||||
|
public static int LEFT_GAP = 15; |
||||||
|
public static int RIGHT_GAP = 10; |
||||||
|
|
||||||
|
public static void setWIDTH(int WIDTH) { |
||||||
|
DataLayoutHelper.WIDTH = WIDTH; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setLabelHeight(int labelHeight) { |
||||||
|
LABEL_HEIGHT = labelHeight; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setLabelWidth(int labelWidth) { |
||||||
|
LABEL_WIDTH = labelWidth; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setLeftGap(int leftGap) { |
||||||
|
LEFT_GAP = leftGap; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setRightGap(int rightGap) { |
||||||
|
RIGHT_GAP = rightGap; |
||||||
|
} |
||||||
|
|
||||||
|
public static JPanel createDataLayoutPane(Component[][] components) { |
||||||
|
int len = components.length; |
||||||
|
double p = TableLayout.PREFERRED; |
||||||
|
double[] columnSize = {DataLayoutHelper.LABEL_WIDTH, DataLayoutHelper.WIDTH}; |
||||||
|
double[] rowSize = new double[len]; |
||||||
|
Arrays.fill(rowSize, p); |
||||||
|
|
||||||
|
return TableLayoutHelper.createTableLayoutPane(components, rowSize, columnSize); |
||||||
|
} |
||||||
|
|
||||||
|
public static JPanel createDataLayoutPane(String label, Component component) { |
||||||
|
Component[][] components = new Component[][]{ |
||||||
|
new Component[]{new UILabel(label, SwingConstants.LEFT), component} |
||||||
|
}; |
||||||
|
|
||||||
|
return createDataLayoutPane(components); |
||||||
|
} |
||||||
|
|
||||||
|
public static void addNormalBorder(JComponent component) { |
||||||
|
component.setBorder(BorderFactory.createEmptyBorder(0, DataLayoutHelper.LEFT_GAP, 0, DataLayoutHelper.RIGHT_GAP)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
package com.fr.van.chart.designer.type; |
||||||
|
|
||||||
|
import com.fr.chart.chartglyph.ConditionCollection; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.plugin.chart.PiePlot4VanChart; |
||||||
|
import com.fr.plugin.chart.area.VanChartAreaPlot; |
||||||
|
import com.fr.plugin.chart.attr.axis.VanChartAxis; |
||||||
|
import com.fr.plugin.chart.attr.plot.VanChartPlot; |
||||||
|
import com.fr.plugin.chart.attr.plot.VanChartRectanglePlot; |
||||||
|
import com.fr.plugin.chart.bubble.VanChartBubblePlot; |
||||||
|
import com.fr.plugin.chart.column.VanChartColumnPlot; |
||||||
|
import com.fr.plugin.chart.line.VanChartLinePlot; |
||||||
|
import com.fr.plugin.chart.scatter.VanChartScatterPlot; |
||||||
|
import com.fr.plugin.chart.type.VanChartPlotType; |
||||||
|
import com.fr.plugin.chart.vanchart.VanChart; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Bjorn |
||||||
|
* @version 10.0 |
||||||
|
* Created by Bjorn on 2020-08-24 |
||||||
|
*/ |
||||||
|
public abstract class AbstractRectanglePlotPane extends AbstractVanChartTypePane { |
||||||
|
|
||||||
|
private static Set<String> extendPlotIds = new HashSet<>(); |
||||||
|
|
||||||
|
static { |
||||||
|
extendPlotIds.add(VanChartColumnPlot.VAN_CHART_COLUMN_PLOT_ID); |
||||||
|
extendPlotIds.add(VanChartColumnPlot.VAN_CHART_BAR_PLOT_ID); |
||||||
|
extendPlotIds.add(VanChartLinePlot.VAN_CHART_LINE_PLOT); |
||||||
|
extendPlotIds.add(VanChartAreaPlot.VAN_CHART_AREA_PLOT_ID); |
||||||
|
extendPlotIds.add(VanChartScatterPlot.VAN_CHART_SCATTER_PLOT_ID); |
||||||
|
extendPlotIds.add(PiePlot4VanChart.VAN_CHART_PIE_PLOT); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected VanChartPlot cloneOldPlot2New(VanChartPlot oldPlot, VanChartPlot newPlot) { |
||||||
|
try { |
||||||
|
VanChartRectanglePlot vanChartRectanglePlot = (VanChartRectanglePlot) newPlot; |
||||||
|
VanChartRectanglePlot clonePlot = (VanChartRectanglePlot) oldPlot.clone(); |
||||||
|
clonePlot.setVanChartPlotType(vanChartRectanglePlot.getVanChartPlotType()); |
||||||
|
//自定义类型的图形要增加一个y2轴,并且增加系列中的堆积条件,反之则要去掉y2轴和条件
|
||||||
|
if (clonePlot.isCustomChart()) { |
||||||
|
List<VanChartAxis> valueAxisList = clonePlot.getValueAxisList(); |
||||||
|
valueAxisList.add(vanChartRectanglePlot.getValueAxisList().get(1)); |
||||||
|
clonePlot.setStackAndAxisCondition(vanChartRectanglePlot.getStackAndAxisCondition()); |
||||||
|
} else { |
||||||
|
List<VanChartAxis> xAxisList = clonePlot.getXAxisList(); |
||||||
|
List<VanChartAxis> yAxisList = clonePlot.getYAxisList(); |
||||||
|
List<VanChartAxis> newXAxisList = new ArrayList<>(); |
||||||
|
List<VanChartAxis> newYAxisList = new ArrayList<>(); |
||||||
|
newXAxisList.add(xAxisList.get(0)); |
||||||
|
newYAxisList.add(yAxisList.get(0)); |
||||||
|
clonePlot.setXAxisList(newXAxisList); |
||||||
|
clonePlot.setYAxisList(newYAxisList); |
||||||
|
clonePlot.setStackAndAxisCondition(new ConditionCollection()); |
||||||
|
} |
||||||
|
|
||||||
|
//百分比堆积图值轴的格式不保留
|
||||||
|
if (clonePlot.getVanChartPlotType() == VanChartPlotType.STACK_BY_PERCENT || |
||||||
|
((VanChartRectanglePlot) oldPlot).getVanChartPlotType() == VanChartPlotType.STACK_BY_PERCENT) { |
||||||
|
VanChartAxis cloneAxis = clonePlot.getValueAxisList().get(0); |
||||||
|
VanChartAxis vanChartAxis = vanChartRectanglePlot.getValueAxisList().get(0); |
||||||
|
cloneAxis.setFormat(vanChartAxis.getFormat()); |
||||||
|
cloneAxis.setPercentage(vanChartAxis.isPercentage()); |
||||||
|
} |
||||||
|
return clonePlot; |
||||||
|
} catch (CloneNotSupportedException ex) { |
||||||
|
FineLoggerFactory.getLogger().error("Error in change plot"); |
||||||
|
return newPlot; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//是否支持属性的继承
|
||||||
|
@Override |
||||||
|
protected boolean supportExtendAttr(VanChart chart) { |
||||||
|
if (StringUtils.equals(VanChartBubblePlot.VAN_CHART_BUBBLE_PLOT_ID, chart.getID())) { |
||||||
|
VanChartBubblePlot vanChartBubblePlot = chart.getPlot(); |
||||||
|
return !vanChartBubblePlot.isForceBubble(); |
||||||
|
} |
||||||
|
return extendPlotIds.contains(chart.getID()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.van.chart.map.line; |
||||||
|
|
||||||
|
import com.fr.chart.chartattr.Plot; |
||||||
|
import com.fr.design.gui.icheckbox.UICheckBox; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.plugin.chart.base.AttrTooltip; |
||||||
|
import com.fr.van.chart.designer.style.VanChartStylePane; |
||||||
|
|
||||||
|
import java.awt.BorderLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Bjorn |
||||||
|
* @version 10.0 |
||||||
|
* Created by Bjorn on 2020-08-20 |
||||||
|
*/ |
||||||
|
public class VanChartLineMapPlotTooltipNoCheckPane extends VanChartLineMapPlotTooltipPane { |
||||||
|
|
||||||
|
public VanChartLineMapPlotTooltipNoCheckPane(Plot plot, VanChartStylePane parent) { |
||||||
|
super(plot, parent); |
||||||
|
} |
||||||
|
|
||||||
|
protected void addComponents(Plot plot) { |
||||||
|
isTooltipShow = new UICheckBox(Toolkit.i18nText("Fine-Design_Chart_Use_Tooltip")); |
||||||
|
tooltipPane = createTooltipPane(plot); |
||||||
|
|
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
this.add(tooltipPane, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void populate(AttrTooltip attr) { |
||||||
|
super.populate(attr); |
||||||
|
isTooltipShow.setSelected(true); |
||||||
|
tooltipPane.setEnabled(isTooltipShow.isSelected()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
package com.fr.design.mainframe.widget.topxcreator; |
||||||
|
|
||||||
|
import com.fr.design.designer.creator.XCreator; |
||||||
|
import com.fr.design.mainframe.FormDesigner; |
||||||
|
import com.fr.design.mainframe.WidgetPropertyPane; |
||||||
|
import com.fr.design.utils.ComponentUtils; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author: Yuan.Wang |
||||||
|
* @Date: 2020/8/31 |
||||||
|
*/ |
||||||
|
public class BasicTopXCreator extends JComponent { |
||||||
|
private FormDesigner designer; |
||||||
|
private XCreator creator; |
||||||
|
|
||||||
|
public BasicTopXCreator(XCreator creator) { |
||||||
|
this.designer = WidgetPropertyPane.getInstance().getEditingFormDesigner(); |
||||||
|
this.creator = creator; |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
setOpaque(false); |
||||||
|
setBackground(null); |
||||||
|
setLayout(null); |
||||||
|
setBounds(calculateBounds()); |
||||||
|
addComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//子类可能会重写该方法
|
||||||
|
protected void resetSize(Rectangle bounds) { |
||||||
|
//do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
protected void addComponent() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重新设置组件大小 |
||||||
|
* */ |
||||||
|
public void resizeTopXCreator() { |
||||||
|
Rectangle bounds=calculateBounds(); |
||||||
|
setBounds(bounds); |
||||||
|
resetSize(bounds); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayCoverPane(MouseEvent e, boolean visible) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* 计算显示大小 |
||||||
|
* */ |
||||||
|
private Rectangle calculateBounds() { |
||||||
|
Rectangle rect = ComponentUtils.getRelativeBounds(creator); |
||||||
|
Rectangle bounds = new Rectangle(0, 0, creator.getWidth(), creator.getHeight()); |
||||||
|
bounds.x += (rect.x - designer.getHorizontalScaleValue()); |
||||||
|
bounds.y += (rect.y - designer.getVerticalScaleValue()); |
||||||
|
return bounds; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
super.paint(g); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package com.fr.design.mainframe.widget.topxcreator; |
||||||
|
|
||||||
|
import com.fr.design.designer.creator.XCreator; |
||||||
|
import com.fr.design.mainframe.CoverReportPane; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author: Yuan.Wang |
||||||
|
* @Date: 2020/8/26 |
||||||
|
*/ |
||||||
|
public class TopXCreator extends BasicTopXCreator { |
||||||
|
|
||||||
|
private final CoverReportPane coverPanel; |
||||||
|
|
||||||
|
public TopXCreator(XCreator creator) { |
||||||
|
super(creator); |
||||||
|
coverPanel = new CoverReportPane(); |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
coverPanel.setSize(getSize()); |
||||||
|
coverPanel.setVisible(false); |
||||||
|
add(coverPanel); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected void resetSize(Rectangle bounds) { |
||||||
|
coverPanel.setSize(getSize()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置是否显示蒙层 |
||||||
|
* */ |
||||||
|
public void displayCoverPane(boolean visible) { |
||||||
|
coverPanel.setVisible(visible); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 依据鼠标事件和visible设置是否显示蒙层 |
||||||
|
* */ |
||||||
|
public void displayCoverPane(MouseEvent event, boolean visible) { |
||||||
|
boolean isVisible = visible && getBounds().contains(event.getX(), event.getY()); |
||||||
|
coverPanel.setVisible(isVisible); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
package com.fr.design.mainframe.widget.topxcreator; |
||||||
|
|
||||||
|
import com.fr.design.designer.beans.events.DesignerEvent; |
||||||
|
import com.fr.design.designer.beans.models.SelectionModel; |
||||||
|
import com.fr.design.designer.creator.XCreator; |
||||||
|
import com.fr.design.mainframe.FormDesigner; |
||||||
|
|
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 需要显示顶层的组件层 |
||||||
|
* |
||||||
|
* @Author: Yuan.Wang |
||||||
|
* @Date: 2020/8/25 |
||||||
|
*/ |
||||||
|
public class TopXCreators extends JComponent { |
||||||
|
final private FormDesigner designer; |
||||||
|
|
||||||
|
public TopXCreators(FormDesigner designer) { |
||||||
|
this.designer = designer; |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
setLayout(null); |
||||||
|
setVisible(false); |
||||||
|
setBackground(null); |
||||||
|
setOpaque(false); |
||||||
|
designer.addDesignerEditListener(e -> { |
||||||
|
if (e.getCreatorEventID() == DesignerEvent.CREATOR_EDITED) { |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 选中的组件有变化时刷新 |
||||||
|
*/ |
||||||
|
public void refresh() { |
||||||
|
removeAll(); |
||||||
|
addXCreators(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
setSize(designer.getSize()); |
||||||
|
resizeTopXCreators(); |
||||||
|
super.paint(g); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setVisible(boolean aFlag) { |
||||||
|
super.setVisible(aFlag); |
||||||
|
for (int i = 0, count = getComponentCount(); i < count; i++) { |
||||||
|
if (getComponent(i) instanceof TopXCreator) { |
||||||
|
TopXCreator xCreator = (TopXCreator) getComponent(i); |
||||||
|
xCreator.displayCoverPane(aFlag); |
||||||
|
} |
||||||
|
} |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 依据MouseEvent坐标来设置是否显示蒙层 |
||||||
|
*/ |
||||||
|
public void displayCoverPane(MouseEvent e) { |
||||||
|
for (int i = 0, count = getComponentCount(); i < count; i++) { |
||||||
|
BasicTopXCreator xCreator = (BasicTopXCreator) getComponent(i); |
||||||
|
xCreator.displayCoverPane(e, isVisible()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 加入被选择的组件 |
||||||
|
*/ |
||||||
|
private void addXCreators() { |
||||||
|
SelectionModel selectionModel = designer.getSelectionModel(); |
||||||
|
XCreator[] xCreators = selectionModel.getSelection().getSelectedCreators(); |
||||||
|
for (XCreator creator : xCreators) { |
||||||
|
BasicTopXCreator topXCreator = creator.getTopXCreator(); |
||||||
|
if (topXCreator != null) { |
||||||
|
add(topXCreator); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新顶层组件的位置和大小 |
||||||
|
*/ |
||||||
|
private void resizeTopXCreators() { |
||||||
|
for (int i = 0, count = getComponentCount(); i < count; i++) { |
||||||
|
BasicTopXCreator topXCreator = (BasicTopXCreator) getComponent(i); |
||||||
|
topXCreator.resizeTopXCreator(); |
||||||
|
} |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
package com.fr.design.widget.ui.designer.mobile.component; |
||||||
|
|
||||||
|
import com.fr.design.constants.LayoutConstants; |
||||||
|
import com.fr.design.designer.IntervalConstants; |
||||||
|
import com.fr.design.designer.beans.events.DesignerEvent; |
||||||
|
import com.fr.design.dialog.BasicPane; |
||||||
|
import com.fr.design.gui.ibutton.ModeButtonGroup; |
||||||
|
import com.fr.design.gui.ibutton.UIRadioButton; |
||||||
|
import com.fr.design.gui.icheckbox.UICheckBox; |
||||||
|
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.WidgetPropertyPane; |
||||||
|
import com.fr.design.style.color.NewColorSelectBox; |
||||||
|
import com.fr.general.cardtag.mobile.MobileTemplateStyle; |
||||||
|
|
||||||
|
import javax.swing.BorderFactory; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.JComponent; |
||||||
|
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.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
|
||||||
|
public class MobileTabCommonSettingPane extends BasicPane { |
||||||
|
|
||||||
|
private UICheckBox showTabTitleCheck; |
||||||
|
private UICheckBox tabSlideCheck; |
||||||
|
private UICheckBox showTabDotIndicatorCheck; |
||||||
|
private ModeButtonGroup<Integer> buttonGroup; |
||||||
|
private NewColorSelectBox initDotColorBox; |
||||||
|
private NewColorSelectBox selectDotColorBox; |
||||||
|
|
||||||
|
public MobileTabCommonSettingPane() { |
||||||
|
initComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponent() { |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
|
||||||
|
this.showTabTitleCheck = new UICheckBox( |
||||||
|
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Tab_Show_Title"), true) { |
||||||
|
@Override |
||||||
|
protected void initListener() { |
||||||
|
this.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
attributeChange(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
this.tabSlideCheck = new UICheckBox( |
||||||
|
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Tab_Slide"), true) { |
||||||
|
@Override |
||||||
|
protected void initListener() { |
||||||
|
this.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
attributeChange(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
this.showTabDotIndicatorCheck = new UICheckBox( |
||||||
|
com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Tab_Show_Indicator"), true) { |
||||||
|
@Override |
||||||
|
protected void initListener() { |
||||||
|
this.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
attributeChange(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
UILabel label = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Tab_Show_Indicator_type")); |
||||||
|
JPanel dotIndicatorShowTypePane = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); |
||||||
|
addIndicatorShowTypeButton(dotIndicatorShowTypePane); |
||||||
|
|
||||||
|
UILabel initColorLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Init_Fill")); |
||||||
|
UILabel selectColor = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Select_Fill")); |
||||||
|
initDotColorBox = new NewColorSelectBox(0){ |
||||||
|
@Override |
||||||
|
protected void iniListener() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
super.mouseClicked(e); |
||||||
|
this.attributeChange(); |
||||||
|
} |
||||||
|
}; |
||||||
|
selectDotColorBox = new NewColorSelectBox(0){ |
||||||
|
@Override |
||||||
|
protected void iniListener() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
super.mouseClicked(e); |
||||||
|
this.attributeChange(); |
||||||
|
} |
||||||
|
}; |
||||||
|
JPanel initDotColorPane = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{new Component[]{initColorLabel, initDotColorBox}}, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_L1, LayoutConstants.VGAP_MEDIUM); |
||||||
|
JPanel selectDotColorPane = TableLayoutHelper.createGapTableLayoutPane(new Component[][]{new Component[]{selectColor, selectDotColorBox}}, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_L1, LayoutConstants.VGAP_MEDIUM); |
||||||
|
|
||||||
|
double[] rowSize = {TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED}; |
||||||
|
double[] columnSize = {TableLayout.FILL}; |
||||||
|
int[][] rowCount = {{1}, {1}, {1}}; |
||||||
|
double[] verticalGaps = {10, 10, 10}; |
||||||
|
double[] dotSettingColumnSize = {TableLayout.PREFERRED, TableLayout.FILL}; |
||||||
|
|
||||||
|
Component[][] components = new Component[][]{ |
||||||
|
new Component[]{this.showTabTitleCheck}, |
||||||
|
new Component[]{this.tabSlideCheck}, |
||||||
|
new Component[]{this.showTabDotIndicatorCheck} |
||||||
|
}; |
||||||
|
|
||||||
|
JPanel tabBaseConfigPane = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, |
||||||
|
IntervalConstants.INTERVAL_L1, IntervalConstants.INTERVAL_L1); |
||||||
|
tabBaseConfigPane.setBorder(BorderFactory.createEmptyBorder(0, 0, IntervalConstants.INTERVAL_L1, 0)); |
||||||
|
|
||||||
|
JPanel dotIndicatorSettingPanel = TableLayoutHelper.createDiffVGapTableLayoutPane(new JComponent[][]{ |
||||||
|
{label, dotIndicatorShowTypePane}, |
||||||
|
{initColorLabel, initDotColorPane}, |
||||||
|
{selectColor, selectDotColorPane}}, rowSize, dotSettingColumnSize, 0, verticalGaps); |
||||||
|
dotIndicatorSettingPanel.setBorder( |
||||||
|
BorderFactory.createEmptyBorder(0, IntervalConstants.INTERVAL_L2, IntervalConstants.INTERVAL_L1, 0) |
||||||
|
); |
||||||
|
|
||||||
|
final JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
jPanel.add(tabBaseConfigPane, BorderLayout.NORTH); |
||||||
|
jPanel.add(dotIndicatorSettingPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
this.add(jPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
showTabDotIndicatorCheck.addChangeListener(new ChangeListener() { |
||||||
|
@Override |
||||||
|
public void stateChanged(ChangeEvent e) { |
||||||
|
dotIndicatorSettingPanel.setVisible(showTabDotIndicatorCheck.isSelected()); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void populate(MobileTemplateStyle mobileTemplateStyle) { |
||||||
|
this.showTabTitleCheck.setSelected(mobileTemplateStyle.isShowTabTitle()); |
||||||
|
this.showTabDotIndicatorCheck.setSelected(mobileTemplateStyle.isShowDotIndicator()); |
||||||
|
this.tabSlideCheck.setSelected(mobileTemplateStyle.canSlide()); |
||||||
|
Color initDotColor = mobileTemplateStyle.getIndicatorInitialColor(); |
||||||
|
Color selectDotColor = mobileTemplateStyle.getIndicatorSelectColor(); |
||||||
|
int dotIndicatorShowType = mobileTemplateStyle.getDotIndicatorShowType(); |
||||||
|
this.populateColorBox(initDotColorBox, initDotColor, MobileTemplateStyle.DEFAULT_INITIAL_DOT_COLOR); |
||||||
|
this.populateColorBox(selectDotColorBox, selectDotColor, MobileTemplateStyle.DEFAULT_SELECT_DOT_COLOR); |
||||||
|
if (dotIndicatorShowType != buttonGroup.getCurrentSelected()) { |
||||||
|
this.buttonGroup.setSelectButton(dotIndicatorShowType); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void update(MobileTemplateStyle mobileTemplateStyle) { |
||||||
|
mobileTemplateStyle.setShowTabTitle(showTabTitleCheck.isSelected()); |
||||||
|
mobileTemplateStyle.setShowDotIndicator(showTabDotIndicatorCheck.isSelected()); |
||||||
|
mobileTemplateStyle.setCanSlide(tabSlideCheck.isSelected()); |
||||||
|
mobileTemplateStyle.setIndicatorInitialColor(initDotColorBox.getSelectObject()); |
||||||
|
mobileTemplateStyle.setIndicatorSelectColor(selectDotColorBox.getSelectObject()); |
||||||
|
mobileTemplateStyle.setDotIndicatorShowType(buttonGroup.getCurrentSelected()); |
||||||
|
WidgetPropertyPane.getInstance().getEditingFormDesigner().getEditListenerTable().fireCreatorModified(DesignerEvent.CREATOR_EDITED); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String title4PopupWindow() { |
||||||
|
return "MobileTabCommonSettingPane"; |
||||||
|
} |
||||||
|
|
||||||
|
private void addIndicatorShowTypeButton(JPanel dotIndicatorShowTypePane) { |
||||||
|
UIRadioButton holderPlaceButton = new UIRadioButton(Toolkit.i18nText("Fine-Design_Mobile_Tab_Holder_Place")); |
||||||
|
holderPlaceButton.setSelected(true); |
||||||
|
UIRadioButton floatButton = new UIRadioButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Mobile_Tab_Float")); |
||||||
|
dotIndicatorShowTypePane.add(holderPlaceButton); |
||||||
|
dotIndicatorShowTypePane.add(floatButton); |
||||||
|
dotIndicatorShowTypePane.setBorder( |
||||||
|
BorderFactory.createEmptyBorder(0, IntervalConstants.INTERVAL_L1, 0, 0) |
||||||
|
); |
||||||
|
buttonGroup = new ModeButtonGroup<>(); |
||||||
|
buttonGroup.put(MobileTemplateStyle.TYPE_PLACEHOLDER_DOT_INDICATOR, holderPlaceButton); |
||||||
|
buttonGroup.put(MobileTemplateStyle.TYPE_FLOAT_DOT_INDICATOR, floatButton); |
||||||
|
} |
||||||
|
|
||||||
|
private void populateColorBox(NewColorSelectBox colorBox, Color color, Color defaultColor) { |
||||||
|
if (color == null) { |
||||||
|
color = defaultColor; |
||||||
|
} |
||||||
|
if (color != colorBox.getSelectObject()) { |
||||||
|
colorBox.setSelectObject(color); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,208 +0,0 @@ |
|||||||
package com.fr.start.fx; |
|
||||||
|
|
||||||
import com.sun.imageio.plugins.gif.GIFImageReader; |
|
||||||
import com.sun.imageio.plugins.gif.GIFImageReaderSpi; |
|
||||||
import com.sun.javafx.tk.ImageLoader; |
|
||||||
import com.sun.javafx.tk.PlatformImage; |
|
||||||
import javafx.animation.KeyFrame; |
|
||||||
import javafx.animation.Timeline; |
|
||||||
import javafx.event.Event; |
|
||||||
import javafx.event.EventHandler; |
|
||||||
import javafx.scene.image.WritableImage; |
|
||||||
import javafx.util.Duration; |
|
||||||
|
|
||||||
import javax.imageio.stream.FileImageInputStream; |
|
||||||
import java.io.File; |
|
||||||
import java.lang.ref.WeakReference; |
|
||||||
import java.lang.reflect.InvocationTargetException; |
|
||||||
import java.lang.reflect.Method; |
|
||||||
import java.net.MalformedURLException; |
|
||||||
import java.net.URI; |
|
||||||
import java.net.URL; |
|
||||||
import java.util.regex.Pattern; |
|
||||||
|
|
||||||
/** |
|
||||||
* 边加载边播放的gif加载器 |
|
||||||
* |
|
||||||
* @author daniel |
|
||||||
*/ |
|
||||||
public class FastGifImage extends WritableImage { |
|
||||||
private String url; |
|
||||||
private int gifCount; |
|
||||||
|
|
||||||
public FastGifImage(String url, int w, int h) { |
|
||||||
super(w, h); |
|
||||||
this.url = validateUrl(url); |
|
||||||
seekCount(); |
|
||||||
initialize(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 给出gif帧数,加快加载速度 |
|
||||||
* |
|
||||||
* @param url gif url |
|
||||||
* @param gifCount gif帧数 |
|
||||||
* @param w 宽 |
|
||||||
* @param h 高 |
|
||||||
*/ |
|
||||||
public FastGifImage(String url, int gifCount, int w, int h) { |
|
||||||
super(w, h); |
|
||||||
this.url = validateUrl(url); |
|
||||||
this.gifCount = gifCount; |
|
||||||
initialize(); |
|
||||||
} |
|
||||||
|
|
||||||
private void seekCount() { |
|
||||||
try { |
|
||||||
GIFImageReaderSpi spi = new GIFImageReaderSpi(); |
|
||||||
GIFImageReader gifReader = (GIFImageReader) spi.createReaderInstance(); |
|
||||||
gifReader.setInput(new FileImageInputStream(new File(new URI(url)))); |
|
||||||
gifCount = gifReader.getNumImages(true); |
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static final Pattern URL_QUICKMATCH = Pattern.compile("^\\p{Alpha}[\\p{Alnum}+.-]*:.*$"); |
|
||||||
|
|
||||||
private static String validateUrl(final String url) { |
|
||||||
if (url == null) { |
|
||||||
throw new NullPointerException("URL must not be null"); |
|
||||||
} |
|
||||||
|
|
||||||
if (url.trim().isEmpty()) { |
|
||||||
throw new IllegalArgumentException("URL must not be empty"); |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
if (!URL_QUICKMATCH.matcher(url).matches()) { |
|
||||||
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); |
|
||||||
URL resource; |
|
||||||
if (url.charAt(0) == '/') { |
|
||||||
resource = contextClassLoader.getResource(url.substring(1)); |
|
||||||
} else { |
|
||||||
resource = contextClassLoader.getResource(url); |
|
||||||
} |
|
||||||
if (resource == null) { |
|
||||||
throw new IllegalArgumentException("Invalid URL or resource not found"); |
|
||||||
} |
|
||||||
return resource.toString(); |
|
||||||
} |
|
||||||
// Use URL constructor for validation
|
|
||||||
return new URL(url).toString(); |
|
||||||
} catch (final IllegalArgumentException e) { |
|
||||||
throw new IllegalArgumentException("Invalid URL" + e.getMessage()); |
|
||||||
} catch (final MalformedURLException e) { |
|
||||||
throw new IllegalArgumentException("Invalid URL" + e.getMessage()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void finishImage(ImageLoader loader) { |
|
||||||
initializeAnimatedImage(loader); |
|
||||||
} |
|
||||||
|
|
||||||
// Generates the animation Timeline for multiframe images.
|
|
||||||
private void initializeAnimatedImage(ImageLoader loader) { |
|
||||||
|
|
||||||
animation = new Animation(this, loader); |
|
||||||
animation.start(); |
|
||||||
} |
|
||||||
|
|
||||||
// Support for animated images.
|
|
||||||
private Animation animation; |
|
||||||
|
|
||||||
private static final class Animation { |
|
||||||
final WeakReference<FastGifImage> imageRef; |
|
||||||
final Timeline timeline; |
|
||||||
private ImageLoader loader; |
|
||||||
|
|
||||||
public Animation(final FastGifImage image, final ImageLoader loader) { |
|
||||||
this.loader = loader; |
|
||||||
imageRef = new WeakReference<FastGifImage>(image); |
|
||||||
timeline = new Timeline(); |
|
||||||
timeline.setCycleCount(Timeline.INDEFINITE); |
|
||||||
|
|
||||||
final int frameCount = loader.getFrameCount(); |
|
||||||
int duration = 0; |
|
||||||
|
|
||||||
for (int i = 0; i < frameCount; ++i) { |
|
||||||
addKeyFrame(i, duration); |
|
||||||
duration = duration + loader.getFrameDelay(i); |
|
||||||
} |
|
||||||
|
|
||||||
// Note: we need one extra frame in the timeline to define how long
|
|
||||||
// the last frame is shown, the wrap around is "instantaneous"
|
|
||||||
addKeyFrame(0, duration); |
|
||||||
} |
|
||||||
|
|
||||||
public void start() { |
|
||||||
timeline.play(); |
|
||||||
} |
|
||||||
|
|
||||||
public void stop() { |
|
||||||
timeline.stop(); |
|
||||||
loader = null; |
|
||||||
} |
|
||||||
|
|
||||||
private void updateImage(final int frameIndex) { |
|
||||||
final FastGifImage image = imageRef.get(); |
|
||||||
if (image != null) { |
|
||||||
image.setPlatformImagePropertyImpl( |
|
||||||
loader.getFrame(frameIndex)); |
|
||||||
} else { |
|
||||||
timeline.stop(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void addKeyFrame(final int index, final double duration) { |
|
||||||
timeline.getKeyFrames().add( |
|
||||||
new KeyFrame(Duration.millis(duration), |
|
||||||
new EventHandler() { |
|
||||||
@Override |
|
||||||
public void handle(Event event) { |
|
||||||
updateImage(index); |
|
||||||
} |
|
||||||
} |
|
||||||
)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static Method method; |
|
||||||
|
|
||||||
static { |
|
||||||
try { |
|
||||||
method = FastGifImage.class.getSuperclass().getSuperclass().getDeclaredMethod("platformImagePropertyImpl"); |
|
||||||
method.setAccessible(true); |
|
||||||
} catch (Exception e) { |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void setPlatformImagePropertyImpl(PlatformImage image) { |
|
||||||
try { |
|
||||||
Object o = method.invoke(this); |
|
||||||
Method method = o.getClass().getDeclaredMethod("set", Object.class); |
|
||||||
method.setAccessible(true); |
|
||||||
method.invoke(o, image); |
|
||||||
} catch (IllegalAccessException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (InvocationTargetException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} catch (NoSuchMethodException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private void initialize() { |
|
||||||
finishImage(new PrismImageLoader2(url, gifCount, (int) getRequestedWidth(), (int) getRequestedHeight(), isPreserveRatio(), isSmooth())); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 销毁gif动画 |
|
||||||
*/ |
|
||||||
public void destroy() { |
|
||||||
animation.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,208 +0,0 @@ |
|||||||
package com.fr.start.fx; |
|
||||||
|
|
||||||
import com.fr.concurrent.NamedThreadFactory; |
|
||||||
import com.fr.log.FineLoggerFactory; |
|
||||||
import com.sun.javafx.iio.ImageFrame; |
|
||||||
import com.sun.javafx.iio.ImageLoadListener; |
|
||||||
import com.sun.javafx.iio.ImageLoader; |
|
||||||
import com.sun.javafx.iio.ImageMetadata; |
|
||||||
import com.sun.javafx.iio.ImageStorageException; |
|
||||||
import com.sun.javafx.iio.common.ImageTools; |
|
||||||
import com.sun.javafx.iio.gif.GIFImageLoaderFactory; |
|
||||||
import com.sun.javafx.tk.PlatformImage; |
|
||||||
import com.sun.prism.Image; |
|
||||||
import com.sun.prism.impl.PrismSettings; |
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.util.concurrent.ExecutorService; |
|
||||||
import java.util.concurrent.Executors; |
|
||||||
|
|
||||||
/** |
|
||||||
* 边加载边播放的gif加载器 |
|
||||||
* |
|
||||||
* @author daniel |
|
||||||
*/ |
|
||||||
class PrismImageLoader2 implements com.sun.javafx.tk.ImageLoader { |
|
||||||
|
|
||||||
private Image[] images; |
|
||||||
private int[] delayTimes; |
|
||||||
private int width; |
|
||||||
private int height; |
|
||||||
private int gifCount = 1; |
|
||||||
private Exception exception; |
|
||||||
|
|
||||||
public PrismImageLoader2(final String url, int gifCount, final int width, final int height, |
|
||||||
final boolean preserveRatio, final boolean smooth) { |
|
||||||
this.gifCount = gifCount; |
|
||||||
images = new Image[gifCount]; |
|
||||||
delayTimes = new int[gifCount]; |
|
||||||
this.width = width; |
|
||||||
this.height = height; |
|
||||||
ExecutorService es = Executors.newSingleThreadExecutor(new NamedThreadFactory("PrismImageLoader2")); |
|
||||||
es.execute(new Runnable() { |
|
||||||
@Override |
|
||||||
public void run() { |
|
||||||
InputStream inputStream = null; |
|
||||||
try { |
|
||||||
inputStream = ImageTools.createInputStream(url); |
|
||||||
loadAll(inputStream, width, height, preserveRatio, smooth); |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} finally { |
|
||||||
try { |
|
||||||
if (inputStream != null) { |
|
||||||
inputStream.close(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
es.shutdown(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getWidth() { |
|
||||||
return width; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getHeight() { |
|
||||||
return height; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getFrameCount() { |
|
||||||
return gifCount; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@SuppressWarnings("squid:S2142") |
|
||||||
public PlatformImage getFrame(int index) { |
|
||||||
while (images[index] == null) { |
|
||||||
synchronized (this) { |
|
||||||
if (images[index] == null) { |
|
||||||
try { |
|
||||||
this.wait(); |
|
||||||
} catch (InterruptedException e) { |
|
||||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return images[index]; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public int getFrameDelay(int index) { |
|
||||||
// while (images[0] == null) {
|
|
||||||
// synchronized (this) {
|
|
||||||
// if(images[0] == null) {
|
|
||||||
// try {
|
|
||||||
// this.wait();
|
|
||||||
// } catch (InterruptedException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
// return delayTimes[0];
|
|
||||||
// 直接使用第一帧的时间
|
|
||||||
return 40; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getLoopCount() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Exception getException() { |
|
||||||
return exception; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("squid:S244") |
|
||||||
private void loadAll(InputStream stream, int w, int h, |
|
||||||
boolean preserveRatio, boolean smooth) { |
|
||||||
ImageLoadListener listener = new PrismLoadListener(); |
|
||||||
|
|
||||||
try { |
|
||||||
ImageLoader loader = null; |
|
||||||
loader = GIFImageLoaderFactory.getInstance().createImageLoader(stream); |
|
||||||
loader.addListener(listener); |
|
||||||
|
|
||||||
for (int i = 0; i < gifCount; i++) { |
|
||||||
ImageFrame imageFrame = loader.load(i, w, h, preserveRatio, smooth); |
|
||||||
images[i] = convert(imageFrame); |
|
||||||
synchronized (this) { |
|
||||||
notifyAll(); |
|
||||||
} |
|
||||||
} |
|
||||||
} catch (ImageStorageException e) { |
|
||||||
handleException(e); |
|
||||||
} catch (Exception e) { |
|
||||||
handleException(e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void handleException(final ImageStorageException isException) { |
|
||||||
// unwrap ImageStorageException if possible
|
|
||||||
final Throwable exceptionCause = isException.getCause(); |
|
||||||
if (exceptionCause instanceof Exception) { |
|
||||||
handleException((Exception) exceptionCause); |
|
||||||
} else { |
|
||||||
handleException((Exception) isException); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void handleException(final Exception exception) { |
|
||||||
if (PrismSettings.verbose) { |
|
||||||
exception.printStackTrace(System.err); |
|
||||||
} |
|
||||||
this.exception = exception; |
|
||||||
} |
|
||||||
|
|
||||||
private Image convert(ImageFrame imgFrames) { |
|
||||||
ImageFrame frame = imgFrames; |
|
||||||
Image image = Image.convertImageFrame(frame); |
|
||||||
ImageMetadata metadata = frame.getMetadata(); |
|
||||||
if (metadata != null) { |
|
||||||
Integer delay = metadata.delayTime; |
|
||||||
if (delay != null) { |
|
||||||
delayTimes[0] = delay.intValue(); |
|
||||||
} |
|
||||||
} |
|
||||||
return image; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private class PrismLoadListener implements ImageLoadListener { |
|
||||||
@Override |
|
||||||
public void imageLoadWarning(ImageLoader loader, String message) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void imageLoadProgress(ImageLoader loader, |
|
||||||
float percentageComplete) { |
|
||||||
// progress only matters when backgroundLoading=true, but
|
|
||||||
// currently we are relying on AbstractRemoteResource for tracking
|
|
||||||
// progress of the InputStream, so there's no need to implement
|
|
||||||
// this for now; eventually though we might want to consider
|
|
||||||
// moving away from AbstractRemoteResource and instead use
|
|
||||||
// the built-in support for progress in the javafx-iio library...
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void imageLoadMetaData(ImageLoader loader, ImageMetadata metadata) { |
|
||||||
// We currently have no need to listen for ImageMetadata ready.
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|