Ticket #13198: patch-fix-flow-layout-wrap.patch

File patch-fix-flow-layout-wrap.patch, 10.2 KB (added by michael2402, 5 years ago)
  • src/org/openstreetmap/josm/gui/io/UploadDialog.java

    diff --git a/src/org/openstreetmap/josm/gui/io/UploadDialog.java b/src/org/openstreetmap/josm/gui/io/UploadDialog.java
    index ed679fa..3312022 100644
    a b import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66import static org.openstreetmap.josm.tools.I18n.trn;
    77
    8 import java.awt.BorderLayout;
    98import java.awt.Component;
    109import java.awt.Dimension;
    1110import java.awt.FlowLayout;
    import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 
    5958import org.openstreetmap.josm.tools.InputMapUtils;
    6059import org.openstreetmap.josm.tools.Utils;
    6160import org.openstreetmap.josm.tools.WindowGeometry;
     61import org.openstreetmap.josm.tools.MultiLineFlowLayout;
    6262
    6363/**
    6464 * This is a dialog for entering upload options like the parameters for
    public class UploadDialog extends AbstractUploadDialog implements PropertyChange 
    165165        tpConfigPanels.setToolTipTextAt(3, tr("Configure advanced settings"));
    166166
    167167        pnl.add(tpConfigPanels, GBC.eol().fill(GBC.HORIZONTAL));
     168
     169        pnl.add(buildActionPanel(), GBC.eol().fill(GBC.HORIZONTAL));
    168170        return pnl;
    169171    }
    170172
    public class UploadDialog extends AbstractUploadDialog implements PropertyChange 
    174176     * @return The panel with the OK and CANCEL buttons
    175177     */
    176178    protected JPanel buildActionPanel() {
    177         JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
     179        JPanel pnl = new JPanel(new MultiLineFlowLayout(FlowLayout.CENTER));
    178180        pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    179181
    180182        // -- upload button
    public class UploadDialog extends AbstractUploadDialog implements PropertyChange 
    201203     */
    202204    protected void build() {
    203205        setTitle(tr("Upload to ''{0}''", OsmApi.getOsmApi().getBaseUrl()));
    204         getContentPane().setLayout(new BorderLayout());
    205         getContentPane().add(buildContentPanel(), BorderLayout.CENTER);
    206         getContentPane().add(buildActionPanel(), BorderLayout.SOUTH);
     206        setContentPane(buildContentPanel());
    207207
    208208        addWindowListener(new WindowEventHandler());
    209209
  • new file src/org/openstreetmap/josm/tools/MultiLineFlowLayout.java

    diff --git a/src/org/openstreetmap/josm/tools/MultiLineFlowLayout.java b/src/org/openstreetmap/josm/tools/MultiLineFlowLayout.java
    new file mode 100644
    index 0000000..4892058
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.tools;
     3
     4import java.awt.Component;
     5import java.awt.Container;
     6import java.awt.Dimension;
     7import java.awt.FlowLayout;
     8import java.awt.Insets;
     9import java.util.function.Function;
     10
     11/**
     12 * This is an extension of the flow layout that preferes wraping the text instead of increasing the component width when there is not enough
     13 * space.
     14 * <p>
     15 * This allows for a better preffered size computation. It should be used in all places where a flow layout fills the full width of the parent
     16 * container.
     17 * <p>
     18 * This does not support baseline alignment.
     19 * @author Michael Zangl
     20 * @since xxx
     21 */
     22public class MultiLineFlowLayout extends FlowLayout {
     23    /**
     24     * Same as {@link FlowLayout#FlowLayout()}
     25     */
     26    public MultiLineFlowLayout() {
     27        super();
     28    }
     29
     30    /**
     31     * Same as {@link FlowLayout#FlowLayout(int, int, int)}
     32     * @param align Alignment
     33     * @param hgap horizontal gap
     34     * @param vgap vertical gap
     35     */
     36    public MultiLineFlowLayout(int align, int hgap, int vgap) {
     37        super(align, hgap, vgap);
     38    }
     39
     40    /**
     41     * Same as {@link FlowLayout#FlowLayout(int)}
     42     * @param align Alignment
     43     */
     44    public MultiLineFlowLayout(int align) {
     45        super(align);
     46    }
     47
     48    @Override
     49    public Dimension preferredLayoutSize(Container target) {
     50        return getLayoutSize(target, c -> c.getPreferredSize());
     51    }
     52
     53    @Override
     54    public Dimension minimumLayoutSize(Container target) {
     55        return getLayoutSize(target, c -> c.getMinimumSize());
     56    }
     57
     58    private Dimension getLayoutSize(Container target, Function<Component, Dimension> baseSize) {
     59        synchronized (target.getTreeLock()) {
     60            int outerWidth = getWidthOf(target);
     61
     62            Insets insets = target.getInsets();
     63            int containerWidth = outerWidth - insets.left - insets.right - getHgap() * 2;
     64
     65            int x = 0;
     66            int totalHeight = insets.top + insets.bottom + getVgap() * 2;
     67            int rowHeight = 0;
     68            for (int i = 0; i < target.getComponentCount(); i++) {
     69                Component child = target.getComponent(i);
     70                if (!child.isVisible()) {
     71                    continue;
     72                }
     73                Dimension size = baseSize.apply(child);
     74                if (x != 0) {
     75                    x += getHgap();
     76                }
     77                x += size.getWidth();
     78                if (x > containerWidth) {
     79                    totalHeight += rowHeight + getVgap();
     80                    rowHeight = 0;
     81                    x = 0;
     82                }
     83
     84                rowHeight = Math.max(rowHeight, size.height);
     85            }
     86            totalHeight += rowHeight;
     87
     88            return new Dimension(outerWidth, totalHeight);
     89        }
     90    }
     91
     92    private static int getWidthOf(Container target) {
     93        Container current = target;
     94        while (current.getWidth() == 0 && current.getParent() != null) {
     95            current = current.getParent();
     96        }
     97        int width = current.getWidth();
     98        if (width == 0) {
     99            return Integer.MAX_VALUE;
     100        } else {
     101            return width;
     102        }
     103    }
     104
     105    @Override
     106    public String toString() {
     107        return "WrapingFlowLayout [align=" + getAlignment() + "]";
     108    }
     109
     110}
  • new file test/unit/org/openstreetmap/josm/tools/MultiLineFlowLayoutTest.java

    diff --git a/test/unit/org/openstreetmap/josm/tools/MultiLineFlowLayoutTest.java b/test/unit/org/openstreetmap/josm/tools/MultiLineFlowLayoutTest.java
    new file mode 100644
    index 0000000..11aa5a0
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.tools;
     3
     4import static org.junit.Assert.assertEquals;
     5
     6import java.awt.Dimension;
     7import java.awt.FlowLayout;
     8
     9import javax.swing.BorderFactory;
     10import javax.swing.JPanel;
     11
     12import org.junit.Before;
     13import org.junit.Rule;
     14import org.junit.Test;
     15import org.openstreetmap.josm.testutils.JOSMTestRules;
     16
     17import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     18
     19/**
     20 * Test {@link MultiLineFlowLayout}
     21 * @author Michael Zangl
     22 * @since xxx
     23 */
     24public class MultiLineFlowLayoutTest {
     25    /**
     26     * No special rules.
     27     */
     28    @Rule
     29    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     30    public JOSMTestRules test = new JOSMTestRules();
     31
     32    private static final int TEST_WIDHT = 500;
     33    private JPanel container;
     34
     35    /**
     36     * Prepare test container.
     37     */
     38    @Before
     39    public void setUp() {
     40        JPanel parent = new JPanel();
     41        parent.setBounds(0, 0, TEST_WIDHT, 500);
     42
     43        container = new JPanel(new MultiLineFlowLayout(FlowLayout.CENTER, 0, 0));
     44        parent.add(container);
     45    }
     46
     47    /**
     48     * Test that one line is layed out correctly
     49     */
     50    @Test
     51    public void testOneLine() {
     52        fillOneLine();
     53
     54        container.invalidate();
     55        Dimension preffered = container.getPreferredSize();
     56        assertEquals(TEST_WIDHT, preffered.width);
     57        assertEquals(100, preffered.height);
     58
     59        Dimension minimum = container.getMinimumSize();
     60        assertEquals(TEST_WIDHT, minimum.width);
     61        assertEquals(50, minimum.height);
     62    }
     63
     64    /**
     65     * Test that insets are respected
     66     */
     67    @Test
     68    public void testInsets() {
     69        fillOneLine();
     70
     71        container.setBorder(BorderFactory.createEmptyBorder(3, 0, 7, 0));
     72        container.invalidate();
     73        Dimension preffered = container.getPreferredSize();
     74        assertEquals(TEST_WIDHT, preffered.width);
     75        assertEquals(110, preffered.height);
     76
     77        // This should force wraping
     78        container.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 40));
     79        container.invalidate();
     80        preffered = container.getPreferredSize();
     81        assertEquals(TEST_WIDHT, preffered.width);
     82        assertEquals(200, preffered.height);
     83    }
     84
     85    /**
     86     * Test that gaps are respected
     87     */
     88    @Test
     89    public void testGaps() {
     90        fillOneLine();
     91
     92        container.setLayout(new MultiLineFlowLayout(FlowLayout.LEADING, 20, 10));
     93        container.invalidate();
     94        Dimension preffered = container.getPreferredSize();
     95        assertEquals(TEST_WIDHT, preffered.width);
     96        assertEquals(230, preffered.height);
     97    }
     98
     99    /**
     100     * Test that it behaves the same as FlowLayout for one line.
     101     */
     102    @Test
     103    public void testSameAsFlowLayout() {
     104        fillOneLine();
     105        JPanel childx = new JPanel();
     106        childx.setPreferredSize(new Dimension(300, 100));
     107        childx.setMinimumSize(new Dimension(200, 50));
     108        childx.setVisible(false);
     109        container.add(childx);
     110        container.setBorder(BorderFactory.createEmptyBorder(3, 4, 5, 6));
     111
     112        container.setLayout(new MultiLineFlowLayout(FlowLayout.LEADING, 2, 1));
     113        container.invalidate();
     114        Dimension is = container.getPreferredSize();
     115
     116        container.setLayout(new FlowLayout(FlowLayout.LEADING, 2, 1));
     117        container.invalidate();
     118        Dimension should = container.getPreferredSize();
     119
     120        assertEquals(should.height, is.height);
     121    }
     122
     123    private void fillOneLine() {
     124        JPanel child1 = new JPanel();
     125        child1.setPreferredSize(new Dimension(300, 100));
     126        child1.setMinimumSize(new Dimension(200, 50));
     127        container.add(child1);
     128        JPanel child2 = new JPanel();
     129        child2.setPreferredSize(new Dimension(100, 100));
     130        child1.setMinimumSize(new Dimension(100, 50));
     131        container.add(child2);
     132        JPanel child3 = new JPanel();
     133        child3.setPreferredSize(new Dimension(50, 100));
     134        child1.setMinimumSize(new Dimension(50, 50));
     135        container.add(child3);
     136    }
     137}