Levy.Xie-解安森
1 month ago
18 changed files with 1230 additions and 84 deletions
@ -0,0 +1,228 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import com.fine.theme.utils.FineUIUtils; |
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||||
|
import com.formdev.flatlaf.util.Animator; |
||||||
|
import com.formdev.flatlaf.util.CubicBezierEasing; |
||||||
|
import com.formdev.flatlaf.util.UIScale; |
||||||
|
import net.miginfocom.swing.MigLayout; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.Image; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.util.Stack; |
||||||
|
|
||||||
|
/** |
||||||
|
* 遮罩功能:渐显遮罩层 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 12.0 |
||||||
|
* Created on 2024/12/12 |
||||||
|
*/ |
||||||
|
public class ComponentLayer extends JPanel { |
||||||
|
|
||||||
|
private GlassPaneChild component; |
||||||
|
private GlassPaneChild nextComponent; |
||||||
|
private Animator animator; |
||||||
|
private float animate; |
||||||
|
private boolean showSnapshot; |
||||||
|
private boolean simpleSnapshot; |
||||||
|
private Image image; |
||||||
|
|
||||||
|
private Image nextImage; |
||||||
|
private Stack<GlassPaneChild> componentStack; |
||||||
|
private boolean push; |
||||||
|
|
||||||
|
private void pushStack(GlassPaneChild component) { |
||||||
|
if (componentStack == null) { |
||||||
|
componentStack = new Stack<>(); |
||||||
|
} |
||||||
|
componentStack.push(component); |
||||||
|
} |
||||||
|
|
||||||
|
public ComponentLayer(GlassPaneChild component) { |
||||||
|
this.component = component; |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
MigLayout layout = new MigLayout("insets 0,fill", "fill", "fill"); |
||||||
|
setLayout(layout); |
||||||
|
if (component.getRoundBorder() > 0) { |
||||||
|
setOpaque(false); |
||||||
|
component.setOpaque(false); |
||||||
|
} |
||||||
|
add(component); |
||||||
|
component.setVisible(false); |
||||||
|
} |
||||||
|
|
||||||
|
private void initAnimator() { |
||||||
|
animator = new Animator(350, new Animator.TimingTarget() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void timingEvent(float v) { |
||||||
|
animate = v; |
||||||
|
repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void begin() { |
||||||
|
nextImage = FineUIUtils.createImage(nextComponent); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void end() { |
||||||
|
showSnapshot = false; |
||||||
|
component = nextComponent; |
||||||
|
component.popupShow(); |
||||||
|
nextComponent = null; |
||||||
|
animate = 0; |
||||||
|
component.setVisible(true); |
||||||
|
if (nextImage != null) { |
||||||
|
nextImage.flush(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
animator.setInterpolator(CubicBezierEasing.EASE); |
||||||
|
} |
||||||
|
|
||||||
|
private void startAnimate() { |
||||||
|
if (animator == null) { |
||||||
|
initAnimator(); |
||||||
|
} |
||||||
|
if (animator.isRunning()) { |
||||||
|
animator.stop(); |
||||||
|
} |
||||||
|
animate = 0; |
||||||
|
animator.start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 放入组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
*/ |
||||||
|
public void pushComponent(GlassPaneChild component) { |
||||||
|
component.onPush(); |
||||||
|
pushStack(this.component); |
||||||
|
push = true; |
||||||
|
showComponent(component); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 弹出组件 |
||||||
|
*/ |
||||||
|
public void popComponent() { |
||||||
|
if (!componentStack.isEmpty()) { |
||||||
|
GlassPaneChild component = componentStack.pop(); |
||||||
|
component.onPop(); |
||||||
|
push = false; |
||||||
|
showComponent(component); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void showComponent(GlassPaneChild component) { |
||||||
|
showSnapshot = true; |
||||||
|
this.nextComponent = component; |
||||||
|
image = FineUIUtils.createImage(this.component); |
||||||
|
if (component.getRoundBorder() > 0) { |
||||||
|
setOpaque(false); |
||||||
|
component.setOpaque(false); |
||||||
|
} |
||||||
|
component.setVisible(false); |
||||||
|
remove(this.component); |
||||||
|
add(component); |
||||||
|
revalidate(); |
||||||
|
startAnimate(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示遮罩 |
||||||
|
*/ |
||||||
|
public void showSnapshot() { |
||||||
|
if (animator != null && animator.isRunning()) { |
||||||
|
animator.stop(); |
||||||
|
} |
||||||
|
simpleSnapshot = true; |
||||||
|
doLayout(); |
||||||
|
image = FineUIUtils.createImage(component, component.getRoundBorder()); |
||||||
|
component.setVisible(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 隐藏遮罩 |
||||||
|
*/ |
||||||
|
public void hideSnapshot() { |
||||||
|
showSnapshot = false; |
||||||
|
simpleSnapshot = false; |
||||||
|
component.setVisible(true); |
||||||
|
if (image != null) { |
||||||
|
image.flush(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void paintComponent(Graphics g) { |
||||||
|
if (!isOpaque() && component.getRoundBorder() > 0) { |
||||||
|
Graphics2D g2 = (Graphics2D) g.create(); |
||||||
|
FlatUIUtils.setRenderingHints(g2); |
||||||
|
g2.setColor(getBackground()); |
||||||
|
int arc = UIScale.scale(component.getRoundBorder()); |
||||||
|
FlatUIUtils.paintComponentBackground(g2, 0, 0, getWidth(), getHeight(), 0, arc); |
||||||
|
g2.dispose(); |
||||||
|
} |
||||||
|
super.paintComponent(g); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
if (simpleSnapshot) { |
||||||
|
g.drawImage(image, 0, 0, null); |
||||||
|
} else if (showSnapshot) { |
||||||
|
int width = getWidth(); |
||||||
|
int height = getHeight(); |
||||||
|
if (width > 0 && height > 0) { |
||||||
|
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); |
||||||
|
Graphics2D g2 = bufferedImage.createGraphics(); |
||||||
|
FlatUIUtils.setRenderingHints(g2); |
||||||
|
int arc = UIScale.scale(component.getRoundBorder()); |
||||||
|
g2.setColor(getBackground()); |
||||||
|
FlatUIUtils.paintComponentBackground(g2, 0, 0, width, height, 0, arc); |
||||||
|
if (image != null) { |
||||||
|
int w = image.getWidth(null); |
||||||
|
double x; |
||||||
|
if (push) { |
||||||
|
x = -w * animate; |
||||||
|
} else { |
||||||
|
x = w * animate; |
||||||
|
} |
||||||
|
g2.setComposite(AlphaComposite.SrcAtop.derive(1f - animate)); |
||||||
|
g2.drawImage(image, (int) x, 0, null); |
||||||
|
} |
||||||
|
if (nextImage != null) { |
||||||
|
int w = nextImage.getWidth(null); |
||||||
|
double x; |
||||||
|
if (push) { |
||||||
|
x = getWidth() - (w * animate); |
||||||
|
} else { |
||||||
|
x = -getWidth() + (w * animate); |
||||||
|
} |
||||||
|
g2.setComposite(AlphaComposite.SrcAtop.derive(animate)); |
||||||
|
g2.drawImage(nextImage, (int) x, 0, null); |
||||||
|
} |
||||||
|
g2.dispose(); |
||||||
|
g.drawImage(bufferedImage, 0, 0, null); |
||||||
|
} |
||||||
|
} else { |
||||||
|
super.paint(g); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public GlassPaneChild getComponent() { |
||||||
|
return component; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils; |
||||||
|
|
||||||
|
import javax.swing.JPanel; |
||||||
|
|
||||||
|
/** |
||||||
|
* 遮罩加载面板组件 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 12.0 |
||||||
|
* Created on 2024/11/28 |
||||||
|
*/ |
||||||
|
public class GlassPaneChild extends JPanel { |
||||||
|
|
||||||
|
public int getRoundBorder() { |
||||||
|
return FlatUIUtils.getUIInt("Component.arc", 6); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 放入组件时的回调方法 |
||||||
|
*/ |
||||||
|
public void onPush() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 弹出组件时的回调方法 |
||||||
|
*/ |
||||||
|
public void onPop() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示弹窗组件时的回调方法 |
||||||
|
*/ |
||||||
|
public void popupShow() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 关闭窗口时的回调方法 |
||||||
|
*/ |
||||||
|
public void onClose() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import com.formdev.flatlaf.FlatLaf; |
||||||
|
import com.formdev.flatlaf.util.Animator; |
||||||
|
import com.formdev.flatlaf.util.CubicBezierEasing; |
||||||
|
import net.miginfocom.swing.MigLayout; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import java.awt.AlphaComposite; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Graphics2D; |
||||||
|
import java.awt.geom.Rectangle2D; |
||||||
|
|
||||||
|
/** |
||||||
|
* 遮罩功能: 弹出面板组件 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 12.0 |
||||||
|
* Created on 2024/12/12 |
||||||
|
*/ |
||||||
|
public class GlassPopup extends JComponent { |
||||||
|
private final GlassPopupManager parent; |
||||||
|
private final ComponentLayer componentLayer; |
||||||
|
private Animator animator; |
||||||
|
private MigLayout layout; |
||||||
|
private float animate; |
||||||
|
private boolean show; |
||||||
|
|
||||||
|
public GlassPopup(GlassPopupManager parent, GlassPaneChild component) { |
||||||
|
this.parent = parent; |
||||||
|
this.componentLayer = new ComponentLayer(component); |
||||||
|
init(); |
||||||
|
initAnimator(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
layout = new MigLayout(); |
||||||
|
setLayout(layout); |
||||||
|
add(componentLayer, getLayout(parent.layerPane, 0)); |
||||||
|
} |
||||||
|
|
||||||
|
private void initAnimator() { |
||||||
|
animator = new Animator(400, new Animator.TimingTarget() { |
||||||
|
@Override |
||||||
|
public void timingEvent(float fraction) { |
||||||
|
if (show) { |
||||||
|
animate = fraction; |
||||||
|
} else { |
||||||
|
animate = 1f - fraction; |
||||||
|
} |
||||||
|
float f = Math.round(animate * 100) / 100f; |
||||||
|
String lc = getLayout(parent.layerPane, f); |
||||||
|
layout.setComponentConstraints(componentLayer, lc); |
||||||
|
repaint(); |
||||||
|
revalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void begin() { |
||||||
|
componentLayer.showSnapshot(); |
||||||
|
parent.windowSnapshots.createSnapshot(); |
||||||
|
parent.contentPane.setVisible(false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void end() { |
||||||
|
componentLayer.hideSnapshot(); |
||||||
|
if (show) { |
||||||
|
componentLayer.getComponent().popupShow(); |
||||||
|
} else { |
||||||
|
parent.removePopup(GlassPopup.this); |
||||||
|
} |
||||||
|
parent.contentPane.setVisible(true); |
||||||
|
parent.windowSnapshots.removeSnapshot(); |
||||||
|
} |
||||||
|
}); |
||||||
|
animator.setInterpolator(CubicBezierEasing.EASE); |
||||||
|
} |
||||||
|
|
||||||
|
public void setShowPopup(boolean show) { |
||||||
|
if (this.show != show) { |
||||||
|
// 前置动画未结束时,先关闭前置动画
|
||||||
|
if (animator.isRunning()) { |
||||||
|
animator.stop(); |
||||||
|
} |
||||||
|
this.show = show; |
||||||
|
animator.start(); |
||||||
|
if (show) { |
||||||
|
setFocusCycleRoot(true); |
||||||
|
} else { |
||||||
|
uninstallListener(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void uninstallListener() { |
||||||
|
if (getMouseListeners().length != 0) { |
||||||
|
removeMouseListener(getMouseListeners()[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void paintComponent(Graphics g) { |
||||||
|
Graphics2D g2 = (Graphics2D) g; |
||||||
|
g2.setComposite(AlphaComposite.SrcOver.derive(animate * 0.5f)); |
||||||
|
g2.setColor(background()); |
||||||
|
g2.fill(new Rectangle2D.Double(0, 0, getWidth(), getHeight())); |
||||||
|
g2.setComposite(AlphaComposite.SrcOver.derive(animate)); |
||||||
|
super.paintComponent(g); |
||||||
|
} |
||||||
|
|
||||||
|
protected Component getComponent() { |
||||||
|
return componentLayer.getComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
protected ComponentLayer getComponentLayer() { |
||||||
|
return componentLayer; |
||||||
|
} |
||||||
|
|
||||||
|
private Color background() { |
||||||
|
return FlatLaf.isLafDark() ? new Color(50, 50, 50) : new Color(20, 20, 20); |
||||||
|
} |
||||||
|
|
||||||
|
private String getLayout(Component parent, float animate) { |
||||||
|
float an = 20f / 600; |
||||||
|
float space = 0.5f + an - (animate * an); |
||||||
|
return "pos 0.5al " + space + "al,height ::100%-20"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,292 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import javax.swing.JLayeredPane; |
||||||
|
import javax.swing.JRootPane; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.CardLayout; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.Container; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.WindowAdapter; |
||||||
|
import java.awt.event.WindowEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* 遮罩功能: 遮罩面板管理器 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 12.0 |
||||||
|
* Created on 2024/12/12 |
||||||
|
*/ |
||||||
|
public class GlassPopupManager { |
||||||
|
|
||||||
|
private static GlassPopupManager instance; |
||||||
|
protected WindowSnapshots windowSnapshots; |
||||||
|
protected JLayeredPane layerPane; |
||||||
|
protected Container contentPane; |
||||||
|
|
||||||
|
private GlassPopupManager() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
layerPane = new JLayeredPane(); |
||||||
|
layerPane.setLayout(new CardLayout()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 添加并显示弹窗组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
* @param name 组件名 |
||||||
|
*/ |
||||||
|
protected void addAndShowPopup(GlassPaneChild component, String name) { |
||||||
|
GlassPopup popup = new GlassPopup(this, component); |
||||||
|
instance.layerPane.applyComponentOrientation(instance.contentPane.getComponentOrientation()); |
||||||
|
popup.applyComponentOrientation(instance.contentPane.getComponentOrientation()); |
||||||
|
if (name != null) { |
||||||
|
popup.setName(name); |
||||||
|
} |
||||||
|
layerPane.setLayer(popup, JLayeredPane.DRAG_LAYER); |
||||||
|
layerPane.add(popup, 0); |
||||||
|
popup.setVisible(true); |
||||||
|
popup.setShowPopup(true); |
||||||
|
if (!layerPane.isVisible()) { |
||||||
|
layerPane.setVisible(true); |
||||||
|
} |
||||||
|
layerPane.grabFocus(); |
||||||
|
} |
||||||
|
|
||||||
|
private void updateLayout() { |
||||||
|
for (Component com : layerPane.getComponents()) { |
||||||
|
com.revalidate(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置遮罩父窗口 |
||||||
|
* |
||||||
|
* @param container 遮罩父容器 |
||||||
|
*/ |
||||||
|
public static void install(Container container) { |
||||||
|
instance = new GlassPopupManager(); |
||||||
|
Window window; |
||||||
|
if (container instanceof Window) { |
||||||
|
window = (Window) container; |
||||||
|
} else { |
||||||
|
window = SwingUtilities.getWindowAncestor(container); |
||||||
|
} |
||||||
|
instance.windowSnapshots = new WindowSnapshots(window); |
||||||
|
|
||||||
|
window.addWindowStateListener(new WindowAdapter() { |
||||||
|
@Override |
||||||
|
public void windowStateChanged(WindowEvent e) { |
||||||
|
SwingUtilities.invokeLater(() -> { |
||||||
|
instance.updateLayout(); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane(container); |
||||||
|
instance.contentPane = rootPane.getContentPane(); |
||||||
|
rootPane.setGlassPane(instance.layerPane); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
* @param name 组件名 |
||||||
|
*/ |
||||||
|
public static void showPopup(GlassPaneChild component, String name) { |
||||||
|
if (component.getMouseListeners().length == 0) { |
||||||
|
component.addMouseListener(new MouseAdapter() { |
||||||
|
}); |
||||||
|
} |
||||||
|
instance.addAndShowPopup(component, name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 显示组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
*/ |
||||||
|
public static void showPopup(GlassPaneChild component) { |
||||||
|
showPopup(component, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 压栈,放入弹窗组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
* @param name 组件名 |
||||||
|
*/ |
||||||
|
public static void push(GlassPaneChild component, String name) { |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com.getName() != null && com.getName().equals(name)) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
popup.getComponentLayer().pushComponent(component); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 出栈,弹出弹窗组件 |
||||||
|
* |
||||||
|
* @param name 组件名 |
||||||
|
*/ |
||||||
|
public static void pop(String name) { |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com.getName() != null && com.getName().equals(name)) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
popup.getComponentLayer().popComponent(); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 出栈,弹出弹窗组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
*/ |
||||||
|
public static void pop(Component component) { |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
if (popup.getComponent() == component) { |
||||||
|
popup.getComponentLayer().popComponent(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 基于序号关闭弹窗组件 |
||||||
|
* |
||||||
|
* @param index 序号 |
||||||
|
*/ |
||||||
|
public static void closePopup(int index) { |
||||||
|
index = instance.layerPane.getComponentCount() - 1 - index; |
||||||
|
if (index >= 0 && index < instance.layerPane.getComponentCount()) { |
||||||
|
if (instance.layerPane.getComponent(index) instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) instance.layerPane.getComponent(index); |
||||||
|
popup.setShowPopup(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 基于名称关闭组件 |
||||||
|
* |
||||||
|
* @param name 组件名称 |
||||||
|
*/ |
||||||
|
public static void closePopup(String name) { |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com.getName() != null && com.getName().equals(name)) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
popup.setShowPopup(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 关闭组件 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
*/ |
||||||
|
public static void closePopup(Component component) { |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
if (popup.getComponent() == component) { |
||||||
|
popup.setShowPopup(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 关闭最后一个组件 |
||||||
|
*/ |
||||||
|
public static void closePopupLast() { |
||||||
|
closePopup(getPopupCount() - 1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 关闭全部组件 |
||||||
|
*/ |
||||||
|
public static void closePopupAll() { |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
popup.setShowPopup(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 基于组件名,判断组件是否显示 |
||||||
|
* |
||||||
|
* @param name 组件名 |
||||||
|
* @return 是否显示 |
||||||
|
*/ |
||||||
|
public static boolean isShowing(String name) { |
||||||
|
boolean act = false; |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com.getName() != null && com.getName().equals(name)) { |
||||||
|
act = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return act; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断组件是否显示 |
||||||
|
* |
||||||
|
* @param component 组件 |
||||||
|
* @return 是否显示 |
||||||
|
*/ |
||||||
|
public static boolean isShowing(Component component) { |
||||||
|
boolean act = false; |
||||||
|
for (Component com : instance.layerPane.getComponents()) { |
||||||
|
if (com instanceof GlassPopup) { |
||||||
|
GlassPopup popup = (GlassPopup) com; |
||||||
|
if (popup.getComponent() == component) { |
||||||
|
act = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return act; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取弹窗组件数量 |
||||||
|
* |
||||||
|
* @return 弹窗数量 |
||||||
|
*/ |
||||||
|
public static int getPopupCount() { |
||||||
|
return instance.layerPane.getComponentCount(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 移除组件 |
||||||
|
* |
||||||
|
* @param popup 弹窗组件 |
||||||
|
*/ |
||||||
|
protected synchronized void removePopup(Component popup) { |
||||||
|
layerPane.remove(popup); |
||||||
|
if (layerPane.getComponentCount() == 0) { |
||||||
|
layerPane.setVisible(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
|
||||||
|
import javax.swing.ImageIcon; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* 图片遮罩层,支撑静态图片及动态GIF |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 11.0 |
||||||
|
* Created on 2024/12/20 |
||||||
|
*/ |
||||||
|
public class ImageChild extends GlassPaneChild { |
||||||
|
|
||||||
|
private static final String DEFAULT_LOADING_PATH = "com/fine/component/pop/loading.gif"; |
||||||
|
|
||||||
|
public ImageChild() { |
||||||
|
this(DEFAULT_LOADING_PATH); |
||||||
|
} |
||||||
|
|
||||||
|
public ImageChild(String imgPath) { |
||||||
|
setLayout(new BorderLayout()); |
||||||
|
setBorder(new ScaledEmptyBorder(8, 8, 8, 8)); |
||||||
|
ImageIcon icon = new ImageIcon(Objects.requireNonNull(getClass().getClassLoader().getResource(imgPath))); |
||||||
|
add(new UILabel(icon), BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import com.fine.theme.utils.FineUIScale; |
||||||
|
import com.formdev.flatlaf.util.ScaledEmptyBorder; |
||||||
|
import com.fr.concurrent.NamedThreadFactory; |
||||||
|
import com.fr.design.locale.impl.SupportLocaleImpl; |
||||||
|
import com.fr.design.ui.util.UIUtil; |
||||||
|
import com.fr.design.utils.DesignUtils; |
||||||
|
import com.fr.general.FRFont; |
||||||
|
import com.fr.general.locale.LocaleCenter; |
||||||
|
|
||||||
|
import javax.swing.JLabel; |
||||||
|
import javax.swing.JProgressBar; |
||||||
|
import javax.swing.plaf.ColorUIResource; |
||||||
|
|
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Font; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
import static com.fine.swing.ui.layout.Layouts.cell; |
||||||
|
import static com.fine.swing.ui.layout.Layouts.column; |
||||||
|
import static com.fine.swing.ui.layout.Layouts.flex; |
||||||
|
import static com.fine.swing.ui.layout.Layouts.row; |
||||||
|
import static com.fine.theme.utils.FineUIScale.scale; |
||||||
|
|
||||||
|
/** |
||||||
|
* 进度条遮罩组件 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 11.0 |
||||||
|
* Created on 2024/12/13 |
||||||
|
*/ |
||||||
|
public class ProgressChild extends GlassPaneChild { |
||||||
|
|
||||||
|
private int progress; |
||||||
|
protected JLabel text; |
||||||
|
protected JProgressBar progressBar; |
||||||
|
|
||||||
|
private int step = 10; |
||||||
|
private static final int STEP_HEARTBEAT = 40; |
||||||
|
|
||||||
|
private static final int FONT_RGB = 333334; |
||||||
|
private static final int FONT_SIZE = 14; |
||||||
|
|
||||||
|
private static final String FONT_NAME = "Dialog"; |
||||||
|
|
||||||
|
public ProgressChild(String text) { |
||||||
|
this(text, DesignUtils.getDefaultGUIFont() |
||||||
|
.applySize(scale(FONT_SIZE)) |
||||||
|
.applyForeground(new ColorUIResource(FONT_RGB))); |
||||||
|
} |
||||||
|
|
||||||
|
public ProgressChild(String text, Font font) { |
||||||
|
initComponents(text, font); |
||||||
|
initLayout(); |
||||||
|
initScheduler(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initComponents(String text, Font font) { |
||||||
|
this.text = new JLabel(text); |
||||||
|
this.text.setFont(font); |
||||||
|
//由于默认值的字体不支持韩文,所以要对韩文单独生成字体
|
||||||
|
LocaleCenter.buildAction(() -> { |
||||||
|
FRFont frFont = FRFont.getInstance().applySize(FONT_SIZE) |
||||||
|
.applyForeground(new ColorUIResource(FONT_RGB)).applyName(FONT_NAME); |
||||||
|
this.text.setFont(frFont); |
||||||
|
}, SupportLocaleImpl.SUPPORT_KOREA); |
||||||
|
this.progressBar = new JProgressBar(); |
||||||
|
progressBar.setBorderPainted(false); |
||||||
|
progressBar.setOpaque(false); |
||||||
|
progressBar.setBorder(null); |
||||||
|
progressBar.setMaximum(1000); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置进度条最大加载时间,单位为s |
||||||
|
* |
||||||
|
* @param maxWait 最大等待时间 |
||||||
|
* @return 进度条遮罩层 |
||||||
|
*/ |
||||||
|
public ProgressChild setMaxWait(int maxWait) { |
||||||
|
this.step = progressBar.getMaximum() * STEP_HEARTBEAT / (maxWait * 1000); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
private void initLayout() { |
||||||
|
setLayout(new BorderLayout()); |
||||||
|
setPreferredSize(FineUIScale.createScaleDimension(400, 100)); |
||||||
|
add(column(10, |
||||||
|
cell(progressBar).weight(1), row(flex(), cell(text), flex()).weight(1.5) |
||||||
|
).with(it -> it.setBorder(new ScaledEmptyBorder(30, 30, 20, 30))).getComponent(), |
||||||
|
BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
private void initScheduler() { |
||||||
|
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, |
||||||
|
new NamedThreadFactory(getClass().getSimpleName())); |
||||||
|
scheduler.scheduleWithFixedDelay(() -> { |
||||||
|
if (isComplete() && !scheduler.isShutdown()) { |
||||||
|
scheduler.shutdown(); |
||||||
|
return; |
||||||
|
} |
||||||
|
UIUtil.invokeLaterIfNeeded(() -> progressBar.setValue(incrementProgress())); |
||||||
|
}, 0, STEP_HEARTBEAT, TimeUnit.MILLISECONDS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 递增进度条 |
||||||
|
* |
||||||
|
* @return 进度值 |
||||||
|
*/ |
||||||
|
public int incrementProgress() { |
||||||
|
if (progress != progressBar.getMaximum()) { |
||||||
|
progress += step; |
||||||
|
} |
||||||
|
return progress; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onClose() { |
||||||
|
this.progress = progressBar.getMaximum(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重置进度条 |
||||||
|
*/ |
||||||
|
public void reset() { |
||||||
|
this.progress = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 进度是否已结束 |
||||||
|
* |
||||||
|
* @return 进度结束状态 |
||||||
|
*/ |
||||||
|
public boolean isComplete() { |
||||||
|
return this.progress >= progressBar.getMaximum(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package com.fine.component.popup; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JLayeredPane; |
||||||
|
import javax.swing.JRootPane; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Window; |
||||||
|
import java.awt.image.VolatileImage; |
||||||
|
|
||||||
|
/** |
||||||
|
* WindowSnapshots, 捕获 Window 的快照 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 12.0 |
||||||
|
* Created on 2024/12/12 |
||||||
|
*/ |
||||||
|
public class WindowSnapshots { |
||||||
|
|
||||||
|
private final Window window; |
||||||
|
private JComponent snapshotLayer; |
||||||
|
private boolean inShowSnapshot; |
||||||
|
|
||||||
|
public WindowSnapshots(Window window) { |
||||||
|
this.window = window; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建窗口快照并将其显示为覆盖层 |
||||||
|
*/ |
||||||
|
public void createSnapshot() { |
||||||
|
if (inShowSnapshot) { |
||||||
|
return; |
||||||
|
} |
||||||
|
inShowSnapshot = true; |
||||||
|
if ((window != null && window.isShowing())) { |
||||||
|
VolatileImage snapshot = window.createVolatileImage(window.getWidth(), window.getHeight()); |
||||||
|
if (snapshot != null) { |
||||||
|
JLayeredPane layeredPane = getLayeredPane(); |
||||||
|
if (layeredPane != null) { |
||||||
|
layeredPane.paint(snapshot.getGraphics()); |
||||||
|
snapshotLayer = new JComponent() { |
||||||
|
@Override |
||||||
|
public void paint(Graphics g) { |
||||||
|
if (snapshot.contentsLost()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
g.drawImage(snapshot, 0, 0, null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeNotify() { |
||||||
|
super.removeNotify(); |
||||||
|
snapshot.flush(); |
||||||
|
} |
||||||
|
}; |
||||||
|
snapshotLayer.setSize(layeredPane.getSize()); |
||||||
|
layeredPane.add(snapshotLayer, Integer.valueOf(JLayeredPane.DRAG_LAYER + 1)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 移除窗口快照覆盖层 |
||||||
|
*/ |
||||||
|
public void removeSnapshot() { |
||||||
|
if (snapshotLayer == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
JLayeredPane layeredPane = getLayeredPane(); |
||||||
|
if (layeredPane != null) { |
||||||
|
layeredPane.remove(snapshotLayer); |
||||||
|
} |
||||||
|
inShowSnapshot = false; |
||||||
|
} |
||||||
|
|
||||||
|
private JLayeredPane getLayeredPane() { |
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane(window); |
||||||
|
if (rootPane != null) { |
||||||
|
return rootPane.getLayeredPane(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
package com.fine.theme.utils; |
||||||
|
|
||||||
|
import com.fine.component.popup.ProgressChild; |
||||||
|
import com.fine.component.popup.GlassPopupManager; |
||||||
|
import com.fine.component.popup.GlassPaneChild; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.ui.util.UIUtil; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import java.awt.Container; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
import java.util.function.Consumer; |
||||||
|
|
||||||
|
/** |
||||||
|
* 遮罩层加载器 |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 11.0 |
||||||
|
* Created on 2024/12/06 |
||||||
|
*/ |
||||||
|
public class GlassLayerLoader { |
||||||
|
|
||||||
|
private static volatile GlassLayerLoader loader = null; |
||||||
|
|
||||||
|
private GlassLayerLoader() { |
||||||
|
} |
||||||
|
|
||||||
|
public static GlassLayerLoader getInstance() { |
||||||
|
if (loader == null) { |
||||||
|
synchronized (GlassLayerLoader.class) { |
||||||
|
if (loader == null) { |
||||||
|
loader = new GlassLayerLoader(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return loader; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 带有加载面板的后台任务执行器(无回调) |
||||||
|
* |
||||||
|
* @param task 耗时任务 |
||||||
|
* @param container 遮罩的底层容器 |
||||||
|
* @param loadingPane 显示加载动画的 GlassPaneChild |
||||||
|
* @param <T> 任务的返回值类型 |
||||||
|
*/ |
||||||
|
public <T> void runWithLoader(Callable<T> task, Container container, GlassPaneChild loadingPane) { |
||||||
|
// 默认无回调
|
||||||
|
runWithLoader(task, container, loadingPane, result -> {}, e -> {}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 带有加载面板的后台任务执行器(有成功和失败的回调) |
||||||
|
* |
||||||
|
* @param task 耗时任务 |
||||||
|
* @param container 遮罩的底层容器 |
||||||
|
* @param loadingPane 显示加载动画的 GlassPaneChild |
||||||
|
* @param <T> 任务的返回值类型 |
||||||
|
*/ |
||||||
|
public <T> void runWithLoader(Callable<T> task, Container container, GlassPaneChild loadingPane, |
||||||
|
Consumer<T> onSuccess, Consumer<Throwable> onFailure) { |
||||||
|
// 安装并显示加载面板
|
||||||
|
UIUtil.invokeAndWaitIfNeeded(() -> { |
||||||
|
GlassPopupManager.install(container); |
||||||
|
GlassPopupManager.showPopup(loadingPane); |
||||||
|
}); |
||||||
|
SwingWorker<T, Void> worker = new SwingWorker<T, Void>() { |
||||||
|
@Override |
||||||
|
protected T doInBackground() throws Exception { |
||||||
|
return task.call(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void done() { |
||||||
|
try { |
||||||
|
T t = get(); |
||||||
|
onSuccess.accept(t); |
||||||
|
} catch (Exception e) { |
||||||
|
onFailure.accept(e); |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage()); |
||||||
|
} finally { |
||||||
|
loadingPane.onClose(); |
||||||
|
GlassPopupManager.closePopup(loadingPane); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
worker.execute(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 进度条遮罩加载动画 |
||||||
|
* |
||||||
|
* @param task 耗时任务 |
||||||
|
* @param info 显示加载面板的 JFrame |
||||||
|
* @param maxWait 最大等待时间 |
||||||
|
* @param <T> 任务的返回值类型 |
||||||
|
*/ |
||||||
|
public <T> void runWithProgressLoader(Callable<T> task, String info, int maxWait) { |
||||||
|
runWithLoader(task, DesignerContext.getDesignerFrame(), new ProgressChild(info).setMaxWait(maxWait)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,62 @@ |
|||||||
|
package com.fr.design.gui.storybook.components; |
||||||
|
|
||||||
|
import com.fine.component.popup.ImageChild; |
||||||
|
import com.fine.component.popup.ProgressChild; |
||||||
|
import com.fine.theme.utils.GlassLayerLoader; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.storybook.StoryBoard; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.JFrame; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
|
||||||
|
import java.awt.Font; |
||||||
|
|
||||||
|
import static com.fine.swing.ui.layout.Layouts.cell; |
||||||
|
|
||||||
|
/** |
||||||
|
* GlassLoaderStoryBoard |
||||||
|
* |
||||||
|
* @author Levy.Xie |
||||||
|
* @since 11.0 |
||||||
|
* Created on 2024/12/19 |
||||||
|
*/ |
||||||
|
public class GlassLoaderStoryBoard extends StoryBoard { |
||||||
|
|
||||||
|
public GlassLoaderStoryBoard() { |
||||||
|
super("遮罩动画"); |
||||||
|
add( |
||||||
|
cell(createProgressBtn()), |
||||||
|
cell(createImageBtn("GIF图片遮罩-1", null)), |
||||||
|
cell(createImageBtn("GIF图片遮罩-2", "com/fr/design/gui/storybook/components/loadingBar.gif")) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private UIButton createProgressBtn() { |
||||||
|
UIButton btn = new UIButton("进度条遮罩"); |
||||||
|
btn.addActionListener(e -> { |
||||||
|
GlassLayerLoader.getInstance().runWithLoader(() -> { |
||||||
|
Thread.sleep(3000); |
||||||
|
return null; |
||||||
|
}, (JFrame) SwingUtilities.getWindowAncestor(this), new ProgressChild("running...", |
||||||
|
new Font("宋体", Font.PLAIN, 14)).setMaxWait(3)); |
||||||
|
}); |
||||||
|
return btn; |
||||||
|
} |
||||||
|
|
||||||
|
private UIButton createImageBtn(String name, String path) { |
||||||
|
UIButton btn = new UIButton(name); |
||||||
|
btn.addActionListener(e -> { |
||||||
|
showGlassLoadingPane(path); |
||||||
|
}); |
||||||
|
return btn; |
||||||
|
} |
||||||
|
|
||||||
|
private void showGlassLoadingPane(String gifPath) { |
||||||
|
GlassLayerLoader.getInstance().runWithLoader(() -> { |
||||||
|
Thread.sleep(3000); |
||||||
|
return null; |
||||||
|
}, (JFrame) SwingUtilities.getWindowAncestor(this), StringUtils.isEmpty(gifPath) ? new ImageChild() : new ImageChild(gifPath)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
After Width: | Height: | Size: 609 B |
Loading…
Reference in new issue