package com.intellij.flex.uiDesigner.preview;
import com.intellij.flex.uiDesigner.FlashUIDesignerBundle;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.LoadingDecorator;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBLayeredPane;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.util.ui.AbstractLayoutManager;
import com.intellij.util.ui.AsyncProcessIcon;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
class MxmlPreviewPanel extends JBLayeredPane implements Disposable {
private static final double EPS = 0.0000001;
private static final double MAX_ZOOM_FACTOR = 2.0;
private static final double ZOOM_STEP = 1.25;
private BufferedImage image;
private double zoomFactor = 1.0;
private boolean zoomToFit = true;
private final LoadingDecorator loadingDecorator;
private JLabel cannotRenderText;
private final JPanel imagePanel = new JPanel() {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null) {
return;
}
final Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(image, 0, 0, getWidth(), getHeight(), 0, 0, image.getWidth(), image.getHeight(), null);
}
};
public MxmlPreviewPanel() {
setBackground(JBColor.WHITE);
setOpaque(true);
imagePanel.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, JBColor.GRAY));
JPanel imageWrapper = new JPanel(new MyLayout());
imageWrapper.add(imagePanel);
loadingDecorator = new LoadingDecorator(imageWrapper, this, 1000) {
@Override
protected NonOpaquePanel customizeLoadingLayer(JPanel parent, JLabel text, AsyncProcessIcon icon) {
NonOpaquePanel panel = super.customizeLoadingLayer(parent, text, icon);
JBLoadingPanel.customizeStatusText(text);
return panel;
}
};
loadingDecorator.setLoadingText(FlashUIDesignerBundle.message("rendering"));
add(loadingDecorator.getComponent(), 0, JLayeredPane.DEFAULT_LAYER);
}
@Override
public void doLayout() {
super.doLayout();
for (int i = 0; i < getComponentCount(); i++) {
getComponent(i).setBounds(0, 0, getWidth(), getHeight());
}
}
public LoadingDecorator getLoadingDecorator() {
return loadingDecorator;
}
public BufferedImage getImage() {
return image;
}
public void setImage(@Nullable BufferedImage image) {
this.image = image;
clearCannotRender();
doRevalidate();
}
public void clearCannotRender() {
if (cannotRenderText != null) {
remove(cannotRenderText);
cannotRenderText = null;
}
}
public void showCannotRender() {
if (cannotRenderText != null) {
return;
}
cannotRenderText = new JLabel(FlashUIDesignerBundle.message("cannot.render"), SwingConstants.CENTER);
JBLoadingPanel.customizeStatusText(cannotRenderText);
add(cannotRenderText, 1, JLayeredPane.MODAL_LAYER);
}
private void doRevalidate() {
revalidate();
updateImageSize();
repaint();
}
void updateImageSize() {
if (image == null) {
imagePanel.setSize(0, 0);
}
else {
imagePanel.setSize(getScaledDimension());
}
}
private Dimension getScaledDimension() {
if (zoomToFit) {
final Dimension panelSize = getParent().getSize();
if (image.getWidth() <= panelSize.width && image.getHeight() <= panelSize.height) {
return new Dimension(image.getWidth(), image.getHeight());
}
if (image.getWidth() <= panelSize.width) {
final double f = panelSize.getHeight() / image.getHeight();
return new Dimension((int)(image.getWidth() * f), (int)(image.getHeight() * f));
}
else if (image.getHeight() <= panelSize.height) {
final double f = panelSize.getWidth() / image.getWidth();
return new Dimension((int)(image.getWidth() * f), (int)(image.getHeight() * f));
}
double f = panelSize.getWidth() / image.getWidth();
int candidateWidth = (int)(image.getWidth() * f);
int candidateHeight = (int)(image.getHeight() * f);
if (candidateWidth <= panelSize.getWidth() && candidateHeight <= panelSize.getHeight()) {
return new Dimension(candidateWidth, candidateHeight);
}
f = panelSize.getHeight() / image.getHeight();
return new Dimension((int)(image.getWidth() * f), (int)(image.getHeight() * f));
}
return new Dimension((int)(image.getWidth() * zoomFactor), (int)(image.getHeight() * zoomFactor));
}
private void setZoomFactor(double zoomFactor) {
this.zoomFactor = zoomFactor;
doRevalidate();
}
private double computeCurrentZoomFactor() {
if (image == null) {
return zoomFactor;
}
return (double)imagePanel.getWidth() / (double)image.getWidth();
}
private double getZoomFactor() {
return zoomToFit ? computeCurrentZoomFactor() : zoomFactor;
}
public void zoomOut() {
setZoomFactor(Math.max(getMinZoomFactor(), zoomFactor / ZOOM_STEP));
}
public boolean canZoomOut() {
return image != null && zoomFactor > getMinZoomFactor() + EPS;
}
private double getMinZoomFactor() {
return Math.min(1.0, (double)getParent().getWidth() / (double)image.getWidth());
}
public void zoomIn() {
if (zoomToFit) {
zoomToFit = false;
setZoomFactor(computeCurrentZoomFactor() * ZOOM_STEP);
return;
}
setZoomFactor(zoomFactor * ZOOM_STEP);
}
public boolean canZoomIn() {
return getZoomFactor() * ZOOM_STEP < MAX_ZOOM_FACTOR - EPS;
}
public void zoomActual() {
if (image == null) {
return;
}
if (zoomToFit && imagePanel.getWidth() >= image.getWidth() && imagePanel.getHeight() >= image.getHeight()) {
return;
}
zoomToFit = false;
setZoomFactor(1.0);
}
public void setZoomToFit(boolean zoomToFit) {
this.zoomToFit = zoomToFit;
doRevalidate();
}
public boolean isZoomToFit() {
return zoomToFit;
}
@Override
public void dispose() {
}
private class MyLayout extends AbstractLayoutManager {
@Override
public Dimension preferredLayoutSize(Container parent) {
return parent.getComponent(0).getSize();
}
@Override
public void layoutContainer(Container target) {
Component component = target.getComponent(0);
Dimension size = image == null ? new Dimension() : getScaledDimension();
int diff = target.getWidth() - size.width;
component.setSize(size);
component.setLocation(diff > 0 ? diff / 2 : 0, component.getY());
}
}
}