summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo12.java225
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/layout/BoxLayout.java122
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/layout/Margin.java197
3 files changed, 544 insertions, 0 deletions
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo12.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo12.java
new file mode 100644
index 000000000..5e3f1031f
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo12.java
@@ -0,0 +1,225 @@
+/**
+ * Copyright 2010-2023 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.demos.graph.ui;
+
+import java.io.IOException;
+
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.ui.Group;
+import com.jogamp.graph.ui.Scene;
+import com.jogamp.graph.ui.Shape;
+import com.jogamp.graph.ui.layout.BoxLayout;
+import com.jogamp.graph.ui.layout.GridLayout;
+import com.jogamp.graph.ui.layout.Margin;
+import com.jogamp.graph.ui.layout.Padding;
+import com.jogamp.graph.ui.shapes.Button;
+import com.jogamp.graph.ui.shapes.Label;
+import com.jogamp.graph.ui.shapes.Rectangle;
+import com.jogamp.graph.ui.shapes.RoundButton;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.demos.graph.ui.util.GraphUIDemoArgs;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.Animator;
+
+/**
+ * Res independent {@link Shape}s in a {@link Group} using a {@link GridLayout}, contained within a Scene attached to GLWindow.
+ * <p>
+ * Pass '-keep' to main-function to keep running after animation,
+ * then user can test Shape drag-move and drag-resize w/ 1-pointer.
+ * </p>
+ */
+public class UISceneDemo12 {
+ static GraphUIDemoArgs options = new GraphUIDemoArgs(1280, 720, Region.VBAA_RENDERING_BIT);
+
+ public static void main(final String[] args) throws IOException {
+ if( 0 != args.length ) {
+ final int[] idx = { 0 };
+ for (idx[0] = 0; idx[0] < args.length; ++idx[0]) {
+ if( options.parse(args, idx) ) {
+ continue;
+ }
+ }
+ }
+ System.err.println(options);
+
+ final GLProfile reqGLP = GLProfile.get(options.glProfileName);
+ System.err.println("GLProfile: "+reqGLP);
+
+ final Animator animator = new Animator();
+
+ final GLCapabilities caps = new GLCapabilities(reqGLP);
+ caps.setAlphaBits(4);
+ if( options.sceneMSAASamples > 0 ) {
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(options.sceneMSAASamples);
+ }
+ System.out.println("Requested: " + caps);
+
+ final GLWindow window = GLWindow.create(caps);
+ window.setSize(options.surface_width, options.surface_height);
+ window.setTitle(UISceneDemo12.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ window.setVisible(true);
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowResized(final WindowEvent e) {
+ window.setTitle(UISceneDemo12.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ }
+ @Override
+ public void windowDestroyNotify(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+
+ animator.setUpdateFPSFrames(1*60, null); // System.err);
+ animator.add(window);
+ animator.start();
+
+ final Scene scene = new Scene();
+ scene.setClearParams(new float[] { 1f, 1f, 1f, 1f}, GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ scene.setFrustumCullingEnabled(true);
+ scene.attachInputListenerTo(window);
+ window.addGLEventListener(scene);
+ scene.waitUntilDisplayed();
+
+ //
+ // Resolution independent, no screen size
+ //
+ final Font font = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_SERIF);
+ System.err.println("Font: "+font.getFullFamilyName());
+
+ final AABBox sceneBox = scene.getBounds();
+ System.err.println("SceneBox "+sceneBox);
+
+ final float sxy = 1/8f;
+ float nextPos = 0;
+
+ final Group groupA0 = new Group(new BoxLayout( new Padding(0.15f, 0.15f) ) );
+ {
+ groupA0.addShape( new RoundButton(options.renderModes, 0.70f, 0.70f).setCorner(0f).setInteractive(false).setColor(0, 1, 0, 1) );
+ groupA0.addShape( new Button(options.renderModes, font, "stack1", 0.50f, 0.50f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupA0.addShape( new Label(options.renderModes, font, 0.70f/4f, "pajq").setDragAndResizeable(false).setColor(0, 0, 1, 1) );
+ }
+ groupA0.setInteractive(true);
+ groupA0.scale(sxy, sxy, 1);
+ groupA0.moveTo(sceneBox.getLow()).move(nextPos, 0, 0);
+ groupA0.validate(reqGLP);
+ System.err.println("Group-A0 "+groupA0);
+ System.err.println("Group-A0 Layout "+groupA0.getLayout());
+ groupA0.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ scene.addShape(groupA0);
+ scene.addShape( new Rectangle(options.renderModes, 1f, 1f, 0.01f).scale(sxy, sxy, 1).moveTo(sceneBox.getLow()).move(nextPos, 0, 0).setInteractive(false).setColor(0, 0, 0, 1) );
+ nextPos = groupA0.getScaledWidth() * 1.5f;
+
+ final Group groupA1 = new Group(new BoxLayout( 1f, 1f, new Margin(0.05f, 0.05f), new Padding(0.10f, 0.10f) ) );
+ {
+ groupA1.addShape( new RoundButton(options.renderModes, 0.70f, 0.70f).setCorner(0f).setInteractive(false).setColor(0, 1, 0, 1) );
+ groupA1.addShape( new Button(options.renderModes, font, "stack2", 0.50f, 0.50f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupA1.addShape( new Label(options.renderModes, font, 0.70f/4f, "pajq").setDragAndResizeable(false).setColor(0, 0, 1, 1) );
+ }
+ groupA1.setInteractive(true);
+ groupA1.scale(sxy, sxy, 1);
+ groupA1.moveTo(sceneBox.getLow()).move(nextPos, 0, 0);
+ groupA1.validate(reqGLP);
+ System.err.println("Group-A1 "+groupA1);
+ System.err.println("Group-A1 Layout "+groupA1.getLayout());
+ groupA1.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ scene.addShape(groupA1);
+ scene.addShape( new Rectangle(options.renderModes, 1f, 1f, 0.01f).scale(sxy, sxy, 1).moveTo(sceneBox.getLow()).move(nextPos, 0, 0).setInteractive(false).setColor(0, 0, 0, 1) );
+ nextPos += groupA1.getScaledWidth() * 1.5f;
+
+ final Group groupA2 = new Group(new BoxLayout( 1f, 1f, new Margin(0.10f, Margin.CENTER), new Padding(0.05f, 0) ) );
+ {
+ groupA2.addShape( new RoundButton(options.renderModes, 0.70f, 0.70f).setCorner(0f).setInteractive(false).setColor(0, 1, 0, 1) );
+ groupA2.addShape( new Button(options.renderModes, font, "stack3", 0.50f, 0.50f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupA2.addShape( new Label(options.renderModes, font, 0.70f/4f, "pajq").setDragAndResizeable(false).setColor(0, 0, 1, 1) );
+ }
+ groupA2.setInteractive(true);
+ groupA2.scale(sxy, sxy, 1);
+ groupA2.moveTo(sceneBox.getLow()).move(nextPos, 0, 0);
+ groupA2.validate(reqGLP);
+ System.err.println("Group-A2 "+groupA2);
+ System.err.println("Group-A2 Layout "+groupA2.getLayout());
+ groupA2.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ scene.addShape(groupA2);
+ scene.addShape( new Rectangle(options.renderModes, 1f, 1f, 0.01f).scale(sxy, sxy, 1).moveTo(sceneBox.getLow()).move(nextPos, 0, 0).setInteractive(false).setColor(0, 0, 0, 1) );
+ nextPos += groupA2.getScaledWidth() * 1.5f;
+
+ final Group groupA3 = new Group(new BoxLayout( 1f, 1f, new Margin(Margin.CENTER) ) );
+ {
+ groupA3.addShape( new RoundButton(options.renderModes, 0.70f, 0.70f).setCorner(0f).setInteractive(false).setColor(0, 1, 0, 1) );
+ groupA3.addShape( new Button(options.renderModes, font, "stack4", 0.50f, 0.50f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupA3.addShape( new Label(options.renderModes, font, 0.70f/4f, "pajq").setDragAndResizeable(false).setColor(0, 0, 1, 1) );
+ }
+ groupA3.setInteractive(true);
+ groupA3.scale(sxy, sxy, 1);
+ groupA3.moveTo(sceneBox.getLow()).move(nextPos, 0, 0);
+ groupA3.validate(reqGLP);
+ System.err.println("Group-A1 "+groupA3);
+ System.err.println("Group-A1 Layout "+groupA3.getLayout());
+ groupA3.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ scene.addShape(groupA3);
+ scene.addShape( new Rectangle(options.renderModes, 1f, 1f, 0.01f).scale(sxy, sxy, 1).moveTo(sceneBox.getLow()).move(nextPos, 0, 0).setInteractive(false).setColor(0, 0, 0, 1) );
+ nextPos += groupA3.getScaledWidth() * 1.5f;
+
+ //
+ //
+ //
+ nextPos = 0;
+
+ final Group groupB0 = new Group(new GridLayout(2, 1f, 1/2f, new Padding(0.05f, 0.05f)));
+ {
+ groupB0.addShape( new Button(options.renderModes, font, "r1 c1", 1f, 1f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupB0.addShape( new Button(options.renderModes, font, "r1 c2", 1f, 1f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupB0.addShape( new Button(options.renderModes, font, "r2 c1", 1f, 1f/2f).setCorner(0f).setDragAndResizeable(false) );
+ groupB0.addShape( new Button(options.renderModes, font, "r2 c2", 1f, 1f/2f).setCorner(0f).setDragAndResizeable(false) );
+ }
+ groupB0.setInteractive(true);
+ groupB0.scale(sxy, sxy, 1);
+ groupB0.moveTo(sceneBox.getLow()).move(nextPos, sceneBox.getHeight()/2f, 0);
+ groupB0.validate(reqGLP);
+ System.err.println("Group-B0 "+groupB0);
+ System.err.println("Group-B0 Layout "+groupB0.getLayout());
+ groupB0.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ scene.addShape(groupB0);
+ nextPos = groupB0.getScaledWidth() * 1.5f;
+
+ try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
+ if( !options.stayOpen ) {
+ window.destroy();
+ }
+ }
+}
diff --git a/src/graphui/classes/com/jogamp/graph/ui/layout/BoxLayout.java b/src/graphui/classes/com/jogamp/graph/ui/layout/BoxLayout.java
new file mode 100644
index 000000000..6f6e610f6
--- /dev/null
+++ b/src/graphui/classes/com/jogamp/graph/ui/layout/BoxLayout.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2023 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.graph.ui.layout;
+
+import java.util.List;
+
+import com.jogamp.graph.ui.Group;
+import com.jogamp.graph.ui.Shape;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * GraphUI Stack {@link Group.Layout}.
+ * <p>
+ * A stack of {@link Shape}s
+ * - size kept unscaled
+ * - position depends on {@Link Padding} and {@link Margin}
+ * - *cell* size can be set
+ * </p>
+ */
+public class BoxLayout implements Group.Layout {
+ private final float cellWidth, cellHeight;
+ private final Margin margin;
+ private final Padding padding;
+ private final float borderThickness;
+
+ public BoxLayout(final Padding padding) {
+ this(0f, 0f, new Margin(), padding);
+ }
+ public BoxLayout(final float width, final float height, final Margin margin) {
+ this(width, height, margin, new Padding());
+ }
+ public BoxLayout(final float width, final float height, final Margin margin, final Padding padding) {
+ this.cellWidth = Math.max(0f, width);
+ this.cellHeight = Math.max(0f, height);
+ this.margin = margin;
+ this.padding = padding;
+ this.borderThickness = 0f;
+ }
+
+ public Padding getPadding() { return padding; }
+ public Margin getMargin() { return margin; }
+
+ @Override
+ public void layout(final Group g, final AABBox box, final PMVMatrix pmv) {
+ final boolean hasCellWidth = !FloatUtil.isZero(cellWidth);
+ final boolean hasCellHeight = !FloatUtil.isZero(cellHeight);
+ final List<Shape> shapes = g.getShapes();
+ final AABBox sbox = new AABBox();
+ for(int i=0; i < shapes.size(); ++i) {
+ final Shape s = shapes.get(i);
+ pmv.glPushMatrix();
+ s.setTransform(pmv);
+ s.getBounds().transformMv(pmv, sbox);
+ pmv.glPopMatrix();
+
+ // adjust size and position (centered)
+ final float paddedWidth = sbox.getWidth() + padding.width();
+ final float paddedHeight = sbox.getHeight() + padding.height();
+ final float marginedWidth = paddedWidth + margin.width();
+ final float marginedHeight = paddedHeight + margin.height();
+ final float cellWidth2 = hasCellWidth ? cellWidth : marginedWidth;
+ final float cellHeight2 = hasCellHeight ? cellHeight : marginedHeight;
+ final float x, y;
+ if( margin.isCenteredHoriz() || hasCellWidth && sbox.getWidth() + padding.width() + margin.width() > cellWidth2 ) {
+ x = 0;
+ } else {
+ x = margin.left + padding.left;
+ }
+ if( margin.isCenteredVert() || hasCellHeight && sbox.getHeight() + padding.height() + margin.height() > cellHeight2 ) {
+ y = 0;
+ } else {
+ y = margin.bottom + padding.bottom;
+ }
+ float dxh = 0, dyh = 0;
+ if( margin.isCenteredHoriz() ) {
+ dxh += 0.5f * ( cellWidth2 - paddedWidth ); // actual horiz-centered
+ }
+ if( margin.isCenteredVert() ) {
+ dyh += 0.5f * ( cellHeight2 - paddedHeight ); // actual vert-centered
+ }
+ System.err.println("["+i+"].m: "+x+" / "+y+" + "+dxh+" / "+dyh+", p "+paddedWidth+" x "+paddedHeight+", sz "+cellWidth2+" x "+cellHeight2+", box "+box.getWidth()+" x "+box.getHeight());
+ s.moveTo( x + dxh, y + dyh, 0f ); // center the scaled artifact
+ s.move( sbox.getLow().mul(-1f) ); // remove the bottom-left delta
+ box.resize( x + cellWidth2 + padding.right, y + cellHeight2 + padding.top, 0);
+ box.resize( x - padding.left, y - padding.bottom, 0);
+ System.err.println("["+i+"].x: "+x+" / "+y+" + "+dxh+" / "+dyh+" -> "+s.getPosition()+", p "+paddedWidth+" x "+paddedHeight+", sz "+cellWidth2+" x "+cellHeight2+", box "+box.getWidth()+" x "+box.getHeight());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Box[cell["+cellWidth+" x "+cellHeight+"], "+margin+", "+padding+"]";
+ }
+}
+
diff --git a/src/graphui/classes/com/jogamp/graph/ui/layout/Margin.java b/src/graphui/classes/com/jogamp/graph/ui/layout/Margin.java
new file mode 100644
index 000000000..f78c9dea9
--- /dev/null
+++ b/src/graphui/classes/com/jogamp/graph/ui/layout/Margin.java
@@ -0,0 +1,197 @@
+/**
+ * Copyright 2023 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.graph.ui.layout;
+
+import com.jogamp.opengl.math.FloatUtil;
+
+/**
+ * GraphUI CSS property Margin, space between or around elements.
+ *
+ * The CSS margin properties are used to create space around elements, outside of any defined borders.
+ *
+ * {@link Margin#CENTER} is mapped to `zero` while earmarking {@link #isCenteredHoriz()} and {@link #isCenteredVert()}.
+ * The container must be sized via its layout horizontally and/or vertically matching the centered axis, similar to CSS.
+ */
+public class Margin {
+ /** Auto margin value to horizontally and/or vertically center an element within its sized-layout container, value if {@link Float#NaN}. */
+ public static final float CENTER = Float.NaN;
+
+ /** Top value */
+ public final float top;
+ /** Right value */
+ public final float right;
+ /** Bottom value */
+ public final float bottom;
+ /** Left value */
+ public final float left;
+
+ private final int bits;
+ static private final int CENTER_HORIZ = 1 << 0;
+ static private final int CENTER_VERT = 1 << 1;
+ static private int getBits(final float top, final float right, final float bottom, final float left) {
+ int b = 0;
+ if( FloatUtil.isEqual(CENTER, left) && FloatUtil.isEqual(CENTER, right) ) {
+ b |= CENTER_HORIZ;
+ }
+ if( FloatUtil.isEqual(CENTER, top) && FloatUtil.isEqual(CENTER, bottom) ) {
+ b |= CENTER_VERT;
+ }
+ return b;
+ }
+
+ /**
+ * Ctor w/ zero values
+ */
+ public Margin() {
+ top = 0f; right = 0f; bottom = 0f; left = 0f; bits = 0;
+ }
+
+ /**
+ * Ctor
+ * @param top top value
+ * @param right right value
+ * @param bottom bottom value
+ * @param left left value
+ */
+ public Margin(final float top, final float right, final float bottom, final float left) {
+ this.bits = getBits(top, right, bottom, left);
+ if( isCenteredVert() ) {
+ this.top = 0;
+ this.bottom = 0;
+ } else {
+ this.top = top;
+ this.bottom = bottom;
+ }
+ if( isCenteredHoriz() ) {
+ this.right = 0;
+ this.left = 0;
+ } else {
+ this.right = right;
+ this.left = left;
+ }
+ }
+
+ /**
+ * Ctor
+ * @param top top value
+ * @param rl right and left value, use {@link #CENTER} to horizontally center the element in its container
+ * @param bottom bottom value
+ */
+ public Margin(final float top, final float rl, final float bottom) {
+ this.bits = getBits(top, rl, bottom, rl);
+ if( isCenteredVert() ) {
+ this.top = 0;
+ this.bottom = 0;
+ } else {
+ this.top = top;
+ this.bottom = bottom;
+ }
+ if( isCenteredHoriz() ) {
+ this.right = 0;
+ this.left = 0;
+ } else {
+ this.right = rl;
+ this.left = rl;
+ }
+ }
+
+ /**
+ * Ctor
+ * @param tb top and bottom value, use {@link #CENTER} to vertically center the element in its container
+ * @param rl right and left value, use {@link #CENTER} to horizontally center the element in its container
+ */
+ public Margin(final float tb, final float rl) {
+ this.bits = getBits(tb, rl, tb, rl);
+ if( isCenteredVert() ) {
+ this.top = 0;
+ this.bottom = 0;
+ } else {
+ this.top = tb;
+ this.bottom = tb;
+ }
+ if( isCenteredHoriz() ) {
+ this.right = 0;
+ this.left = 0;
+ } else {
+ this.right = rl;
+ this.left = rl;
+ }
+ }
+
+ /**
+ * Ctor
+ * @param trbl top, right, bottom and left value, use {@link #CENTER} to horizontally and vertically center the element in its container.
+ */
+ public Margin(final float trbl) {
+ this.bits = getBits(trbl, trbl, trbl, trbl);
+ if( isCenteredVert() ) {
+ this.top = 0;
+ this.bottom = 0;
+ } else {
+ this.top = trbl;
+ this.bottom = trbl;
+ }
+ if( isCenteredHoriz() ) {
+ this.right = 0;
+ this.left = 0;
+ } else {
+ this.right = trbl;
+ this.left = trbl;
+ }
+ }
+
+ /** Returns true if {@link #left} and {@link #right} is {@link #CENTER}. */
+ public boolean isCenteredHoriz() {
+ return 0 != ( CENTER_HORIZ & bits );
+ }
+
+ /** Returns true if {@link #top} and {@link #bottom} is {@link #CENTER}. */
+ public boolean isCenteredVert() {
+ return 0 != ( CENTER_VERT & bits );
+ }
+
+ /** Returns true if {@link #isCenteredHoriz()} and {@link #isCenteredVert()} is true, i.e. for horizontal and vertical center. */
+ public boolean isCentered() {
+ return 0 != ( ( CENTER_VERT | CENTER_HORIZ ) & bits );
+ }
+
+ /** Return width of horizontal values top + right. Zero if {@link #isCenteredHoriz()}. */
+ public float width() { return left + right; }
+
+ /** Return height of vertical values bottom + top. Zero if {@link #isCenteredVert()}. */
+ public float height() { return bottom + top; }
+
+ public boolean zeroSumWidth() { return FloatUtil.isZero( width() ); };
+
+ public boolean zeroSumHeight() { return FloatUtil.isZero( height() ); };
+
+ public boolean zeroSumSize() { return zeroSumWidth() && zeroSumHeight(); }
+
+ @Override
+ public String toString() { return "Margin[t "+top+", r "+right+", b "+bottom+", l "+left+", ctr[h "+isCenteredHoriz()+", v "+isCenteredVert()+"]]"; }
+}