Skip to content

Instantly share code, notes, and snippets.

@fmarot
Created May 2, 2017 08:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fmarot/87cf6642b5a555874d11242de899402a to your computer and use it in GitHub Desktop.
Save fmarot/87cf6642b5a555874d11242de899402a to your computer and use it in GitHub Desktop.
A Swing layout able to lay pages horizontally inside an horizontally scrollable panel
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PrintPreviewPagesTester {
public PrintPreviewPagesTester() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PrintPreviewPagesController controller = new PrintPreviewPagesController();
Container contentPane = frame.getContentPane();
Component pagesPanel = controller.getPagesPanel();
contentPane.add(pagesPanel, BorderLayout.CENTER);
Component actionPanel = controller.getActionPanel();
contentPane.add(actionPanel, BorderLayout.SOUTH);
frame.setVisible(true);
frame.setSize(300, 400);
frame.setState(Frame.MAXIMIZED_BOTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new PrintPreviewPagesTester());
}
public class PrintPreviewPagesPanel {
private JScrollPane mainView;
private JPanel pagesPanel;
public PrintPreviewPagesPanel() {
pagesPanel = new ScrollablePanel(new PrintPreviewPagesLayout());
mainView = new JScrollPane(pagesPanel);
mainView.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
mainView.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
mainView.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
updatePagesPanelSize(); // first update the size of the pages panel...
// pagesPanel.doLayout(); // ... then resize the pages inside
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentHidden(ComponentEvent e) {
}
});
}
public Component getComponent() {
return mainView;
}
private void updatePagesPanelSize() {
int mainViewHeight = mainView.getHeight();
int mainViewWidth = mainView.getWidth();
log.info("mainViewWidth={} - mainViewHeight={}", mainViewWidth, mainViewHeight);
int pagesPanelTargetHeight = mainViewHeight;
int pagesPanelTargetWidth = -1;
if (pagesPanel.getComponentCount() >= 1) {
Rectangle lastComponentBounds = pagesPanel.getComponent(pagesPanel.getComponentCount() - 1).getBounds();
pagesPanelTargetWidth = lastComponentBounds.width + lastComponentBounds.x;
} else {
pagesPanelTargetWidth = mainViewWidth;
}
Dimension pagesPanelTargetDimension = new Dimension(pagesPanelTargetWidth, pagesPanelTargetHeight);
log.info("pagesPanelTargetDimension={}", pagesPanelTargetDimension);
pagesPanel.setPreferredSize(pagesPanelTargetDimension);
pagesPanel.setSize(pagesPanelTargetDimension);
}
public void addPage(JPanel newPage) {
pagesPanel.add(newPage);
// mainView.invalidate();
updatePagesPanelSize(); // first resize the panel (with wrong content but to position the height)
pagesPanel.doLayout(); // then do a layout pass to position the pages (their setBounds method will be called)...
updatePagesPanelSize(); // .. then resize the pagesPanel to match its content
}
}
public class PrintPreviewPagesLayout implements LayoutManager {
public PrintPreviewPagesLayout() {
}
@Override
public void addLayoutComponent(String arg0, Component arg1) {
}
@Override
public void layoutContainer(Container parent) {
Insets insets = parent.getInsets();
// int maxWidth = parent.getWidth() - (insets.left + insets.right);
int targetHeight = parent.getHeight() - (insets.top + insets.bottom);
int currentHorizontalPosition = 0;
int nbComponents = parent.getComponentCount();
for (int i = 0; i < nbComponents; i++) {
Component comp = parent.getComponent(i);
Dimension compPreferredSize = comp.getPreferredSize();
log.info("targetHeight: {} ", targetHeight);
double targetWidth = (compPreferredSize.getWidth() * targetHeight) / compPreferredSize.getHeight();
comp.setBounds(currentHorizontalPosition, 0, (int) targetWidth, targetHeight);
log.info("Component Bounds: {}+{} ", currentHorizontalPosition, targetWidth);
currentHorizontalPosition += targetWidth;
}
// parent.setPreferredSize(new Dimension(currentHorizontalPosition, targetHeight));
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
return new Dimension(10, 10);
}
@Override
public void removeLayoutComponent(Component parent) {
}
}
public class PrintPreviewPagesController {
/** 1st view of the controller */
private PrintPreviewPagesPanel pagesPanel;
/** 2nd view of the controller */
private JPanel actionPanel;
/** last color used to color a page */
private Color lastColor = new Color(50, 100, 150);
public PrintPreviewPagesController() {
pagesPanel = new PrintPreviewPagesPanel();
actionPanel = buildActionPanel();
}
public Component getPagesPanel() {
return pagesPanel.getComponent();
}
public Component getActionPanel() {
return actionPanel;
}
private JPanel buildActionPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.darkGray);
JButton button = new JButton("add page");
button.addActionListener(event -> addPage());
panel.add(button);
return panel;
}
private void addPage() {
JPanel newPage = createNewPage();
pagesPanel.addPage(newPage);
}
private JPanel createNewPage() {
lastColor = new Color(
modifyColorComponent(lastColor.getRed()),
modifyColorComponent(lastColor.getGreen()),
modifyColorComponent(lastColor.getBlue()));
JPanel newPage = new JPanel();
newPage.setBorder(BorderFactory.createLineBorder(Color.black, 10));
newPage.setBackground(lastColor);
newPage.setPreferredSize(new Dimension(20, 30));
return newPage;
}
private int modifyColorComponent(int value) {
return (value + 50) % 255;
}
}
/** see http://stackoverflow.com/questions/10331129/jscrollpane-resize-containing-jpanel-when-scrollbars-appear */
public class ScrollablePanel extends JPanel implements Scrollable {
public ScrollablePanel(PrintPreviewPagesLayout printPreviewPagesLayout) {
super(printPreviewPagesLayout);
setBackground(Color.cyan);
}
@Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 200);
}
@Override
public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) {
return 100;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return true;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
@Override
public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) {
return 100;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment