├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
└── java
└── com
├── emaraic
└── ObjectRecognition
│ └── Recognizer.java
└── esotericsoftware
├── TableLayout.gwt.xml
└── tablelayout
├── BaseTableLayout.java
├── Cell.java
├── Toolkit.java
├── Value.java
└── swing
├── Stack.java
├── SwingToolkit.java
├── Table.java
└── TableLayout.java
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Object Recognition using TensorFlow and Java
2 |
3 | This small demo was written in Java to recognize objects in images and classify it into 1000 classes like Lion, Frog, Flowers, ....etc using Inception V3 model.
4 |
5 | The pre-trained Inception V3 model can be downloaded from here https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip
6 |
7 | For more details read this article: http://emaraic.com/blog/object-recognition-using-TensorFlow-Java
8 |
9 | Compiling and running code using Netbeans (Video) : https://www.youtube.com/watch?v=hFzAA0xHbZA
10 |
11 | **Screenshots**
12 |
13 | 
14 |
15 | 
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | **Thanks to**
24 | - EsotericSoftware to for their TableLayout Library https://github.com/EsotericSoftware/tablelayout
25 |
26 | - TensorFlow for their example which I built my demo on it.
27 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.emaraic
5 | ObjectRecognition
6 | 1.0-SNAPSHOT
7 | jar
8 |
9 | UTF-8
10 | 1.8
11 | 1.8
12 |
13 |
14 |
15 | org.tensorflow
16 | tensorflow
17 | 1.1.0-rc2
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | org.apache.maven.plugins
27 | maven-jar-plugin
28 | 2.4
29 |
30 |
31 |
32 | true
33 | lib/
34 | com.emaraic.ObjectRecognition.Recognizer
35 |
36 |
37 |
38 |
39 |
40 | maven-dependency-plugin
41 |
42 |
43 | install
44 |
45 | copy-dependencies
46 |
47 |
48 | ${project.build.directory}/lib
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/main/java/com/emaraic/ObjectRecognition/Recognizer.java:
--------------------------------------------------------------------------------
1 | package com.emaraic.ObjectRecognition;
2 |
3 | import com.esotericsoftware.tablelayout.swing.Table;
4 | import java.awt.Image;
5 | import java.awt.event.ActionEvent;
6 | import java.awt.event.ActionListener;
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.nio.charset.Charset;
10 | import java.nio.file.Files;
11 | import java.nio.file.Path;
12 | import java.nio.file.Paths;
13 | import java.util.Arrays;
14 | import java.util.List;
15 | import java.util.logging.Level;
16 | import java.util.logging.Logger;
17 | import javax.imageio.ImageIO;
18 | import javax.swing.ImageIcon;
19 | import javax.swing.JButton;
20 | import javax.swing.JFileChooser;
21 | import javax.swing.JFrame;
22 | import javax.swing.JLabel;
23 | import javax.swing.JTextField;
24 | import javax.swing.SwingUtilities;
25 | import javax.swing.filechooser.FileNameExtensionFilter;
26 | import org.tensorflow.DataType;
27 | import org.tensorflow.Graph;
28 | import org.tensorflow.Output;
29 | import org.tensorflow.Session;
30 | import org.tensorflow.Tensor;
31 |
32 | /**
33 | *
34 | * @author Taha Emara
35 | * Website: http://www.emaraic.com
36 | * Email : taha@emaraic.com
37 | * Created on: Apr 29, 2017
38 | * Kindly: Don't remove this header
39 | * Download the pre-trained inception model from here: https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip
40 | */
41 | public class Recognizer extends JFrame implements ActionListener {
42 |
43 |
44 | private Table table;
45 | private JButton predict;
46 | private JButton incep;
47 | private JButton img;
48 | private JFileChooser incepch;
49 | private JFileChooser imgch;
50 | private JLabel viewer;
51 | private JTextField result;
52 | private JTextField imgpth;
53 | private JTextField modelpth;
54 | private FileNameExtensionFilter imgfilter = new FileNameExtensionFilter(
55 | "JPG & JPEG Images", "jpg", "jpeg");
56 | private String modelpath;
57 | private String imagepath;
58 | private boolean modelselected = false;
59 | private byte[] graphDef;
60 | private List labels;
61 |
62 | public Recognizer() {
63 | setTitle("Object Recognition - Emaraic.com");
64 | setSize(500, 500);
65 | table = new Table();
66 |
67 | predict = new JButton("Predict");
68 | predict.setEnabled(false);
69 | incep = new JButton("Choose Inception");
70 | img = new JButton("Choose Image");
71 | incep.addActionListener(this);
72 | img.addActionListener(this);
73 | predict.addActionListener(this);
74 |
75 | incepch = new JFileChooser();
76 | imgch = new JFileChooser();
77 | imgch.setFileFilter(imgfilter);
78 | imgch.setFileSelectionMode(JFileChooser.FILES_ONLY);
79 | incepch.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
80 |
81 | result=new JTextField();
82 | modelpth=new JTextField();
83 | imgpth=new JTextField();
84 | modelpth.setEditable(false);
85 | imgpth.setEditable(false);
86 | viewer = new JLabel();
87 | getContentPane().add(table);
88 | table.addCell(modelpth).width(250);
89 | table.addCell(incep);
90 | table.row();
91 | table.addCell(imgpth).width(250);
92 | table.addCell(img);
93 |
94 | table.row();
95 | table.addCell(viewer).size(200, 200).colspan(2);
96 | table.row();
97 | table.addCell(predict).colspan(2);
98 | table.row();
99 | table.addCell(result).width(300).colspan(2);
100 | table.row();
101 | table.addCell(new JLabel("By: Taha Emara")).center().padTop(30).colspan(2);
102 | table.row();
103 | table.addCell(new JLabel("Email: taha@emaraic.com")).center().colspan(2);
104 |
105 | setLocationRelativeTo(null);
106 |
107 | setResizable(false);
108 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
109 | }
110 |
111 | @Override
112 | public void actionPerformed(ActionEvent e) {
113 |
114 | if (e.getSource() == incep) {
115 | int returnVal = incepch.showOpenDialog(this);
116 |
117 | if (returnVal == JFileChooser.APPROVE_OPTION) {
118 | File file = incepch.getSelectedFile();
119 | modelpath = file.getAbsolutePath();
120 | modelpth.setText(modelpath);
121 | System.out.println("Opening: " + file.getAbsolutePath());
122 | modelselected = true;
123 | graphDef = readAllBytesOrExit(Paths.get(modelpath, "tensorflow_inception_graph.pb"));
124 | labels = readAllLinesOrExit(Paths.get(modelpath, "imagenet_comp_graph_label_strings.txt"));
125 | } else {
126 | System.out.println("Process was cancelled by user.");
127 | }
128 |
129 | } else if (e.getSource() == img) {
130 | int returnVal = imgch.showOpenDialog(Recognizer.this);
131 | if (returnVal == JFileChooser.APPROVE_OPTION) {
132 | try {
133 | File file = imgch.getSelectedFile();
134 | imagepath = file.getAbsolutePath();
135 | imgpth.setText(imagepath);
136 | System.out.println("Image Path: " + imagepath);
137 | Image img = ImageIO.read(file);
138 |
139 | viewer.setIcon(new ImageIcon(img.getScaledInstance(200, 200, 200)));
140 | if (modelselected) {
141 | predict.setEnabled(true);
142 | }
143 | } catch (IOException ex) {
144 | Logger.getLogger(Recognizer.class.getName()).log(Level.SEVERE, null, ex);
145 | }
146 | } else {
147 | System.out.println("Process was cancelled by user.");
148 | }
149 | } else if (e.getSource() == predict) {
150 | byte[] imageBytes = readAllBytesOrExit(Paths.get(imagepath));
151 |
152 | try (Tensor image = Tensor.create(imageBytes)) {
153 | float[] labelProbabilities = executeInceptionGraph(graphDef, image);
154 | int bestLabelIdx = maxIndex(labelProbabilities);
155 | result.setText("");
156 | result.setText(String.format(
157 | "BEST MATCH: %s (%.2f%% likely)",
158 | labels.get(bestLabelIdx), labelProbabilities[bestLabelIdx] * 100f));
159 | System.out.println(
160 | String.format(
161 | "BEST MATCH: %s (%.2f%% likely)",
162 | labels.get(bestLabelIdx), labelProbabilities[bestLabelIdx] * 100f));
163 | }
164 |
165 | }
166 | }
167 |
168 | ///
169 | private static float[] executeInceptionGraph(byte[] graphDef, Tensor image) {
170 | try (Graph g = new Graph()) {
171 | g.importGraphDef(graphDef);
172 | try (Session s = new Session(g);
173 | Tensor result = s.runner().feed("DecodeJpeg/contents", image).fetch("softmax").run().get(0)) {
174 | final long[] rshape = result.shape();
175 | if (result.numDimensions() != 2 || rshape[0] != 1) {
176 | throw new RuntimeException(
177 | String.format(
178 | "Expected model to produce a [1 N] shaped tensor where N is the number of labels, instead it produced one with shape %s",
179 | Arrays.toString(rshape)));
180 | }
181 | int nlabels = (int) rshape[1];
182 | return result.copyTo(new float[1][nlabels])[0];
183 | }
184 | }
185 | }
186 |
187 | private static int maxIndex(float[] probabilities) {
188 | int best = 0;
189 | for (int i = 1; i < probabilities.length; ++i) {
190 | if (probabilities[i] > probabilities[best]) {
191 | best = i;
192 | }
193 | }
194 | return best;
195 | }
196 |
197 | private static byte[] readAllBytesOrExit(Path path) {
198 | try {
199 | return Files.readAllBytes(path);
200 | } catch (IOException e) {
201 | System.err.println("Failed to read [" + path + "]: " + e.getMessage());
202 | System.exit(1);
203 | }
204 | return null;
205 | }
206 |
207 | private static List readAllLinesOrExit(Path path) {
208 | try {
209 | return Files.readAllLines(path, Charset.forName("UTF-8"));
210 | } catch (IOException e) {
211 | System.err.println("Failed to read [" + path + "]: " + e.getMessage());
212 | System.exit(0);
213 | }
214 | return null;
215 | }
216 |
217 | // In the fullness of time, equivalents of the methods of this class should be auto-generated from
218 | // the OpDefs linked into libtensorflow_jni.so. That would match what is done in other languages
219 | // like Python, C++ and Go.
220 | static class GraphBuilder {
221 |
222 | GraphBuilder(Graph g) {
223 | this.g = g;
224 | }
225 |
226 | Output div(Output x, Output y) {
227 | return binaryOp("Div", x, y);
228 | }
229 |
230 | Output sub(Output x, Output y) {
231 | return binaryOp("Sub", x, y);
232 | }
233 |
234 | Output resizeBilinear(Output images, Output size) {
235 | return binaryOp("ResizeBilinear", images, size);
236 | }
237 |
238 | Output expandDims(Output input, Output dim) {
239 | return binaryOp("ExpandDims", input, dim);
240 | }
241 |
242 | Output cast(Output value, DataType dtype) {
243 | return g.opBuilder("Cast", "Cast").addInput(value).setAttr("DstT", dtype).build().output(0);
244 | }
245 |
246 | Output decodeJpeg(Output contents, long channels) {
247 | return g.opBuilder("DecodeJpeg", "DecodeJpeg")
248 | .addInput(contents)
249 | .setAttr("channels", channels)
250 | .build()
251 | .output(0);
252 | }
253 |
254 | Output constant(String name, Object value) {
255 | try (Tensor t = Tensor.create(value)) {
256 | return g.opBuilder("Const", name)
257 | .setAttr("dtype", t.dataType())
258 | .setAttr("value", t)
259 | .build()
260 | .output(0);
261 | }
262 | }
263 |
264 | private Output binaryOp(String type, Output in1, Output in2) {
265 | return g.opBuilder(type, type).addInput(in1).addInput(in2).build().output(0);
266 | }
267 |
268 | private Graph g;
269 | }
270 | ////////////
271 |
272 | public static void main(String[] args) {
273 | SwingUtilities.invokeLater(new Runnable() {
274 | public void run() {
275 | new Recognizer().setVisible(true);
276 |
277 | }
278 | });
279 | }
280 |
281 | }
282 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/TableLayout.gwt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/BaseTableLayout.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2011, Nathan Sweet
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | * * Redistributions of source code must retain the above copyright
8 | * notice, this list of conditions and the following disclaimer.
9 | * * Redistributions in binary form must reproduce the above copyright
10 | * notice, this list of conditions and the following disclaimer in the
11 | * documentation and/or other materials provided with the distribution.
12 | * * Neither the name of the nor the
13 | * names of its contributors may be used to endorse or promote products
14 | * derived from this software without specific prior written permission.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | ******************************************************************************/
27 |
28 | package com.esotericsoftware.tablelayout;
29 |
30 | import com.esotericsoftware.tablelayout.Value.FixedValue;
31 |
32 | import java.util.ArrayList;
33 | import java.util.List;
34 |
35 | /** Base layout functionality.
36 | * @author Nathan Sweet */
37 | abstract public class BaseTableLayout> {
38 | static public final int CENTER = 1 << 0;
39 | static public final int TOP = 1 << 1;
40 | static public final int BOTTOM = 1 << 2;
41 | static public final int LEFT = 1 << 3;
42 | static public final int RIGHT = 1 << 4;
43 |
44 | static public enum Debug {
45 | none, all, table, cell, widget
46 | }
47 |
48 | K toolkit;
49 | T table;
50 | private int columns, rows;
51 |
52 | private final ArrayList cells = new ArrayList(4);
53 | private final Cell cellDefaults;
54 | private final ArrayList columnDefaults = new ArrayList(2);
55 | private Cell rowDefaults;
56 |
57 | private boolean sizeInvalid = true;
58 | private float[] columnMinWidth, rowMinHeight;
59 | private float[] columnPrefWidth, rowPrefHeight;
60 | private float tableMinWidth, tableMinHeight;
61 | private float tablePrefWidth, tablePrefHeight;
62 | private float[] columnWidth, rowHeight;
63 | private float[] expandWidth, expandHeight;
64 | private float[] columnWeightedWidth, rowWeightedHeight;
65 |
66 | Value padTop, padLeft, padBottom, padRight;
67 | int align = CENTER;
68 | Debug debug = Debug.none;
69 |
70 | public BaseTableLayout (K toolkit) {
71 | this.toolkit = toolkit;
72 | cellDefaults = toolkit.obtainCell((L)this);
73 | cellDefaults.defaults();
74 | }
75 |
76 | /** Invalidates the layout. The cached min and pref sizes are recalculated the next time layout is done or the min or pref sizes
77 | * are accessed. */
78 | public void invalidate () {
79 | sizeInvalid = true;
80 | }
81 |
82 | /** Invalidates the layout of this table and every parent widget. */
83 | abstract public void invalidateHierarchy ();
84 |
85 | /** Adds a new cell to the table with the specified widget. */
86 | public Cell add (C widget) {
87 | Cell cell = toolkit.obtainCell((L)this);
88 | cell.widget = widget;
89 |
90 | if (cells.size() > 0) {
91 | // Set cell column and row.
92 | Cell lastCell = cells.get(cells.size() - 1);
93 | if (!lastCell.endRow) {
94 | cell.column = lastCell.column + lastCell.colspan;
95 | cell.row = lastCell.row;
96 | } else {
97 | cell.column = 0;
98 | cell.row = lastCell.row + 1;
99 | }
100 | // Set the index of the cell above.
101 | if (cell.row > 0) {
102 | outer:
103 | for (int i = cells.size() - 1; i >= 0; i--) {
104 | Cell other = cells.get(i);
105 | for (int column = other.column, nn = column + other.colspan; column < nn; column++) {
106 | if (column == cell.column) {
107 | cell.cellAboveIndex = i;
108 | break outer;
109 | }
110 | }
111 | }
112 | }
113 | } else {
114 | cell.column = 0;
115 | cell.row = 0;
116 | }
117 | cells.add(cell);
118 |
119 | cell.set(cellDefaults);
120 | if (cell.column < columnDefaults.size()) {
121 | Cell columnCell = columnDefaults.get(cell.column);
122 | if (columnCell != null) cell.merge(columnCell);
123 | }
124 | cell.merge(rowDefaults);
125 |
126 | if (widget != null) toolkit.addChild(table, widget);
127 |
128 | return cell;
129 | }
130 |
131 | /** Indicates that subsequent cells should be added to a new row and returns the cell values that will be used as the defaults
132 | * for all cells in the new row. */
133 | public Cell row () {
134 | if (cells.size() > 0) endRow();
135 | if (rowDefaults != null) toolkit.freeCell(rowDefaults);
136 | rowDefaults = toolkit.obtainCell((L)this);
137 | rowDefaults.clear();
138 | return rowDefaults;
139 | }
140 |
141 | private void endRow () {
142 | int rowColumns = 0;
143 | for (int i = cells.size() - 1; i >= 0; i--) {
144 | Cell cell = cells.get(i);
145 | if (cell.endRow) break;
146 | rowColumns += cell.colspan;
147 | }
148 | columns = Math.max(columns, rowColumns);
149 | rows++;
150 | cells.get(cells.size() - 1).endRow = true;
151 | invalidate();
152 | }
153 |
154 | /** Gets the cell values that will be used as the defaults for all cells in the specified column. Columns are indexed starting
155 | * at 0. */
156 | public Cell columnDefaults (int column) {
157 | Cell cell = columnDefaults.size() > column ? columnDefaults.get(column) : null;
158 | if (cell == null) {
159 | cell = toolkit.obtainCell((L)this);
160 | cell.clear();
161 | if (column >= columnDefaults.size()) {
162 | for (int i = columnDefaults.size(); i < column; i++)
163 | columnDefaults.add(null);
164 | columnDefaults.add(cell);
165 | } else
166 | columnDefaults.set(column, cell);
167 | }
168 | return cell;
169 | }
170 |
171 | /** Removes all widgets and cells from the table (same as {@link #clear()}) and additionally resets all table properties and
172 | * cell, column, and row defaults. */
173 | public void reset () {
174 | clear();
175 | padTop = null;
176 | padLeft = null;
177 | padBottom = null;
178 | padRight = null;
179 | align = CENTER;
180 | if (debug != Debug.none) toolkit.clearDebugRectangles((L)this);
181 | debug = Debug.none;
182 | cellDefaults.defaults();
183 | for (int i = 0, n = columnDefaults.size(); i < n; i++) {
184 | Cell columnCell = columnDefaults.get(i);
185 | if (columnCell != null) toolkit.freeCell(columnCell);
186 | }
187 | columnDefaults.clear();
188 | }
189 |
190 | /** Removes all widgets and cells from the table. */
191 | public void clear () {
192 | for (int i = cells.size() - 1; i >= 0; i--) {
193 | Cell cell = cells.get(i);
194 | Object widget = cell.widget;
195 | if (widget != null) toolkit.removeChild(table, (C)widget);
196 | toolkit.freeCell(cell);
197 | }
198 | cells.clear();
199 | rows = 0;
200 | columns = 0;
201 | if (rowDefaults != null) toolkit.freeCell(rowDefaults);
202 | rowDefaults = null;
203 | invalidate();
204 | }
205 |
206 | /** Returns the cell for the specified widget in this table, or null. */
207 | public Cell getCell (C widget) {
208 | for (int i = 0, n = cells.size(); i < n; i++) {
209 | Cell c = cells.get(i);
210 | if (c.widget == widget) return c;
211 | }
212 | return null;
213 | }
214 |
215 | /** Returns the cells for this table. */
216 | public List getCells () {
217 | return cells;
218 | }
219 |
220 | public void setToolkit (K toolkit) {
221 | this.toolkit = toolkit;
222 | }
223 |
224 | /** Returns the table widget that will be laid out. */
225 | public T getTable () {
226 | return table;
227 | }
228 |
229 | /** Sets the table widget that will be laid out. */
230 | public void setTable (T table) {
231 | this.table = table;
232 | }
233 |
234 | /** The minimum width of the table. */
235 | public float getMinWidth () {
236 | if (sizeInvalid) computeSize();
237 | return tableMinWidth;
238 | }
239 |
240 | /** The minimum size of the table. */
241 | public float getMinHeight () {
242 | if (sizeInvalid) computeSize();
243 | return tableMinHeight;
244 | }
245 |
246 | /** The preferred width of the table. */
247 | public float getPrefWidth () {
248 | if (sizeInvalid) computeSize();
249 | return tablePrefWidth;
250 | }
251 |
252 | /** The preferred height of the table. */
253 | public float getPrefHeight () {
254 | if (sizeInvalid) computeSize();
255 | return tablePrefHeight;
256 | }
257 |
258 | /** The cell values that will be used as the defaults for all cells. */
259 | public Cell defaults () {
260 | return cellDefaults;
261 | }
262 |
263 | /** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
264 | public L pad (Value pad) {
265 | padTop = pad;
266 | padLeft = pad;
267 | padBottom = pad;
268 | padRight = pad;
269 | sizeInvalid = true;
270 | return (L)this;
271 | }
272 |
273 | public L pad (Value top, Value left, Value bottom, Value right) {
274 | padTop = top;
275 | padLeft = left;
276 | padBottom = bottom;
277 | padRight = right;
278 | sizeInvalid = true;
279 | return (L)this;
280 | }
281 |
282 | /** Padding at the top edge of the table. */
283 | public L padTop (Value padTop) {
284 | this.padTop = padTop;
285 | sizeInvalid = true;
286 | return (L)this;
287 | }
288 |
289 | /** Padding at the left edge of the table. */
290 | public L padLeft (Value padLeft) {
291 | this.padLeft = padLeft;
292 | sizeInvalid = true;
293 | return (L)this;
294 | }
295 |
296 | /** Padding at the bottom edge of the table. */
297 | public L padBottom (Value padBottom) {
298 | this.padBottom = padBottom;
299 | sizeInvalid = true;
300 | return (L)this;
301 | }
302 |
303 | /** Padding at the right edge of the table. */
304 | public L padRight (Value padRight) {
305 | this.padRight = padRight;
306 | sizeInvalid = true;
307 | return (L)this;
308 | }
309 |
310 | /** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
311 | public L pad (float pad) {
312 | padTop = new FixedValue(pad);
313 | padLeft = new FixedValue(pad);
314 | padBottom = new FixedValue(pad);
315 | padRight = new FixedValue(pad);
316 | sizeInvalid = true;
317 | return (L)this;
318 | }
319 |
320 | public L pad (float top, float left, float bottom, float right) {
321 | padTop = new FixedValue(top);
322 | padLeft = new FixedValue(left);
323 | padBottom = new FixedValue(bottom);
324 | padRight = new FixedValue(right);
325 | sizeInvalid = true;
326 | return (L)this;
327 | }
328 |
329 | /** Padding at the top edge of the table. */
330 | public L padTop (float padTop) {
331 | this.padTop = new FixedValue(padTop);
332 | sizeInvalid = true;
333 | return (L)this;
334 | }
335 |
336 | /** Padding at the left edge of the table. */
337 | public L padLeft (float padLeft) {
338 | this.padLeft = new FixedValue(padLeft);
339 | sizeInvalid = true;
340 | return (L)this;
341 | }
342 |
343 | /** Padding at the bottom edge of the table. */
344 | public L padBottom (float padBottom) {
345 | this.padBottom = new FixedValue(padBottom);
346 | sizeInvalid = true;
347 | return (L)this;
348 | }
349 |
350 | /** Padding at the right edge of the table. */
351 | public L padRight (float padRight) {
352 | this.padRight = new FixedValue(padRight);
353 | sizeInvalid = true;
354 | return (L)this;
355 | }
356 |
357 | /** Alignment of the logical table within the table widget. Set to {@link #CENTER}, {@link #TOP}, {@link #BOTTOM} ,
358 | * {@link #LEFT}, {@link #RIGHT}, or any combination of those. */
359 | public L align (int align) {
360 | this.align = align;
361 | return (L)this;
362 | }
363 |
364 | /** Sets the alignment of the logical table within the table widget to {@link #CENTER}. This clears any other alignment. */
365 | public L center () {
366 | align = CENTER;
367 | return (L)this;
368 | }
369 |
370 | /** Adds {@link #TOP} and clears {@link #BOTTOM} for the alignment of the logical table within the table widget. */
371 | public L top () {
372 | align |= TOP;
373 | align &= ~BOTTOM;
374 | return (L)this;
375 | }
376 |
377 | /** Adds {@link #LEFT} and clears {@link #RIGHT} for the alignment of the logical table within the table widget. */
378 | public L left () {
379 | align |= LEFT;
380 | align &= ~RIGHT;
381 | return (L)this;
382 | }
383 |
384 | /** Adds {@link #BOTTOM} and clears {@link #TOP} for the alignment of the logical table within the table widget. */
385 | public L bottom () {
386 | align |= BOTTOM;
387 | align &= ~TOP;
388 | return (L)this;
389 | }
390 |
391 | /** Adds {@link #RIGHT} and clears {@link #LEFT} for the alignment of the logical table within the table widget. */
392 | public L right () {
393 | align |= RIGHT;
394 | align &= ~LEFT;
395 | return (L)this;
396 | }
397 |
398 | /** Turns on all debug lines. */
399 | public L debug () {
400 | this.debug = Debug.all;
401 | invalidate();
402 | return (L)this;
403 | }
404 |
405 | /** Turns on table debug lines. */
406 | public L debugTable () {
407 | this.debug = Debug.table;
408 | invalidate();
409 | return (L)this;
410 | }
411 |
412 | /** Turns on cell debug lines. */
413 | public L debugCell () {
414 | this.debug = Debug.cell;
415 | invalidate();
416 | return (L)this;
417 | }
418 |
419 | /** Turns on widget debug lines. */
420 | public L debugWidget () {
421 | this.debug = Debug.widget;
422 | invalidate();
423 | return (L)this;
424 | }
425 |
426 | /** Turns on debug lines. */
427 | public L debug (Debug debug) {
428 | this.debug = debug;
429 | if (debug == Debug.none)
430 | toolkit.clearDebugRectangles((L)this);
431 | else
432 | invalidate();
433 | return (L)this;
434 | }
435 |
436 | public Debug getDebug () {
437 | return debug;
438 | }
439 |
440 | public Value getPadTopValue () {
441 | return padTop;
442 | }
443 |
444 | public float getPadTop () {
445 | return padTop == null ? 0 : padTop.height(this);
446 | }
447 |
448 | public Value getPadLeftValue () {
449 | return padLeft;
450 | }
451 |
452 | public float getPadLeft () {
453 | return padLeft == null ? 0 : padLeft.width(this);
454 | }
455 |
456 | public Value getPadBottomValue () {
457 | return padBottom;
458 | }
459 |
460 | public float getPadBottom () {
461 | return padBottom == null ? 0 : padBottom.height(this);
462 | }
463 |
464 | public Value getPadRightValue () {
465 | return padRight;
466 | }
467 |
468 | public float getPadRight () {
469 | return padRight == null ? 0 : padRight.width(this);
470 | }
471 |
472 | public int getAlign () {
473 | return align;
474 | }
475 |
476 | /** Returns the row index for the y coordinate, or -1 if there are no cells. */
477 | public int getRow (float y) {
478 | int row = 0;
479 | y += h(padTop);
480 | int i = 0, n = cells.size();
481 | if (n == 0) return -1;
482 | if (n == 1) return 0;
483 | if (cells.get(0).widgetY < cells.get(1).widgetY) {
484 | // Using y-down coordinate system.
485 | while (i < n) {
486 | Cell c = cells.get(i++);
487 | if (c.getIgnore()) continue;
488 | if (c.widgetY + c.computedPadTop > y) break;
489 | if (c.endRow) row++;
490 | }
491 | return row - 1;
492 | }
493 | // Using y-up coordinate system.
494 | while (i < n) {
495 | Cell c = cells.get(i++);
496 | if (c.getIgnore()) continue;
497 | if (c.widgetY + c.computedPadTop < y) break;
498 | if (c.endRow) row++;
499 | }
500 | return row;
501 | }
502 |
503 | private float[] ensureSize (float[] array, int size) {
504 | if (array == null || array.length < size) return new float[size];
505 | for (int i = 0, n = array.length; i < n; i++)
506 | array[i] = 0;
507 | return array;
508 | }
509 |
510 | private float w (Value value) {
511 | return value == null ? 0 : value.width(table);
512 | }
513 |
514 | private float h (Value value) {
515 | return value == null ? 0 : value.height(table);
516 | }
517 |
518 | private float w (Value value, Cell cell) {
519 | return value == null ? 0 : value.width(cell);
520 | }
521 |
522 | private float h (Value value, Cell cell) {
523 | return value == null ? 0 : value.height(cell);
524 | }
525 |
526 | private void computeSize () {
527 | sizeInvalid = false;
528 |
529 | Toolkit toolkit = this.toolkit;
530 | ArrayList cells = this.cells;
531 |
532 | if (cells.size() > 0 && !cells.get(cells.size() - 1).endRow) endRow();
533 |
534 | columnMinWidth = ensureSize(columnMinWidth, columns);
535 | rowMinHeight = ensureSize(rowMinHeight, rows);
536 | columnPrefWidth = ensureSize(columnPrefWidth, columns);
537 | rowPrefHeight = ensureSize(rowPrefHeight, rows);
538 | columnWidth = ensureSize(columnWidth, columns);
539 | rowHeight = ensureSize(rowHeight, rows);
540 | expandWidth = ensureSize(expandWidth, columns);
541 | expandHeight = ensureSize(expandHeight, rows);
542 |
543 | float spaceRightLast = 0;
544 | for (int i = 0, n = cells.size(); i < n; i++) {
545 | Cell c = cells.get(i);
546 | if (c.ignore) continue;
547 |
548 | // Collect columns/rows that expand.
549 | if (c.expandY != 0 && expandHeight[c.row] == 0) expandHeight[c.row] = c.expandY;
550 | if (c.colspan == 1 && c.expandX != 0 && expandWidth[c.column] == 0) expandWidth[c.column] = c.expandX;
551 |
552 | // Compute combined padding/spacing for cells.
553 | // Spacing between widgets isn't additive, the larger is used. Also, no spacing around edges.
554 | c.computedPadLeft = w(c.padLeft, c) + (c.column == 0 ? 0 : Math.max(0, w(c.spaceLeft, c) - spaceRightLast));
555 | c.computedPadTop = h(c.padTop, c);
556 | if (c.cellAboveIndex != -1) {
557 | Cell above = cells.get(c.cellAboveIndex);
558 | c.computedPadTop += Math.max(0, h(c.spaceTop, c) - h(above.spaceBottom, above));
559 | }
560 | float spaceRight = w(c.spaceRight, c);
561 | c.computedPadRight = w(c.padRight, c) + ((c.column + c.colspan) == columns ? 0 : spaceRight);
562 | c.computedPadBottom = h(c.padBottom, c) + (c.row == rows - 1 ? 0 : h(c.spaceBottom, c));
563 | spaceRightLast = spaceRight;
564 |
565 | // Determine minimum and preferred cell sizes.
566 | float prefWidth = w(c.prefWidth, c);
567 | float prefHeight = h(c.prefHeight, c);
568 | float minWidth = w(c.minWidth, c);
569 | float minHeight = h(c.minHeight, c);
570 | float maxWidth = w(c.maxWidth, c);
571 | float maxHeight = h(c.maxHeight, c);
572 | if (prefWidth < minWidth) prefWidth = minWidth;
573 | if (prefHeight < minHeight) prefHeight = minHeight;
574 | if (maxWidth > 0 && prefWidth > maxWidth) prefWidth = maxWidth;
575 | if (maxHeight > 0 && prefHeight > maxHeight) prefHeight = maxHeight;
576 |
577 | if (c.colspan == 1) { // Spanned column min and pref width is added later.
578 | float hpadding = c.computedPadLeft + c.computedPadRight;
579 | columnPrefWidth[c.column] = Math.max(columnPrefWidth[c.column], prefWidth + hpadding);
580 | columnMinWidth[c.column] = Math.max(columnMinWidth[c.column], minWidth + hpadding);
581 | }
582 | float vpadding = c.computedPadTop + c.computedPadBottom;
583 | rowPrefHeight[c.row] = Math.max(rowPrefHeight[c.row], prefHeight + vpadding);
584 | rowMinHeight[c.row] = Math.max(rowMinHeight[c.row], minHeight + vpadding);
585 | }
586 |
587 | // Colspan with expand will expand all spanned columns if none of the spanned columns have expand.
588 | outer:
589 | for (int i = 0, n = cells.size(); i < n; i++) {
590 | Cell c = cells.get(i);
591 | if (c.ignore || c.expandX == 0) continue;
592 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
593 | if (expandWidth[column] != 0) continue outer;
594 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
595 | expandWidth[column] = c.expandX;
596 | }
597 |
598 | // Distribute any additional min and pref width added by colspanned cells to the columns spanned.
599 | for (int i = 0, n = cells.size(); i < n; i++) {
600 | Cell c = cells.get(i);
601 | if (c.ignore || c.colspan == 1) continue;
602 |
603 | float minWidth = w(c.minWidth, c);
604 | float prefWidth = w(c.prefWidth, c);
605 | float maxWidth = w(c.maxWidth, c);
606 | if (prefWidth < minWidth) prefWidth = minWidth;
607 | if (maxWidth > 0 && prefWidth > maxWidth) prefWidth = maxWidth;
608 |
609 | float spannedMinWidth = -(c.computedPadLeft + c.computedPadRight), spannedPrefWidth = spannedMinWidth;
610 | for (int column = c.column, nn = column + c.colspan; column < nn; column++) {
611 | spannedMinWidth += columnMinWidth[column];
612 | spannedPrefWidth += columnPrefWidth[column];
613 | }
614 |
615 | // Distribute extra space using expand, if any columns have expand.
616 | float totalExpandWidth = 0;
617 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
618 | totalExpandWidth += expandWidth[column];
619 |
620 | float extraMinWidth = Math.max(0, minWidth - spannedMinWidth);
621 | float extraPrefWidth = Math.max(0, prefWidth - spannedPrefWidth);
622 | for (int column = c.column, nn = column + c.colspan; column < nn; column++) {
623 | float ratio = totalExpandWidth == 0 ? 1f / c.colspan : expandWidth[column] / totalExpandWidth;
624 | columnMinWidth[column] += extraMinWidth * ratio;
625 | columnPrefWidth[column] += extraPrefWidth * ratio;
626 | }
627 | }
628 |
629 | // Collect uniform size.
630 | float uniformMinWidth = 0, uniformMinHeight = 0;
631 | float uniformPrefWidth = 0, uniformPrefHeight = 0;
632 | for (int i = 0, n = cells.size(); i < n; i++) {
633 | Cell c = cells.get(i);
634 | if (c.ignore) continue;
635 |
636 | // Collect uniform sizes.
637 | if (c.uniformX == Boolean.TRUE && c.colspan == 1) {
638 | float hpadding = c.computedPadLeft + c.computedPadRight;
639 | uniformMinWidth = Math.max(uniformMinWidth, columnMinWidth[c.column] - hpadding);
640 | uniformPrefWidth = Math.max(uniformPrefWidth, columnPrefWidth[c.column] - hpadding);
641 | }
642 | if (c.uniformY == Boolean.TRUE) {
643 | float vpadding = c.computedPadTop + c.computedPadBottom;
644 | uniformMinHeight = Math.max(uniformMinHeight, rowMinHeight[c.row] - vpadding);
645 | uniformPrefHeight = Math.max(uniformPrefHeight, rowPrefHeight[c.row] - vpadding);
646 | }
647 | }
648 |
649 | // Size uniform cells to the same width/height.
650 | if (uniformPrefWidth > 0 || uniformPrefHeight > 0) {
651 | for (int i = 0, n = cells.size(); i < n; i++) {
652 | Cell c = cells.get(i);
653 | if (c.ignore) continue;
654 | if (uniformPrefWidth > 0 && c.uniformX == Boolean.TRUE && c.colspan == 1) {
655 | float hpadding = c.computedPadLeft + c.computedPadRight;
656 | columnMinWidth[c.column] = uniformMinWidth + hpadding;
657 | columnPrefWidth[c.column] = uniformPrefWidth + hpadding;
658 | }
659 | if (uniformPrefHeight > 0 && c.uniformY == Boolean.TRUE) {
660 | float vpadding = c.computedPadTop + c.computedPadBottom;
661 | rowMinHeight[c.row] = uniformMinHeight + vpadding;
662 | rowPrefHeight[c.row] = uniformPrefHeight + vpadding;
663 | }
664 | }
665 | }
666 |
667 | // Determine table min and pref size.
668 | tableMinWidth = 0;
669 | tableMinHeight = 0;
670 | tablePrefWidth = 0;
671 | tablePrefHeight = 0;
672 | for (int i = 0; i < columns; i++) {
673 | tableMinWidth += columnMinWidth[i];
674 | tablePrefWidth += columnPrefWidth[i];
675 | }
676 | for (int i = 0; i < rows; i++) {
677 | tableMinHeight += rowMinHeight[i];
678 | tablePrefHeight += Math.max(rowMinHeight[i], rowPrefHeight[i]);
679 | }
680 | float hpadding = w(padLeft) + w(padRight);
681 | float vpadding = h(padTop) + h(padBottom);
682 | tableMinWidth = tableMinWidth + hpadding;
683 | tableMinHeight = tableMinHeight + vpadding;
684 | tablePrefWidth = Math.max(tablePrefWidth + hpadding, tableMinWidth);
685 | tablePrefHeight = Math.max(tablePrefHeight + vpadding, tableMinHeight);
686 | }
687 |
688 | /** Positions and sizes children of the table using the cell associated with each child. The values given are the position
689 | * within the parent and size of the table. */
690 | public void layout (float layoutX, float layoutY, float layoutWidth, float layoutHeight) {
691 | Toolkit toolkit = this.toolkit;
692 | ArrayList cells = this.cells;
693 |
694 | if (sizeInvalid) computeSize();
695 |
696 | float hpadding = w(padLeft) + w(padRight);
697 | float vpadding = h(padTop) + h(padBottom);
698 |
699 | float totalExpandWidth = 0, totalExpandHeight = 0;
700 | for (int i = 0; i < columns; i++)
701 | totalExpandWidth += expandWidth[i];
702 | for (int i = 0; i < rows; i++)
703 | totalExpandHeight += expandHeight[i];
704 |
705 | // Size columns and rows between min and pref size using (preferred - min) size to weight distribution of extra space.
706 | float[] columnWeightedWidth;
707 | float totalGrowWidth = tablePrefWidth - tableMinWidth;
708 | if (totalGrowWidth == 0)
709 | columnWeightedWidth = columnMinWidth;
710 | else {
711 | float extraWidth = Math.min(totalGrowWidth, Math.max(0, layoutWidth - tableMinWidth));
712 | columnWeightedWidth = this.columnWeightedWidth = ensureSize(this.columnWeightedWidth, columns);
713 | for (int i = 0; i < columns; i++) {
714 | float growWidth = columnPrefWidth[i] - columnMinWidth[i];
715 | float growRatio = growWidth / (float)totalGrowWidth;
716 | columnWeightedWidth[i] = columnMinWidth[i] + extraWidth * growRatio;
717 | }
718 | }
719 |
720 | float[] rowWeightedHeight;
721 | float totalGrowHeight = tablePrefHeight - tableMinHeight;
722 | if (totalGrowHeight == 0)
723 | rowWeightedHeight = rowMinHeight;
724 | else {
725 | rowWeightedHeight = this.rowWeightedHeight = ensureSize(this.rowWeightedHeight, rows);
726 | float extraHeight = Math.min(totalGrowHeight, Math.max(0, layoutHeight - tableMinHeight));
727 | for (int i = 0; i < rows; i++) {
728 | float growHeight = rowPrefHeight[i] - rowMinHeight[i];
729 | float growRatio = growHeight / (float)totalGrowHeight;
730 | rowWeightedHeight[i] = rowMinHeight[i] + extraHeight * growRatio;
731 | }
732 | }
733 |
734 | // Determine widget and cell sizes (before expand or fill).
735 | for (int i = 0, n = cells.size(); i < n; i++) {
736 | Cell c = cells.get(i);
737 | if (c.ignore) continue;
738 |
739 | float spannedWeightedWidth = 0;
740 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
741 | spannedWeightedWidth += columnWeightedWidth[column];
742 | float weightedHeight = rowWeightedHeight[c.row];
743 |
744 | float prefWidth = w(c.prefWidth, c);
745 | float prefHeight = h(c.prefHeight, c);
746 | float minWidth = w(c.minWidth, c);
747 | float minHeight = h(c.minHeight, c);
748 | float maxWidth = w(c.maxWidth, c);
749 | float maxHeight = h(c.maxHeight, c);
750 | if (prefWidth < minWidth) prefWidth = minWidth;
751 | if (prefHeight < minHeight) prefHeight = minHeight;
752 | if (maxWidth > 0 && prefWidth > maxWidth) prefWidth = maxWidth;
753 | if (maxHeight > 0 && prefHeight > maxHeight) prefHeight = maxHeight;
754 |
755 | c.widgetWidth = Math.min(spannedWeightedWidth - c.computedPadLeft - c.computedPadRight, prefWidth);
756 | c.widgetHeight = Math.min(weightedHeight - c.computedPadTop - c.computedPadBottom, prefHeight);
757 |
758 | if (c.colspan == 1) columnWidth[c.column] = Math.max(columnWidth[c.column], spannedWeightedWidth);
759 | rowHeight[c.row] = Math.max(rowHeight[c.row], weightedHeight);
760 | }
761 |
762 | // Distribute remaining space to any expanding columns/rows.
763 | if (totalExpandWidth > 0) {
764 | float extra = layoutWidth - hpadding;
765 | for (int i = 0; i < columns; i++)
766 | extra -= columnWidth[i];
767 | float used = 0;
768 | int lastIndex = 0;
769 | for (int i = 0; i < columns; i++) {
770 | if (expandWidth[i] == 0) continue;
771 | float amount = extra * expandWidth[i] / totalExpandWidth;
772 | columnWidth[i] += amount;
773 | used += amount;
774 | lastIndex = i;
775 | }
776 | columnWidth[lastIndex] += extra - used;
777 | }
778 | if (totalExpandHeight > 0) {
779 | float extra = layoutHeight - vpadding;
780 | for (int i = 0; i < rows; i++)
781 | extra -= rowHeight[i];
782 | float used = 0;
783 | int lastIndex = 0;
784 | for (int i = 0; i < rows; i++) {
785 | if (expandHeight[i] == 0) continue;
786 | float amount = extra * expandHeight[i] / totalExpandHeight;
787 | rowHeight[i] += amount;
788 | used += amount;
789 | lastIndex = i;
790 | }
791 | rowHeight[lastIndex] += extra - used;
792 | }
793 |
794 | // Distribute any additional width added by colspanned cells to the columns spanned.
795 | for (int i = 0, n = cells.size(); i < n; i++) {
796 | Cell c = cells.get(i);
797 | if (c.ignore) continue;
798 | if (c.colspan == 1) continue;
799 |
800 | float extraWidth = 0;
801 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
802 | extraWidth += columnWeightedWidth[column] - columnWidth[column];
803 | extraWidth -= Math.max(0, c.computedPadLeft + c.computedPadRight);
804 |
805 | extraWidth /= c.colspan;
806 | if (extraWidth > 0) {
807 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
808 | columnWidth[column] += extraWidth;
809 | }
810 | }
811 |
812 | // Determine table size.
813 | float tableWidth = hpadding, tableHeight = vpadding;
814 | for (int i = 0; i < columns; i++)
815 | tableWidth += columnWidth[i];
816 | for (int i = 0; i < rows; i++)
817 | tableHeight += rowHeight[i];
818 |
819 | // Position table within the container.
820 | float x = layoutX + w(padLeft);
821 | if ((align & RIGHT) != 0)
822 | x += layoutWidth - tableWidth;
823 | else if ((align & LEFT) == 0) // Center
824 | x += (layoutWidth - tableWidth) / 2;
825 |
826 | float y = layoutY + h(padTop);
827 | if ((align & BOTTOM) != 0)
828 | y += layoutHeight - tableHeight;
829 | else if ((align & TOP) == 0) // Center
830 | y += (layoutHeight - tableHeight) / 2;
831 |
832 | // Position widgets within cells.
833 | float currentX = x, currentY = y;
834 | for (int i = 0, n = cells.size(); i < n; i++) {
835 | Cell c = cells.get(i);
836 | if (c.ignore) continue;
837 |
838 | float spannedCellWidth = 0;
839 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
840 | spannedCellWidth += columnWidth[column];
841 | spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
842 |
843 | currentX += c.computedPadLeft;
844 |
845 | if (c.fillX > 0) {
846 | c.widgetWidth = spannedCellWidth * c.fillX;
847 | float maxWidth = w(c.maxWidth, c);
848 | if (maxWidth > 0) c.widgetWidth = Math.min(c.widgetWidth, maxWidth);
849 | }
850 | if (c.fillY > 0) {
851 | c.widgetHeight = rowHeight[c.row] * c.fillY - c.computedPadTop - c.computedPadBottom;
852 | float maxHeight = h(c.maxHeight, c);
853 | if (maxHeight > 0) c.widgetHeight = Math.min(c.widgetHeight, maxHeight);
854 | }
855 |
856 | if ((c.align & LEFT) != 0)
857 | c.widgetX = currentX;
858 | else if ((c.align & RIGHT) != 0)
859 | c.widgetX = currentX + spannedCellWidth - c.widgetWidth;
860 | else
861 | c.widgetX = currentX + (spannedCellWidth - c.widgetWidth) / 2;
862 |
863 | if ((c.align & TOP) != 0)
864 | c.widgetY = currentY + c.computedPadTop;
865 | else if ((c.align & BOTTOM) != 0)
866 | c.widgetY = currentY + rowHeight[c.row] - c.widgetHeight - c.computedPadBottom;
867 | else
868 | c.widgetY = currentY + (rowHeight[c.row] - c.widgetHeight + c.computedPadTop - c.computedPadBottom) / 2;
869 |
870 | if (c.endRow) {
871 | currentX = x;
872 | currentY += rowHeight[c.row];
873 | } else
874 | currentX += spannedCellWidth + c.computedPadRight;
875 | }
876 |
877 | // Draw debug widgets and bounds.
878 | if (debug == Debug.none) return;
879 | toolkit.clearDebugRectangles(this);
880 | currentX = x;
881 | currentY = y;
882 | if (debug == Debug.table || debug == Debug.all) {
883 | toolkit.addDebugRectangle(this, Debug.table, layoutX, layoutY, layoutWidth, layoutHeight);
884 | toolkit.addDebugRectangle(this, Debug.table, x, y, tableWidth - hpadding, tableHeight - vpadding);
885 | }
886 | for (int i = 0, n = cells.size(); i < n; i++) {
887 | Cell c = cells.get(i);
888 | if (c.ignore) continue;
889 |
890 | // Widget bounds.
891 | if (debug == Debug.widget || debug == Debug.all)
892 | toolkit.addDebugRectangle(this, Debug.widget, c.widgetX, c.widgetY, c.widgetWidth, c.widgetHeight);
893 |
894 | // Cell bounds.
895 | float spannedCellWidth = 0;
896 | for (int column = c.column, nn = column + c.colspan; column < nn; column++)
897 | spannedCellWidth += columnWidth[column];
898 | spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
899 | currentX += c.computedPadLeft;
900 | if (debug == Debug.cell || debug == Debug.all) {
901 | toolkit.addDebugRectangle(this, Debug.cell, currentX, currentY + c.computedPadTop, spannedCellWidth, rowHeight[c.row]
902 | - c.computedPadTop - c.computedPadBottom);
903 | }
904 |
905 | if (c.endRow) {
906 | currentX = x;
907 | currentY += rowHeight[c.row];
908 | } else
909 | currentX += spannedCellWidth + c.computedPadRight;
910 | }
911 | }
912 | }
913 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/Cell.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2011, Nathan Sweet
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | * * Redistributions of source code must retain the above copyright
8 | * notice, this list of conditions and the following disclaimer.
9 | * * Redistributions in binary form must reproduce the above copyright
10 | * notice, this list of conditions and the following disclaimer in the
11 | * documentation and/or other materials provided with the distribution.
12 | * * Neither the name of the nor the
13 | * names of its contributors may be used to endorse or promote products
14 | * derived from this software without specific prior written permission.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | ******************************************************************************/
27 |
28 | package com.esotericsoftware.tablelayout;
29 |
30 | import com.esotericsoftware.tablelayout.Value.FixedValue;
31 |
32 | import static com.esotericsoftware.tablelayout.BaseTableLayout.*;
33 |
34 | /** A cell in a table.
35 | * @author Nathan Sweet */
36 | public class Cell {
37 | Value minWidth, minHeight;
38 | Value prefWidth, prefHeight;
39 | Value maxWidth, maxHeight;
40 | Value spaceTop, spaceLeft, spaceBottom, spaceRight;
41 | Value padTop, padLeft, padBottom, padRight;
42 | Float fillX, fillY;
43 | Integer align;
44 | Integer expandX, expandY;
45 | Boolean ignore;
46 | Integer colspan;
47 | Boolean uniformX, uniformY;
48 |
49 | C widget;
50 | float widgetX, widgetY;
51 | float widgetWidth, widgetHeight;
52 |
53 | private BaseTableLayout layout;
54 | boolean endRow;
55 | int column, row;
56 | int cellAboveIndex = -1;
57 | float computedPadTop, computedPadLeft, computedPadBottom, computedPadRight;
58 |
59 | public Cell () {
60 | }
61 |
62 | public void setLayout (BaseTableLayout layout) {
63 | this.layout = layout;
64 | }
65 |
66 | void set (Cell defaults) {
67 | minWidth = defaults.minWidth;
68 | minHeight = defaults.minHeight;
69 | prefWidth = defaults.prefWidth;
70 | prefHeight = defaults.prefHeight;
71 | maxWidth = defaults.maxWidth;
72 | maxHeight = defaults.maxHeight;
73 | spaceTop = defaults.spaceTop;
74 | spaceLeft = defaults.spaceLeft;
75 | spaceBottom = defaults.spaceBottom;
76 | spaceRight = defaults.spaceRight;
77 | padTop = defaults.padTop;
78 | padLeft = defaults.padLeft;
79 | padBottom = defaults.padBottom;
80 | padRight = defaults.padRight;
81 | fillX = defaults.fillX;
82 | fillY = defaults.fillY;
83 | align = defaults.align;
84 | expandX = defaults.expandX;
85 | expandY = defaults.expandY;
86 | ignore = defaults.ignore;
87 | colspan = defaults.colspan;
88 | uniformX = defaults.uniformX;
89 | uniformY = defaults.uniformY;
90 | }
91 |
92 | void merge (Cell cell) {
93 | if (cell == null) return;
94 | if (cell.minWidth != null) minWidth = cell.minWidth;
95 | if (cell.minHeight != null) minHeight = cell.minHeight;
96 | if (cell.prefWidth != null) prefWidth = cell.prefWidth;
97 | if (cell.prefHeight != null) prefHeight = cell.prefHeight;
98 | if (cell.maxWidth != null) maxWidth = cell.maxWidth;
99 | if (cell.maxHeight != null) maxHeight = cell.maxHeight;
100 | if (cell.spaceTop != null) spaceTop = cell.spaceTop;
101 | if (cell.spaceLeft != null) spaceLeft = cell.spaceLeft;
102 | if (cell.spaceBottom != null) spaceBottom = cell.spaceBottom;
103 | if (cell.spaceRight != null) spaceRight = cell.spaceRight;
104 | if (cell.padTop != null) padTop = cell.padTop;
105 | if (cell.padLeft != null) padLeft = cell.padLeft;
106 | if (cell.padBottom != null) padBottom = cell.padBottom;
107 | if (cell.padRight != null) padRight = cell.padRight;
108 | if (cell.fillX != null) fillX = cell.fillX;
109 | if (cell.fillY != null) fillY = cell.fillY;
110 | if (cell.align != null) align = cell.align;
111 | if (cell.expandX != null) expandX = cell.expandX;
112 | if (cell.expandY != null) expandY = cell.expandY;
113 | if (cell.ignore != null) ignore = cell.ignore;
114 | if (cell.colspan != null) colspan = cell.colspan;
115 | if (cell.uniformX != null) uniformX = cell.uniformX;
116 | if (cell.uniformY != null) uniformY = cell.uniformY;
117 | }
118 |
119 | /** Sets the widget in this cell and adds the widget to the cell's table. If null, removes any current widget. */
120 | public Cell setWidget (C widget) {
121 | layout.toolkit.setWidget(layout, this, widget);
122 | return this;
123 | }
124 |
125 | /** Returns the widget for this cell, or null. */
126 | public C getWidget () {
127 | return widget;
128 | }
129 |
130 | /** Returns true if the cell's widget is not null. */
131 | public boolean hasWidget () {
132 | return widget != null;
133 | }
134 |
135 | /** Sets the minWidth, prefWidth, maxWidth, minHeight, prefHeight, and maxHeight to the specified value. */
136 | public Cell size (Value size) {
137 | minWidth = size;
138 | minHeight = size;
139 | prefWidth = size;
140 | prefHeight = size;
141 | maxWidth = size;
142 | maxHeight = size;
143 | return this;
144 | }
145 |
146 | /** Sets the minWidth, prefWidth, maxWidth, minHeight, prefHeight, and maxHeight to the specified values. */
147 | public Cell size (Value width, Value height) {
148 | minWidth = width;
149 | minHeight = height;
150 | prefWidth = width;
151 | prefHeight = height;
152 | maxWidth = width;
153 | maxHeight = height;
154 | return this;
155 | }
156 |
157 | /** Sets the minWidth, prefWidth, maxWidth, minHeight, prefHeight, and maxHeight to the specified value. */
158 | public Cell size (float size) {
159 | size(new FixedValue(size));
160 | return this;
161 | }
162 |
163 | /** Sets the minWidth, prefWidth, maxWidth, minHeight, prefHeight, and maxHeight to the specified values. */
164 | public Cell size (float width, float height) {
165 | size(new FixedValue(width), new FixedValue(height));
166 | return this;
167 | }
168 |
169 | /** Sets the minWidth, prefWidth, and maxWidth to the specified value. */
170 | public Cell width (Value width) {
171 | minWidth = width;
172 | prefWidth = width;
173 | maxWidth = width;
174 | return this;
175 | }
176 |
177 | /** Sets the minWidth, prefWidth, and maxWidth to the specified value. */
178 | public Cell width (float width) {
179 | width(new FixedValue(width));
180 | return this;
181 | }
182 |
183 | /** Sets the minHeight, prefHeight, and maxHeight to the specified value. */
184 | public Cell height (Value height) {
185 | minHeight = height;
186 | prefHeight = height;
187 | maxHeight = height;
188 | return this;
189 | }
190 |
191 | /** Sets the minHeight, prefHeight, and maxHeight to the specified value. */
192 | public Cell height (float height) {
193 | height(new FixedValue(height));
194 | return this;
195 | }
196 |
197 | /** Sets the minWidth and minHeight to the specified value. */
198 | public Cell minSize (Value size) {
199 | minWidth = size;
200 | minHeight = size;
201 | return this;
202 | }
203 |
204 | /** Sets the minWidth and minHeight to the specified values. */
205 | public Cell minSize (Value width, Value height) {
206 | minWidth = width;
207 | minHeight = height;
208 | return this;
209 | }
210 |
211 | public Cell minWidth (Value minWidth) {
212 | this.minWidth = minWidth;
213 | return this;
214 | }
215 |
216 | public Cell minHeight (Value minHeight) {
217 | this.minHeight = minHeight;
218 | return this;
219 | }
220 |
221 | /** Sets the minWidth and minHeight to the specified value. */
222 | public Cell minSize (float size) {
223 | minWidth = new FixedValue(size);
224 | minHeight = new FixedValue(size);
225 | return this;
226 | }
227 |
228 | /** Sets the minWidth and minHeight to the specified values. */
229 | public Cell minSize (float width, float height) {
230 | minWidth = new FixedValue(width);
231 | minHeight = new FixedValue(height);
232 | return this;
233 | }
234 |
235 | public Cell minWidth (float minWidth) {
236 | this.minWidth = new FixedValue(minWidth);
237 | return this;
238 | }
239 |
240 | public Cell minHeight (float minHeight) {
241 | this.minHeight = new FixedValue(minHeight);
242 | return this;
243 | }
244 |
245 | /** Sets the prefWidth and prefHeight to the specified value. */
246 | public Cell prefSize (Value size) {
247 | prefWidth = size;
248 | prefHeight = size;
249 | return this;
250 | }
251 |
252 | /** Sets the prefWidth and prefHeight to the specified values. */
253 | public Cell prefSize (Value width, Value height) {
254 | prefWidth = width;
255 | prefHeight = height;
256 | return this;
257 | }
258 |
259 | public Cell prefWidth (Value prefWidth) {
260 | this.prefWidth = prefWidth;
261 | return this;
262 | }
263 |
264 | public Cell prefHeight (Value prefHeight) {
265 | this.prefHeight = prefHeight;
266 | return this;
267 | }
268 |
269 | /** Sets the prefWidth and prefHeight to the specified value. */
270 | public Cell prefSize (float width, float height) {
271 | prefWidth = new FixedValue(width);
272 | prefHeight = new FixedValue(height);
273 | return this;
274 | }
275 |
276 | /** Sets the prefWidth and prefHeight to the specified values. */
277 | public Cell prefSize (float size) {
278 | prefWidth = new FixedValue(size);
279 | prefHeight = new FixedValue(size);
280 | return this;
281 | }
282 |
283 | public Cell prefWidth (float prefWidth) {
284 | this.prefWidth = new FixedValue(prefWidth);
285 | return this;
286 | }
287 |
288 | public Cell prefHeight (float prefHeight) {
289 | this.prefHeight = new FixedValue(prefHeight);
290 | return this;
291 | }
292 |
293 | /** Sets the maxWidth and maxHeight to the specified value. */
294 | public Cell maxSize (Value size) {
295 | maxWidth = size;
296 | maxHeight = size;
297 | return this;
298 | }
299 |
300 | /** Sets the maxWidth and maxHeight to the specified values. */
301 | public Cell maxSize (Value width, Value height) {
302 | maxWidth = width;
303 | maxHeight = height;
304 | return this;
305 | }
306 |
307 | public Cell maxWidth (Value maxWidth) {
308 | this.maxWidth = maxWidth;
309 | return this;
310 | }
311 |
312 | public Cell maxHeight (Value maxHeight) {
313 | this.maxHeight = maxHeight;
314 | return this;
315 | }
316 |
317 | /** Sets the maxWidth and maxHeight to the specified value. */
318 | public Cell maxSize (float size) {
319 | maxWidth = new FixedValue(size);
320 | maxHeight = new FixedValue(size);
321 | return this;
322 | }
323 |
324 | /** Sets the maxWidth and maxHeight to the specified values. */
325 | public Cell maxSize (float width, float height) {
326 | maxWidth = new FixedValue(width);
327 | maxHeight = new FixedValue(height);
328 | return this;
329 | }
330 |
331 | public Cell maxWidth (float maxWidth) {
332 | this.maxWidth = new FixedValue(maxWidth);
333 | return this;
334 | }
335 |
336 | public Cell maxHeight (float maxHeight) {
337 | this.maxHeight = new FixedValue(maxHeight);
338 | return this;
339 | }
340 |
341 | /** Sets the spaceTop, spaceLeft, spaceBottom, and spaceRight to the specified value. */
342 | public Cell space (Value space) {
343 | spaceTop = space;
344 | spaceLeft = space;
345 | spaceBottom = space;
346 | spaceRight = space;
347 | return this;
348 | }
349 |
350 | public Cell space (Value top, Value left, Value bottom, Value right) {
351 | spaceTop = top;
352 | spaceLeft = left;
353 | spaceBottom = bottom;
354 | spaceRight = right;
355 | return this;
356 | }
357 |
358 | public Cell spaceTop (Value spaceTop) {
359 | this.spaceTop = spaceTop;
360 | return this;
361 | }
362 |
363 | public Cell spaceLeft (Value spaceLeft) {
364 | this.spaceLeft = spaceLeft;
365 | return this;
366 | }
367 |
368 | public Cell spaceBottom (Value spaceBottom) {
369 | this.spaceBottom = spaceBottom;
370 | return this;
371 | }
372 |
373 | public Cell spaceRight (Value spaceRight) {
374 | this.spaceRight = spaceRight;
375 | return this;
376 | }
377 |
378 | /** Sets the spaceTop, spaceLeft, spaceBottom, and spaceRight to the specified value. */
379 | public Cell space (float space) {
380 | if (space < 0) throw new IllegalArgumentException("space cannot be < 0.");
381 | Value value = new FixedValue(space);
382 | spaceTop = value;
383 | spaceLeft = value;
384 | spaceBottom = value;
385 | spaceRight = value;
386 | return this;
387 | }
388 |
389 | public Cell space (float top, float left, float bottom, float right) {
390 | if (top < 0) throw new IllegalArgumentException("top cannot be < 0.");
391 | if (left < 0) throw new IllegalArgumentException("left cannot be < 0.");
392 | if (bottom < 0) throw new IllegalArgumentException("bottom cannot be < 0.");
393 | if (right < 0) throw new IllegalArgumentException("right cannot be < 0.");
394 | spaceTop = new FixedValue(top);
395 | spaceLeft = new FixedValue(left);
396 | spaceBottom = new FixedValue(bottom);
397 | spaceRight = new FixedValue(right);
398 | return this;
399 | }
400 |
401 | public Cell spaceTop (float spaceTop) {
402 | if (spaceTop < 0) throw new IllegalArgumentException("spaceTop cannot be < 0.");
403 | this.spaceTop = new FixedValue(spaceTop);
404 | return this;
405 | }
406 |
407 | public Cell spaceLeft (float spaceLeft) {
408 | if (spaceLeft < 0) throw new IllegalArgumentException("spaceLeft cannot be < 0.");
409 | this.spaceLeft = new FixedValue(spaceLeft);
410 | return this;
411 | }
412 |
413 | public Cell spaceBottom (float spaceBottom) {
414 | if (spaceBottom < 0) throw new IllegalArgumentException("spaceBottom cannot be < 0.");
415 | this.spaceBottom = new FixedValue(spaceBottom);
416 | return this;
417 | }
418 |
419 | public Cell spaceRight (float spaceRight) {
420 | if (spaceRight < 0) throw new IllegalArgumentException("spaceRight cannot be < 0.");
421 | this.spaceRight = new FixedValue(spaceRight);
422 | return this;
423 | }
424 |
425 | /** Sets the padTop, padLeft, padBottom, and padRight to the specified value. */
426 | public Cell pad (Value pad) {
427 | padTop = pad;
428 | padLeft = pad;
429 | padBottom = pad;
430 | padRight = pad;
431 | return this;
432 | }
433 |
434 | public Cell pad (Value top, Value left, Value bottom, Value right) {
435 | padTop = top;
436 | padLeft = left;
437 | padBottom = bottom;
438 | padRight = right;
439 | return this;
440 | }
441 |
442 | public Cell padTop (Value padTop) {
443 | this.padTop = padTop;
444 | return this;
445 | }
446 |
447 | public Cell padLeft (Value padLeft) {
448 | this.padLeft = padLeft;
449 | return this;
450 | }
451 |
452 | public Cell padBottom (Value padBottom) {
453 | this.padBottom = padBottom;
454 | return this;
455 | }
456 |
457 | public Cell padRight (Value padRight) {
458 | this.padRight = padRight;
459 | return this;
460 | }
461 |
462 | /** Sets the padTop, padLeft, padBottom, and padRight to the specified value. */
463 | public Cell pad (float pad) {
464 | Value value = new FixedValue(pad);
465 | padTop = value;
466 | padLeft = value;
467 | padBottom = value;
468 | padRight = value;
469 | return this;
470 | }
471 |
472 | public Cell pad (float top, float left, float bottom, float right) {
473 | padTop = new FixedValue(top);
474 | padLeft = new FixedValue(left);
475 | padBottom = new FixedValue(bottom);
476 | padRight = new FixedValue(right);
477 | return this;
478 | }
479 |
480 | public Cell padTop (float padTop) {
481 | this.padTop = new FixedValue(padTop);
482 | return this;
483 | }
484 |
485 | public Cell padLeft (float padLeft) {
486 | this.padLeft = new FixedValue(padLeft);
487 | return this;
488 | }
489 |
490 | public Cell padBottom (float padBottom) {
491 | this.padBottom = new FixedValue(padBottom);
492 | return this;
493 | }
494 |
495 | public Cell padRight (float padRight) {
496 | this.padRight = new FixedValue(padRight);
497 | return this;
498 | }
499 |
500 | /** Sets fillX and fillY to 1. */
501 | public Cell fill () {
502 | fillX = 1f;
503 | fillY = 1f;
504 | return this;
505 | }
506 |
507 | /** Sets fillX to 1. */
508 | public Cell fillX () {
509 | fillX = 1f;
510 | return this;
511 | }
512 |
513 | /** Sets fillY to 1. */
514 | public Cell fillY () {
515 | fillY = 1f;
516 | return this;
517 | }
518 |
519 | public Cell fill (Float x, Float y) {
520 | fillX = x;
521 | fillY = y;
522 | return this;
523 | }
524 |
525 | /** Sets fillX and fillY to 1 if true, 0 if false. */
526 | public Cell fill (boolean x, boolean y) {
527 | fillX = x ? 1f : 0;
528 | fillY = y ? 1f : 0;
529 | return this;
530 | }
531 |
532 | /** Sets fillX and fillY to 1 if true, 0 if false. */
533 | public Cell fill (boolean fill) {
534 | fillX = fill ? 1f : 0;
535 | fillY = fill ? 1f : 0;
536 | return this;
537 | }
538 |
539 | /** Sets the alignment of the widget within the cell. Set to {@link #CENTER}, {@link #TOP}, {@link #BOTTOM}, {@link #LEFT},
540 | * {@link #RIGHT}, or any combination of those. */
541 | public Cell align (Integer align) {
542 | this.align = align;
543 | return this;
544 | }
545 |
546 | /** Sets the alignment of the widget within the cell to {@link #CENTER}. This clears any other alignment. */
547 | public Cell center () {
548 | align = CENTER;
549 | return this;
550 | }
551 |
552 | /** Adds {@link #TOP} and clears {@link #BOTTOM} for the alignment of the widget within the cell. */
553 | public Cell top () {
554 | if (align == null)
555 | align = TOP;
556 | else {
557 | align |= TOP;
558 | align &= ~BOTTOM;
559 | }
560 | return this;
561 | }
562 |
563 | /** Adds {@link #LEFT} and clears {@link #RIGHT} for the alignment of the widget within the cell. */
564 | public Cell left () {
565 | if (align == null)
566 | align = LEFT;
567 | else {
568 | align |= LEFT;
569 | align &= ~RIGHT;
570 | }
571 | return this;
572 | }
573 |
574 | /** Adds {@link #BOTTOM} and clears {@link #TOP} for the alignment of the widget within the cell. */
575 | public Cell bottom () {
576 | if (align == null)
577 | align = BOTTOM;
578 | else {
579 | align |= BOTTOM;
580 | align &= ~TOP;
581 | }
582 | return this;
583 | }
584 |
585 | /** Adds {@link #RIGHT} and clears {@link #LEFT} for the alignment of the widget within the cell. */
586 | public Cell right () {
587 | if (align == null)
588 | align = RIGHT;
589 | else {
590 | align |= RIGHT;
591 | align &= ~LEFT;
592 | }
593 | return this;
594 | }
595 |
596 | /** Sets expandX and expandY to 1. */
597 | public Cell expand () {
598 | expandX = 1;
599 | expandY = 1;
600 | return this;
601 | }
602 |
603 | /** Sets expandX to 1. */
604 | public Cell expandX () {
605 | expandX = 1;
606 | return this;
607 | }
608 |
609 | /** Sets expandY to 1. */
610 | public Cell expandY () {
611 | expandY = 1;
612 | return this;
613 | }
614 |
615 | public Cell expand (Integer x, Integer y) {
616 | expandX = x;
617 | expandY = y;
618 | return this;
619 | }
620 |
621 | /** Sets expandX and expandY to 1 if true, 0 if false. */
622 | public Cell expand (boolean x, boolean y) {
623 | expandX = x ? 1 : 0;
624 | expandY = y ? 1 : 0;
625 | return this;
626 | }
627 |
628 | public Cell ignore (Boolean ignore) {
629 | this.ignore = ignore;
630 | return this;
631 | }
632 |
633 | /** Sets ignore to true. */
634 | public Cell ignore () {
635 | this.ignore = true;
636 | return this;
637 | }
638 |
639 | public boolean getIgnore () {
640 | return ignore != null && ignore == true;
641 | }
642 |
643 | public Cell colspan (Integer colspan) {
644 | this.colspan = colspan;
645 | return this;
646 | }
647 |
648 | /** Sets uniformX and uniformY to true. */
649 | public Cell uniform () {
650 | uniformX = true;
651 | uniformY = true;
652 | return this;
653 | }
654 |
655 | /** Sets uniformX to true. */
656 | public Cell uniformX () {
657 | uniformX = true;
658 | return this;
659 | }
660 |
661 | /** Sets uniformY to true. */
662 | public Cell uniformY () {
663 | uniformY = true;
664 | return this;
665 | }
666 |
667 | public Cell uniform (Boolean x, Boolean y) {
668 | uniformX = x;
669 | uniformY = y;
670 | return this;
671 | }
672 |
673 | public float getWidgetX () {
674 | return widgetX;
675 | }
676 |
677 | public void setWidgetX (float widgetX) {
678 | this.widgetX = widgetX;
679 | }
680 |
681 | public float getWidgetY () {
682 | return widgetY;
683 | }
684 |
685 | public void setWidgetY (float widgetY) {
686 | this.widgetY = widgetY;
687 | }
688 |
689 | public float getWidgetWidth () {
690 | return widgetWidth;
691 | }
692 |
693 | public void setWidgetWidth (float widgetWidth) {
694 | this.widgetWidth = widgetWidth;
695 | }
696 |
697 | public float getWidgetHeight () {
698 | return widgetHeight;
699 | }
700 |
701 | public void setWidgetHeight (float widgetHeight) {
702 | this.widgetHeight = widgetHeight;
703 | }
704 |
705 | public int getColumn () {
706 | return column;
707 | }
708 |
709 | public int getRow () {
710 | return row;
711 | }
712 |
713 | /** @return May be null if this cell is row defaults. */
714 | public Value getMinWidthValue () {
715 | return minWidth;
716 | }
717 |
718 | public float getMinWidth () {
719 | return minWidth == null ? 0 : minWidth.width(this);
720 | }
721 |
722 | /** @return May be null if this cell is row defaults. */
723 | public Value getMinHeightValue () {
724 | return minHeight;
725 | }
726 |
727 | public float getMinHeight () {
728 | return minHeight == null ? 0 : minHeight.height(this);
729 | }
730 |
731 | /** @return May be null if this cell is row defaults. */
732 | public Value getPrefWidthValue () {
733 | return prefWidth;
734 | }
735 |
736 | public float getPrefWidth () {
737 | return prefWidth == null ? 0 : prefWidth.width(this);
738 | }
739 |
740 | /** @return May be null if this cell is row defaults. */
741 | public Value getPrefHeightValue () {
742 | return prefHeight;
743 | }
744 |
745 | public float getPrefHeight () {
746 | return prefHeight == null ? 0 : prefHeight.height(this);
747 | }
748 |
749 | /** @return May be null if this cell is row defaults. */
750 | public Value getMaxWidthValue () {
751 | return maxWidth;
752 | }
753 |
754 | public float getMaxWidth () {
755 | return maxWidth == null ? 0 : maxWidth.width(this);
756 | }
757 |
758 | /** @return May be null if this cell is row defaults. */
759 | public Value getMaxHeightValue () {
760 | return maxHeight;
761 | }
762 |
763 | public float getMaxHeight () {
764 | return maxHeight == null ? 0 : maxHeight.height(this);
765 | }
766 |
767 | /** @return May be null if this value is not set. */
768 | public Value getSpaceTopValue () {
769 | return spaceTop;
770 | }
771 |
772 | public float getSpaceTop () {
773 | return spaceTop == null ? 0 : spaceTop.height(this);
774 | }
775 |
776 | /** @return May be null if this value is not set. */
777 | public Value getSpaceLeftValue () {
778 | return spaceLeft;
779 | }
780 |
781 | public float getSpaceLeft () {
782 | return spaceLeft == null ? 0 : spaceLeft.width(this);
783 | }
784 |
785 | /** @return May be null if this value is not set. */
786 | public Value getSpaceBottomValue () {
787 | return spaceBottom;
788 | }
789 |
790 | public float getSpaceBottom () {
791 | return spaceBottom == null ? 0 : spaceBottom.height(this);
792 | }
793 |
794 | /** @return May be null if this value is not set. */
795 | public Value getSpaceRightValue () {
796 | return spaceRight;
797 | }
798 |
799 | public float getSpaceRight () {
800 | return spaceRight == null ? 0 : spaceRight.width(this);
801 | }
802 |
803 | /** @return May be null if this value is not set. */
804 | public Value getPadTopValue () {
805 | return padTop;
806 | }
807 |
808 | public float getPadTop () {
809 | return padTop == null ? 0 : padTop.height(this);
810 | }
811 |
812 | /** @return May be null if this value is not set. */
813 | public Value getPadLeftValue () {
814 | return padLeft;
815 | }
816 |
817 | public float getPadLeft () {
818 | return padLeft == null ? 0 : padLeft.width(this);
819 | }
820 |
821 | /** @return May be null if this value is not set. */
822 | public Value getPadBottomValue () {
823 | return padBottom;
824 | }
825 |
826 | public float getPadBottom () {
827 | return padBottom == null ? 0 : padBottom.height(this);
828 | }
829 |
830 | /** @return May be null if this value is not set. */
831 | public Value getPadRightValue () {
832 | return padRight;
833 | }
834 |
835 | public float getPadRight () {
836 | return padRight == null ? 0 : padRight.width(this);
837 | }
838 |
839 | /** @return May be null if this value is not set. */
840 | public Float getFillX () {
841 | return fillX;
842 | }
843 |
844 | /** @return May be null. */
845 | public Float getFillY () {
846 | return fillY;
847 | }
848 |
849 | /** @return May be null. */
850 | public Integer getAlign () {
851 | return align;
852 | }
853 |
854 | /** @return May be null. */
855 | public Integer getExpandX () {
856 | return expandX;
857 | }
858 |
859 | /** @return May be null. */
860 | public Integer getExpandY () {
861 | return expandY;
862 | }
863 |
864 | /** @return May be null. */
865 | public Integer getColspan () {
866 | return colspan;
867 | }
868 |
869 | /** @return May be null. */
870 | public Boolean getUniformX () {
871 | return uniformX;
872 | }
873 |
874 | /** @return May be null. */
875 | public Boolean getUniformY () {
876 | return uniformY;
877 | }
878 |
879 | /** Returns true if this cell is the last cell in the row. */
880 | public boolean isEndRow () {
881 | return endRow;
882 | }
883 |
884 | /** The actual amount of combined padding and spacing from the last layout. */
885 | public float getComputedPadTop () {
886 | return computedPadTop;
887 | }
888 |
889 | /** The actual amount of combined padding and spacing from the last layout. */
890 | public float getComputedPadLeft () {
891 | return computedPadLeft;
892 | }
893 |
894 | /** The actual amount of combined padding and spacing from the last layout. */
895 | public float getComputedPadBottom () {
896 | return computedPadBottom;
897 | }
898 |
899 | /** The actual amount of combined padding and spacing from the last layout. */
900 | public float getComputedPadRight () {
901 | return computedPadRight;
902 | }
903 |
904 | public Cell row () {
905 | return layout.row();
906 | }
907 |
908 | public BaseTableLayout getLayout () {
909 | return layout;
910 | }
911 |
912 | /** Sets all constraint fields to null. */
913 | public void clear () {
914 | minWidth = null;
915 | minHeight = null;
916 | prefWidth = null;
917 | prefHeight = null;
918 | maxWidth = null;
919 | maxHeight = null;
920 | spaceTop = null;
921 | spaceLeft = null;
922 | spaceBottom = null;
923 | spaceRight = null;
924 | padTop = null;
925 | padLeft = null;
926 | padBottom = null;
927 | padRight = null;
928 | fillX = null;
929 | fillY = null;
930 | align = null;
931 | expandX = null;
932 | expandY = null;
933 | ignore = null;
934 | colspan = null;
935 | uniformX = null;
936 | uniformY = null;
937 | }
938 |
939 | /** Reset state so the cell can be reused. Doesn't reset the constraint fields. */
940 | public void free () {
941 | widget = null;
942 | layout = null;
943 | endRow = false;
944 | cellAboveIndex = -1;
945 | }
946 |
947 | /** Set all constraints to cell default values. */
948 | void defaults () {
949 | minWidth = Value.minWidth;
950 | minHeight = Value.minHeight;
951 | prefWidth = Value.prefWidth;
952 | prefHeight = Value.prefHeight;
953 | maxWidth = Value.maxWidth;
954 | maxHeight = Value.maxHeight;
955 | spaceTop = Value.zero;
956 | spaceLeft = Value.zero;
957 | spaceBottom = Value.zero;
958 | spaceRight = Value.zero;
959 | padTop = Value.zero;
960 | padLeft = Value.zero;
961 | padBottom = Value.zero;
962 | padRight = Value.zero;
963 | fillX = 0f;
964 | fillY = 0f;
965 | align = CENTER;
966 | expandX = 0;
967 | expandY = 0;
968 | ignore = false;
969 | colspan = 1;
970 | uniformX = null;
971 | uniformY = null;
972 | }
973 | }
974 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/Toolkit.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2011, Nathan Sweet
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | * * Redistributions of source code must retain the above copyright
8 | * notice, this list of conditions and the following disclaimer.
9 | * * Redistributions in binary form must reproduce the above copyright
10 | * notice, this list of conditions and the following disclaimer in the
11 | * documentation and/or other materials provided with the distribution.
12 | * * Neither the name of the nor the
13 | * names of its contributors may be used to endorse or promote products
14 | * derived from this software without specific prior written permission.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | ******************************************************************************/
27 |
28 | package com.esotericsoftware.tablelayout;
29 |
30 | import com.esotericsoftware.tablelayout.BaseTableLayout.Debug;
31 |
32 | /** Base class for UI toolkit.
33 | * @author Nathan Sweet */
34 | public abstract class Toolkit {
35 | static public Toolkit instance;
36 |
37 | abstract public Cell obtainCell (L layout);
38 |
39 | abstract public void freeCell (Cell cell);
40 |
41 | abstract public void addChild (C parent, C child);
42 |
43 | abstract public void removeChild (C parent, C child);
44 |
45 | abstract public float getMinWidth (C widget);
46 |
47 | abstract public float getMinHeight (C widget);
48 |
49 | abstract public float getPrefWidth (C widget);
50 |
51 | abstract public float getPrefHeight (C widget);
52 |
53 | abstract public float getMaxWidth (C widget);
54 |
55 | abstract public float getMaxHeight (C widget);
56 |
57 | abstract public float getWidth (C widget);
58 |
59 | abstract public float getHeight (C widget);
60 |
61 | /** Clears all debugging rectangles. */
62 | abstract public void clearDebugRectangles (L layout);
63 |
64 | /** Adds a rectangle that should be drawn for debugging. */
65 | abstract public void addDebugRectangle (L layout, Debug type, float x, float y, float w, float h);
66 |
67 | /** @param widget May be null. */
68 | public void setWidget (L layout, Cell cell, C widget) {
69 | if (cell.widget == widget) return;
70 | removeChild((T)layout.table, (C)cell.widget);
71 | cell.widget = widget;
72 | if (widget != null) addChild((T)layout.table, widget);
73 | }
74 |
75 | /** Interprets the specified value as a size. This can be used to scale all sizes applied to a table. The default implementation
76 | * returns the value unmodified.
77 | * @see Value#width(Object)
78 | * @see Value#width(Cell) */
79 | public float width (float value) {
80 | return value;
81 | }
82 |
83 | /** Interprets the specified value as a size. This can be used to scale all sizes applied to a table. The default implementation
84 | * returns the value unmodified.
85 | * @see Value#height(Object)
86 | * @see Value#height(Cell) */
87 | public float height (float value) {
88 | return value;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/Value.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2011, Nathan Sweet
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | * * Redistributions of source code must retain the above copyright
8 | * notice, this list of conditions and the following disclaimer.
9 | * * Redistributions in binary form must reproduce the above copyright
10 | * notice, this list of conditions and the following disclaimer in the
11 | * documentation and/or other materials provided with the distribution.
12 | * * Neither the name of the nor the
13 | * names of its contributors may be used to endorse or promote products
14 | * derived from this software without specific prior written permission.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | ******************************************************************************/
27 |
28 | package com.esotericsoftware.tablelayout;
29 |
30 | /** Base class for a table or cell property value. Values are provided a table or cell for context. Eg, the value may compute its
31 | * size taking into consideration the size of the table or the widget in the cell. Some values may be only valid for use with
32 | * either call.
33 | * @author Nathan Sweet */
34 | abstract public class Value {
35 | /** Returns the value in the context of the specified table. */
36 | abstract public float get (Object table);
37 |
38 | /** Returns the value in the context of the specified cell. */
39 | abstract public float get (Cell cell);
40 |
41 | /** Returns the value in the context of a width for the specified table. */
42 | public float width (Object table) {
43 | return Toolkit.instance.width(get(table));
44 | }
45 |
46 | /** Returns the value in the context of a height for the specified table. */
47 | public float height (Object table) {
48 | return Toolkit.instance.height(get(table));
49 | }
50 |
51 | /** Returns the value in the context of a width for the specified cell. */
52 | public float width (Cell cell) {
53 | return Toolkit.instance.width(get(cell));
54 | }
55 |
56 | /** Returns the value in the context of a height for the specified cell. */
57 | public float height (Cell cell) {
58 | return Toolkit.instance.height(get(cell));
59 | }
60 |
61 | /** A value that is always zero. */
62 | static public final Value zero = new CellValue() {
63 | public float get (Cell cell) {
64 | return 0;
65 | }
66 |
67 | public float get (Object table) {
68 | return 0;
69 | }
70 | };
71 |
72 | /** A value that is only valid for use with a cell.
73 | * @author Nathan Sweet */
74 | static abstract public class CellValue extends Value {
75 | public float get (Object table) {
76 | throw new UnsupportedOperationException("This value can only be used for a cell property.");
77 | }
78 | }
79 |
80 | /** A value that is valid for use with a table or a cell.
81 | * @author Nathan Sweet */
82 | static abstract public class TableValue extends Value {
83 | public float get (Cell cell) {
84 | return get(cell.getLayout().getTable());
85 | }
86 | }
87 |
88 | /** A fixed value that is not computed each time it is used.
89 | * @author Nathan Sweet */
90 | static public class FixedValue extends Value {
91 | private float value;
92 |
93 | public FixedValue (float value) {
94 | this.value = value;
95 | }
96 |
97 | public void set (float value) {
98 | this.value = value;
99 | }
100 |
101 | public float get (Object table) {
102 | return value;
103 | }
104 |
105 | public float get (Cell cell) {
106 | return value;
107 | }
108 | }
109 |
110 | /** Value for a cell that is the minWidth of the widget in the cell. */
111 | static public Value minWidth = new CellValue() {
112 | public float get (Cell cell) {
113 | if (cell == null) throw new RuntimeException("minWidth can only be set on a cell property.");
114 | Object widget = cell.widget;
115 | if (widget == null) return 0;
116 | return Toolkit.instance.getMinWidth(widget);
117 | }
118 | };
119 |
120 | /** Value for a cell that is the minHeight of the widget in the cell. */
121 | static public Value minHeight = new CellValue() {
122 | public float get (Cell cell) {
123 | if (cell == null) throw new RuntimeException("minHeight can only be set on a cell property.");
124 | Object widget = cell.widget;
125 | if (widget == null) return 0;
126 | return Toolkit.instance.getMinHeight(widget);
127 | }
128 | };
129 |
130 | /** Value for a cell that is the prefWidth of the widget in the cell. */
131 | static public Value prefWidth = new CellValue() {
132 | public float get (Cell cell) {
133 | if (cell == null) throw new RuntimeException("prefWidth can only be set on a cell property.");
134 | Object widget = cell.widget;
135 | if (widget == null) return 0;
136 | return Toolkit.instance.getPrefWidth(widget);
137 | }
138 | };
139 |
140 | /** Value for a cell that is the prefHeight of the widget in the cell. */
141 | static public Value prefHeight = new CellValue() {
142 | public float get (Cell cell) {
143 | if (cell == null) throw new RuntimeException("prefHeight can only be set on a cell property.");
144 | Object widget = cell.widget;
145 | if (widget == null) return 0;
146 | return Toolkit.instance.getPrefHeight(widget);
147 | }
148 | };
149 |
150 | /** Value for a cell that is the maxWidth of the widget in the cell. */
151 | static public Value maxWidth = new CellValue() {
152 | public float get (Cell cell) {
153 | if (cell == null) throw new RuntimeException("maxWidth can only be set on a cell property.");
154 | Object widget = cell.widget;
155 | if (widget == null) return 0;
156 | return Toolkit.instance.getMaxWidth(widget);
157 | }
158 | };
159 |
160 | /** Value for a cell that is the maxHeight of the widget in the cell. */
161 | static public Value maxHeight = new CellValue() {
162 | public float get (Cell cell) {
163 | if (cell == null) throw new RuntimeException("maxHeight can only be set on a cell property.");
164 | Object widget = cell.widget;
165 | if (widget == null) return 0;
166 | return Toolkit.instance.getMaxHeight(widget);
167 | }
168 | };
169 |
170 | /** Returns a value that is a percentage of the table's width. */
171 | static public Value percentWidth (final float percent) {
172 | return new TableValue() {
173 | public float get (Object table) {
174 | return Toolkit.instance.getWidth(table) * percent;
175 | }
176 | };
177 | }
178 |
179 | /** Returns a value that is a percentage of the table's height. */
180 | static public Value percentHeight (final float percent) {
181 | return new TableValue() {
182 | public float get (Object table) {
183 | return Toolkit.instance.getHeight(table) * percent;
184 | }
185 | };
186 | }
187 |
188 | /** Returns a value that is a percentage of the specified widget's width. */
189 | static public Value percentWidth (final float percent, final Object widget) {
190 | return new Value() {
191 | public float get (Cell cell) {
192 | return Toolkit.instance.getWidth(widget) * percent;
193 | }
194 |
195 | public float get (Object table) {
196 | return Toolkit.instance.getWidth(widget) * percent;
197 | }
198 | };
199 | }
200 |
201 | /** Returns a value that is a percentage of the specified widget's height. */
202 | static public Value percentHeight (final float percent, final Object widget) {
203 | return new TableValue() {
204 | public float get (Object table) {
205 | return Toolkit.instance.getHeight(widget) * percent;
206 | }
207 | };
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/swing/Stack.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.tablelayout.swing;
3 |
4 | import java.awt.Component;
5 | import java.awt.Container;
6 | import java.awt.Dimension;
7 | import java.awt.Graphics;
8 | import java.awt.LayoutManager;
9 |
10 | import javax.swing.JPanel;
11 |
12 | public class Stack extends JPanel {
13 | public Stack () {
14 | super(new LayoutManager() {
15 | public void layoutContainer (Container parent) {
16 | int width = parent.getWidth();
17 | int height = parent.getHeight();
18 | for (int i = 0, n = parent.getComponentCount(); i < n; i++) {
19 | parent.getComponent(i).setLocation(0, 0);
20 | parent.getComponent(i).setSize(width, height);
21 | }
22 | }
23 |
24 | public Dimension preferredLayoutSize (Container parent) {
25 | Dimension size = new Dimension();
26 | for (int i = 0, n = parent.getComponentCount(); i < n; i++) {
27 | Dimension pref = parent.getComponent(i).getPreferredSize();
28 | size.width = Math.max(size.width, pref.width);
29 | size.height = Math.max(size.height, pref.height);
30 | }
31 | return size;
32 | }
33 |
34 | public Dimension minimumLayoutSize (Container parent) {
35 | Dimension size = new Dimension();
36 | for (int i = 0, n = parent.getComponentCount(); i < n; i++) {
37 | Dimension min = parent.getComponent(i).getMinimumSize();
38 | size.width = Math.max(size.width, min.width);
39 | size.height = Math.max(size.height, min.height);
40 | }
41 | return size;
42 | }
43 |
44 | public void addLayoutComponent (String name, Component comp) {
45 | }
46 |
47 | public void removeLayoutComponent (Component comp) {
48 | }
49 | });
50 | }
51 |
52 | protected void paintChildren (Graphics g) {
53 | super.paintChildren(g);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/swing/SwingToolkit.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.tablelayout.swing;
3 |
4 | import com.esotericsoftware.tablelayout.BaseTableLayout.Debug;
5 | import com.esotericsoftware.tablelayout.Cell;
6 | import com.esotericsoftware.tablelayout.Toolkit;
7 |
8 | import java.awt.Component;
9 | import java.awt.Container;
10 | import java.awt.EventQueue;
11 | import java.util.ArrayList;
12 | import java.util.Timer;
13 | import java.util.TimerTask;
14 |
15 | import javax.swing.JScrollPane;
16 |
17 | class SwingToolkit extends Toolkit {
18 | static Timer timer;
19 | static ArrayList debugLayouts = new ArrayList(0);
20 |
21 | public Cell obtainCell (TableLayout layout) {
22 | Cell cell = new Cell();
23 | cell.setLayout(layout);
24 | return cell;
25 | }
26 |
27 | public void freeCell (Cell cell) {
28 | }
29 |
30 | public void addChild (Component parent, Component child) {
31 | if (parent instanceof JScrollPane)
32 | ((JScrollPane)parent).setViewportView(child);
33 | else
34 | ((Container)parent).add(child);
35 | }
36 |
37 | public void removeChild (Component parent, Component child) {
38 | ((Container)parent).remove(child);
39 | }
40 |
41 | public float getMinWidth (Component widget) {
42 | return widget.getMinimumSize().width;
43 | }
44 |
45 | public float getMinHeight (Component widget) {
46 | return widget.getMinimumSize().height;
47 | }
48 |
49 | public float getPrefWidth (Component widget) {
50 | return widget.getPreferredSize().width;
51 | }
52 |
53 | public float getPrefHeight (Component widget) {
54 | return widget.getPreferredSize().height;
55 | }
56 |
57 | public float getMaxWidth (Component widget) {
58 | return widget.getMaximumSize().width;
59 | }
60 |
61 | public float getMaxHeight (Component widget) {
62 | return widget.getMaximumSize().height;
63 | }
64 |
65 | public float getWidth (Component widget) {
66 | return widget.getWidth();
67 | }
68 |
69 | public float getHeight (Component widget) {
70 | return widget.getHeight();
71 | }
72 |
73 | public void clearDebugRectangles (TableLayout layout) {
74 | if (layout.debugRects != null) debugLayouts.remove(this);
75 | layout.debugRects = null;
76 | }
77 |
78 | public void addDebugRectangle (TableLayout layout, Debug type, float x, float y, float w, float h) {
79 | if (layout.debugRects == null) {
80 | layout.debugRects = new ArrayList();
81 | debugLayouts.add(layout);
82 | }
83 | layout.debugRects.add(new DebugRect(type, x, y, w, h));
84 | }
85 |
86 | static void startDebugTimer () {
87 | if (timer != null) return;
88 | timer = new Timer("TableLayout Debug", true);
89 | timer.schedule(newDebugTask(), 100);
90 | }
91 |
92 | static TimerTask newDebugTask () {
93 | return new TimerTask() {
94 | public void run () {
95 | if (!EventQueue.isDispatchThread()) {
96 | EventQueue.invokeLater(this);
97 | return;
98 | }
99 | for (TableLayout layout : debugLayouts)
100 | layout.drawDebug();
101 | timer.schedule(newDebugTask(), 250);
102 | }
103 | };
104 | }
105 |
106 | static class DebugRect {
107 | final Debug type;
108 | final int x, y, width, height;
109 |
110 | public DebugRect (Debug type, float x, float y, float width, float height) {
111 | this.x = (int)x;
112 | this.y = (int)y;
113 | this.width = (int)(width - 1);
114 | this.height = (int)(height - 1);
115 | this.type = type;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/swing/Table.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.tablelayout.swing;
3 |
4 | import com.esotericsoftware.tablelayout.BaseTableLayout;
5 | import com.esotericsoftware.tablelayout.BaseTableLayout.Debug;
6 | import com.esotericsoftware.tablelayout.Cell;
7 | import com.esotericsoftware.tablelayout.Toolkit;
8 | import com.esotericsoftware.tablelayout.Value;
9 |
10 | import java.awt.Component;
11 | import java.awt.Container;
12 | import java.awt.Dimension;
13 | import java.awt.LayoutManager;
14 | import java.util.List;
15 |
16 | import javax.swing.JComponent;
17 | import javax.swing.JLabel;
18 |
19 | public class Table extends JComponent {
20 | static {
21 | Toolkit.instance = new SwingToolkit();
22 | }
23 |
24 | private final TableLayout layout;
25 |
26 | public Table () {
27 | this(new TableLayout());
28 | }
29 |
30 | public Table (final TableLayout layout) {
31 | this.layout = layout;
32 | layout.setTable(this);
33 |
34 | setLayout(new LayoutManager() {
35 | private Dimension minSize = new Dimension(), prefSize = new Dimension();
36 |
37 | public Dimension preferredLayoutSize (Container parent) {
38 | layout.layout(); // BOZO - Cache layout?
39 | prefSize.width = (int)layout.getMinWidth();
40 | prefSize.height = (int)layout.getMinHeight();
41 | return prefSize;
42 | }
43 |
44 | public Dimension minimumLayoutSize (Container parent) {
45 | layout.layout(); // BOZO - Cache layout?
46 | minSize.width = (int)layout.getMinWidth();
47 | minSize.height = (int)layout.getMinHeight();
48 | return minSize;
49 | }
50 |
51 | public void layoutContainer (Container ignored) {
52 | layout.layout();
53 | }
54 |
55 | public void addLayoutComponent (String name, Component comp) {
56 | }
57 |
58 | public void removeLayoutComponent (Component comp) {
59 | }
60 | });
61 | }
62 |
63 | /** Removes all Components and cells from the table. */
64 | public void clear () {
65 | layout.clear();
66 | invalidate();
67 | }
68 |
69 | public Cell addCell (String text) {
70 | return addCell(new JLabel(text));
71 | }
72 |
73 | /** Adds a cell with a placeholder Component. */
74 | public Cell addCell () {
75 | return addCell((Component)null);
76 | }
77 |
78 | /** Adds a new cell to the table with the specified Component.
79 | * @see TableLayout#add(Component)
80 | * @param Component May be null to add a cell without an Component. */
81 | public Cell addCell (Component Component) {
82 | return layout.add(Component);
83 | }
84 |
85 | /** Adds a new cell to the table with the specified Components in a {@link Stack}.
86 | * @param components May be null to add a cell without an Component. */
87 | public Cell stack (Component... components) {
88 | Stack stack = new Stack();
89 | for (int i = 0, n = components.length; i < n; i++)
90 | stack.add(components[i]);
91 | return addCell(stack);
92 | }
93 |
94 | /** Indicates that subsequent cells should be added to a new row and returns the cell values that will be used as the defaults
95 | * for all cells in the new row.
96 | * @see TableLayout#row() */
97 | public Cell row () {
98 | return layout.row();
99 | }
100 |
101 | /** Gets the cell values that will be used as the defaults for all cells in the specified column.
102 | * @see TableLayout#columnDefaults(int) */
103 | public Cell columnDefaults (int column) {
104 | return layout.columnDefaults(column);
105 | }
106 |
107 | /** The cell values that will be used as the defaults for all cells.
108 | * @see TableLayout#defaults() */
109 | public Cell defaults () {
110 | return layout.defaults();
111 | }
112 |
113 | /** Positions and sizes children of the Component being laid out using the cell associated with each child.
114 | * @see TableLayout#layout() */
115 | public void layout () {
116 | layout.layout();
117 | }
118 |
119 | /** Removes all Components and cells from the table (same as {@link #clear()}) and additionally resets all table properties and
120 | * cell, column, and row defaults.
121 | * @see TableLayout#reset() */
122 | public void reset () {
123 | layout.reset();
124 | }
125 |
126 | /** Returns the cell for the specified Component, anywhere in the table hierarchy.
127 | * @see TableLayout#getCell(Component) */
128 | public Cell getCell (Component Component) {
129 | return layout.getCell(Component);
130 | }
131 |
132 | /** Returns the cells for this table.
133 | * @see TableLayout#getCells() */
134 | public List getCells () {
135 | return layout.getCells();
136 | }
137 |
138 | /** Padding around the table.
139 | * @see TableLayout#pad(Value) */
140 | public Table pad (Value pad) {
141 | layout.pad(pad);
142 | return this;
143 | }
144 |
145 | /** Padding around the table.
146 | * @see TableLayout#pad(Value, Value, Value, Value) */
147 | public Table pad (Value top, Value left, Value bottom, Value right) {
148 | layout.pad(top, left, bottom, right);
149 | return this;
150 | }
151 |
152 | /** Padding at the top of the table.
153 | * @see TableLayout#padTop(Value) */
154 | public Table padTop (Value padTop) {
155 | layout.padTop(padTop);
156 | return this;
157 | }
158 |
159 | /** Padding at the left of the table.
160 | * @see TableLayout#padLeft(Value) */
161 | public Table padLeft (Value padLeft) {
162 | layout.padLeft(padLeft);
163 | return this;
164 | }
165 |
166 | /** Padding at the bottom of the table.
167 | * @see TableLayout#padBottom(Value) */
168 | public Table padBottom (Value padBottom) {
169 | layout.padBottom(padBottom);
170 | return this;
171 | }
172 |
173 | /** Padding at the right of the table.
174 | * @see TableLayout#padRight(Value) */
175 | public Table padRight (Value padRight) {
176 | layout.padRight(padRight);
177 | return this;
178 | }
179 |
180 | /** Padding around the table.
181 | * @see TableLayout#pad(float) */
182 | public Table pad (int pad) {
183 | layout.pad(pad);
184 | return this;
185 | }
186 |
187 | /** Padding around the table.
188 | * @see TableLayout#pad(float, float, float, float) */
189 | public Table pad (int top, int left, int bottom, int right) {
190 | layout.pad(top, left, bottom, right);
191 | return this;
192 | }
193 |
194 | /** Padding at the top of the table.
195 | * @see TableLayout#padTop(float) */
196 | public Table padTop (int padTop) {
197 | layout.padTop(padTop);
198 | return this;
199 | }
200 |
201 | /** Padding at the left of the table.
202 | * @see TableLayout#padLeft(float) */
203 | public Table padLeft (int padLeft) {
204 | layout.padLeft(padLeft);
205 | return this;
206 | }
207 |
208 | /** Padding at the bottom of the table.
209 | * @see TableLayout#padBottom(float) */
210 | public Table padBottom (int padBottom) {
211 | layout.padBottom(padBottom);
212 | return this;
213 | }
214 |
215 | /** Padding at the right of the table.
216 | * @see TableLayout#padRight(float) */
217 | public Table padRight (int padRight) {
218 | layout.padRight(padRight);
219 | return this;
220 | }
221 |
222 | /** Alignment of the table within the Component being laid out. Set to {@link BaseTableLayout#CENTER},
223 | * {@link BaseTableLayout#TOP}, {@link BaseTableLayout#BOTTOM} , {@link BaseTableLayout#LEFT} , {@link BaseTableLayout#RIGHT},
224 | * or any combination of those.
225 | * @see TableLayout#align(int) */
226 | public Table align (int align) {
227 | layout.align(align);
228 | return this;
229 | }
230 |
231 | /** Sets the alignment of the table within the Component being laid out to {@link BaseTableLayout#CENTER}.
232 | * @see TableLayout#center() */
233 | public Table center () {
234 | layout.center();
235 | return this;
236 | }
237 |
238 | /** Sets the alignment of the table within the Component being laid out to {@link BaseTableLayout#TOP}.
239 | * @see TableLayout#top() */
240 | public Table top () {
241 | layout.top();
242 | return this;
243 | }
244 |
245 | /** Sets the alignment of the table within the Component being laid out to {@link BaseTableLayout#LEFT}.
246 | * @see TableLayout#left() */
247 | public Table left () {
248 | layout.left();
249 | return this;
250 | }
251 |
252 | /** Sets the alignment of the table within the Component being laid out to {@link BaseTableLayout#BOTTOM}.
253 | * @see TableLayout#bottom() */
254 | public Table bottom () {
255 | layout.bottom();
256 | return this;
257 | }
258 |
259 | /** Sets the alignment of the table within the Component being laid out to {@link BaseTableLayout#RIGHT}.
260 | * @see TableLayout#right() */
261 | public Table right () {
262 | layout.right();
263 | return this;
264 | }
265 |
266 | /** Turns on all debug lines.
267 | * @see TableLayout#debug() */
268 | public Table debug () {
269 | layout.debug();
270 | return this;
271 | }
272 |
273 | /** Turns on debug lines.
274 | * @see TableLayout#debug() */
275 | public Table debug (Debug debug) {
276 | layout.debug(debug);
277 | return this;
278 | }
279 |
280 | public Debug getDebug () {
281 | return layout.getDebug();
282 | }
283 |
284 | public Value getPadTop () {
285 | return layout.getPadTopValue();
286 | }
287 |
288 | public Value getPadLeft () {
289 | return layout.getPadLeftValue();
290 | }
291 |
292 | public Value getPadBottom () {
293 | return layout.getPadBottomValue();
294 | }
295 |
296 | public Value getPadRight () {
297 | return layout.getPadRightValue();
298 | }
299 |
300 | public int getAlign () {
301 | return layout.getAlign();
302 | }
303 |
304 | public TableLayout getTableLayout () {
305 | return layout;
306 | }
307 |
308 | public void invalidate () {
309 | super.invalidate();
310 | layout.invalidate();
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/src/main/java/com/esotericsoftware/tablelayout/swing/TableLayout.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.tablelayout.swing;
3 |
4 | import java.awt.Color;
5 | import java.awt.Component;
6 | import java.awt.Graphics2D;
7 | import java.awt.Insets;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import com.esotericsoftware.tablelayout.BaseTableLayout;
12 | import com.esotericsoftware.tablelayout.Cell;
13 | import com.esotericsoftware.tablelayout.swing.SwingToolkit.DebugRect;
14 |
15 | class TableLayout extends BaseTableLayout {
16 | ArrayList debugRects;
17 |
18 | public TableLayout () {
19 | super((SwingToolkit)SwingToolkit.instance);
20 | }
21 |
22 | public TableLayout (SwingToolkit toolkit) {
23 | super(toolkit);
24 | }
25 |
26 | public void layout () {
27 | Table table = getTable();
28 | Insets insets = table.getInsets();
29 | super.layout(insets.left, insets.top, //
30 | table.getWidth() - insets.left - insets.right, //
31 | table.getHeight() - insets.top - insets.bottom);
32 |
33 | List cells = getCells();
34 | for (int i = 0, n = cells.size(); i < n; i++) {
35 | Cell c = cells.get(i);
36 | if (c.getIgnore()) continue;
37 | Component component = (Component)c.getWidget();
38 | component.setLocation((int)c.getWidgetX(), (int)c.getWidgetY());
39 | component.setSize((int)c.getWidgetWidth(), (int)c.getWidgetHeight());
40 | }
41 |
42 | if (getDebug() != Debug.none) SwingToolkit.startDebugTimer();
43 | }
44 |
45 | public void invalidate () {
46 | super.invalidate();
47 | if (getTable().isValid()) getTable().invalidate();
48 | }
49 |
50 | public void invalidateHierarchy () {
51 | if (getTable().isValid()) getTable().invalidate();
52 | }
53 |
54 | void drawDebug () {
55 | Graphics2D g = (Graphics2D)getTable().getGraphics();
56 | if (g == null) return;
57 | g.setColor(Color.red);
58 | for (DebugRect rect : debugRects) {
59 | if (rect.type == Debug.cell) g.setColor(Color.red);
60 | if (rect.type == Debug.widget) g.setColor(Color.green);
61 | if (rect.type == Debug.table) g.setColor(Color.blue);
62 | g.drawRect(rect.x, rect.y, rect.width, rect.height);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
| | | | | | |