]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/WrapLayout.java
Introduce WrapLayout to replace FlowLayout
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / awt / WrapLayout.java
diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/WrapLayout.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/WrapLayout.java
new file mode 100644 (file)
index 0000000..4ff2ee6
--- /dev/null
@@ -0,0 +1,212 @@
+/*******************************************************************************\r
+ * Copyright (c) 2016 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     Semantum Oy - initial API and implementation, courtesy of Rob Camick\r
+ *                   https://tips4java.wordpress.com/2008/11/06/wrap-layout/\r
+ *******************************************************************************/\r
+package org.simantics.utils.ui.awt;\r
+\r
+import java.awt.Component;\r
+import java.awt.Container;\r
+import java.awt.Dimension;\r
+import java.awt.FlowLayout;\r
+import java.awt.Insets;\r
+\r
+import javax.swing.JScrollPane;\r
+import javax.swing.SwingUtilities;\r
+\r
+/**\r
+ *  FlowLayout subclass that fully supports wrapping of components.\r
+ */\r
+public class WrapLayout extends FlowLayout\r
+{\r
+       /**\r
+        * \r
+        */\r
+       private static final long serialVersionUID = -4312800883442354753L;\r
+\r
+       /**\r
+       * Constructs a new <code>WrapLayout</code> with a left\r
+       * alignment and a default 5-unit horizontal and vertical gap.\r
+       */\r
+       public WrapLayout()\r
+       {\r
+               super();\r
+       }\r
+\r
+       /**\r
+       * Constructs a new <code>FlowLayout</code> with the specified\r
+       * alignment and a default 5-unit horizontal and vertical gap.\r
+       * The value of the alignment argument must be one of\r
+       * <code>WrapLayout</code>, <code>WrapLayout</code>,\r
+       * or <code>WrapLayout</code>.\r
+       * @param align the alignment value\r
+       */\r
+       public WrapLayout(int align)\r
+       {\r
+               super(align);\r
+       }\r
+\r
+       /**\r
+       * Creates a new flow layout manager with the indicated alignment\r
+       * and the indicated horizontal and vertical gaps.\r
+       * <p>\r
+       * The value of the alignment argument must be one of\r
+       * <code>WrapLayout</code>, <code>WrapLayout</code>,\r
+       * or <code>WrapLayout</code>.\r
+       * @param align the alignment value\r
+       * @param hgap the horizontal gap between components\r
+       * @param vgap the vertical gap between components\r
+       */\r
+       public WrapLayout(int align, int hgap, int vgap)\r
+       {\r
+               super(align, hgap, vgap);\r
+       }\r
+\r
+       /**\r
+       * Returns the preferred dimensions for this layout given the\r
+       * <i>visible</i> components in the specified target container.\r
+       * @param target the component which needs to be laid out\r
+       * @return the preferred dimensions to lay out the\r
+       * subcomponents of the specified container\r
+       */\r
+       @Override\r
+       public Dimension preferredLayoutSize(Container target)\r
+       {\r
+               return layoutSize(target, true);\r
+       }\r
+\r
+       /**\r
+       * Returns the minimum dimensions needed to layout the <i>visible</i>\r
+       * components contained in the specified target container.\r
+       * @param target the component which needs to be laid out\r
+       * @return the minimum dimensions to lay out the\r
+       * subcomponents of the specified container\r
+       */\r
+       @Override\r
+       public Dimension minimumLayoutSize(Container target)\r
+       {\r
+               Dimension minimum = layoutSize(target, false);\r
+               minimum.width -= (getHgap() + 1);\r
+               return minimum;\r
+       }\r
+\r
+       /**\r
+       * Returns the minimum or preferred dimension needed to layout the target\r
+       * container.\r
+       *\r
+       * @param target target to get layout size for\r
+       * @param preferred should preferred size be calculated\r
+       * @return the dimension to layout the target container\r
+       */\r
+       private Dimension layoutSize(Container target, boolean preferred)\r
+       {\r
+       synchronized (target.getTreeLock())\r
+       {\r
+               //  Each row must fit with the width allocated to the containter.\r
+               //  When the container width = 0, the preferred width of the container\r
+               //  has not yet been calculated so lets ask for the maximum.\r
+\r
+               int targetWidth = target.getSize().width;\r
+               Container container = target;\r
+\r
+               while (container.getSize().width == 0 && container.getParent() != null)\r
+               {\r
+                       container = container.getParent();\r
+               }\r
+\r
+               targetWidth = container.getSize().width;\r
+\r
+               if (targetWidth == 0)\r
+                       targetWidth = Integer.MAX_VALUE;\r
+\r
+               int hgap = getHgap();\r
+               int vgap = getVgap();\r
+               Insets insets = target.getInsets();\r
+               int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);\r
+               int maxWidth = targetWidth - horizontalInsetsAndGap;\r
+\r
+               //  Fit components into the allowed width\r
+\r
+               Dimension dim = new Dimension(0, 0);\r
+               int rowWidth = 0;\r
+               int rowHeight = 0;\r
+\r
+               int nmembers = target.getComponentCount();\r
+\r
+               for (int i = 0; i < nmembers; i++)\r
+               {\r
+                       Component m = target.getComponent(i);\r
+\r
+                       if (m.isVisible())\r
+                       {\r
+                               Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();\r
+\r
+                               //  Can't add the component to current row. Start a new row.\r
+\r
+                               if (rowWidth + d.width > maxWidth)\r
+                               {\r
+                                       addRow(dim, rowWidth, rowHeight);\r
+                                       rowWidth = 0;\r
+                                       rowHeight = 0;\r
+                               }\r
+\r
+                               //  Add a horizontal gap for all components after the first\r
+\r
+                               if (rowWidth != 0)\r
+                               {\r
+                                       rowWidth += hgap;\r
+                               }\r
+\r
+                               rowWidth += d.width;\r
+                               rowHeight = Math.max(rowHeight, d.height);\r
+                       }\r
+               }\r
+\r
+               addRow(dim, rowWidth, rowHeight);\r
+\r
+               dim.width += horizontalInsetsAndGap;\r
+               dim.height += insets.top + insets.bottom + vgap * 2;\r
+\r
+               //      When using a scroll pane or the DecoratedLookAndFeel we need to\r
+               //  make sure the preferred size is less than the size of the\r
+               //  target containter so shrinking the container size works\r
+               //  correctly. Removing the horizontal gap is an easy way to do this.\r
+\r
+               Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);\r
+\r
+               if (scrollPane != null && target.isValid())\r
+               {\r
+                       dim.width -= (hgap + 1);\r
+               }\r
+\r
+               return dim;\r
+       }\r
+       }\r
+\r
+       /*\r
+        *  A new row has been completed. Use the dimensions of this row\r
+        *  to update the preferred size for the container.\r
+        *\r
+        *  @param dim update the width and height when appropriate\r
+        *  @param rowWidth the width of the row to add\r
+        *  @param rowHeight the height of the row to add\r
+        */\r
+       private void addRow(Dimension dim, int rowWidth, int rowHeight)\r
+       {\r
+               dim.width = Math.max(dim.width, rowWidth);\r
+\r
+               if (dim.height > 0)\r
+               {\r
+                       dim.height += getVgap();\r
+               }\r
+\r
+               dim.height += rowHeight;\r
+       }\r
+}
\ No newline at end of file