From 6a0ac90182efba40b0e6dab8f6390898aced70e8 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Wed, 12 Apr 2023 19:20:24 +0200 Subject: GraphUI GridLayout: Functional Grid Layout w/ Padding, demo'ed in UISceneDemo20 with button Groups All layout magic is simply performed in Group.Layout.layout(..) @ validate, incl. updating the bounding box to have the padding included. This demonstrates GraphUI's capability to be used with correct layout, i.e. its pure matrix based position, scale and rotation. --- src/graphui/classes/com/jogamp/graph/ui/Group.java | 47 ++++--- .../com/jogamp/graph/ui/layout/GridLayout.java | 144 +++++++++++++++++---- .../com/jogamp/graph/ui/layout/Padding.java | 49 +++++++ 3 files changed, 198 insertions(+), 42 deletions(-) create mode 100644 src/graphui/classes/com/jogamp/graph/ui/layout/Padding.java (limited to 'src/graphui') diff --git a/src/graphui/classes/com/jogamp/graph/ui/Group.java b/src/graphui/classes/com/jogamp/graph/ui/Group.java index b3ae5b41d..e349b1302 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/Group.java +++ b/src/graphui/classes/com/jogamp/graph/ui/Group.java @@ -48,10 +48,22 @@ import jogamp.graph.ui.TreeTool; * @see Group.Layout */ public class Group extends Shape implements Container { - /** Layout for the group, called @ {@link Group#validate(GL2ES2)} or {@link Group#validate(GLProfile)}. */ + /** Layout for the GraphUI {@link Group}, called @ {@link Shape#validate(GL2ES2)} or {@link Shape#validate(GLProfile)}. */ public static interface Layout { - /** Performing the layout, called @ {@link Group#validate(GL2ES2)} or {@link Group#validate(GLProfile)}. */ - void layout(Group g); + /** + * Performing the layout of {@link Group#getShapes()}, called @ {@link Shape#validate(GL2ES2)} or {@link Shape#validate(GLProfile)}. + *

+ * According to the implemented layout, method + * - may scale the {@Link Shape}s + * - may move the {@Link Shape}s + * - may reuse the given {@link PMVMatrix} `pmv` + * - must update the given {@link AABBox} `box` + *

+ * @param g the {@link Group} to layout + * @param box the bounding box of {@link Group} to be updated by this method. + * @param pmv a {@link PMVMatrix} which can be reused. + */ + void layout(final Group g, final AABBox box, final PMVMatrix pmv); } private final List shapes = new ArrayList(); @@ -161,12 +173,6 @@ public class Group extends Shape implements Container { } } - private void layout() { - if( null != layouter ) { - layouter.layout(this); - } - } - private boolean doFrustumCulling = false; @Override @@ -200,21 +206,25 @@ public class Group extends Shape implements Container { @Override protected void validateImpl(final GLProfile glp, final GL2ES2 gl) { if( isShapeDirty() ) { - layout(); final PMVMatrix pmv = new PMVMatrix(); final AABBox tmpBox = new AABBox(); for(final Shape s : shapes) { - // s.validateImpl(glp, gl); if( null != gl ) { s.validate(gl); } else { s.validate(glp); } - pmv.glPushMatrix(); - s.setTransform(pmv); - s.getBounds().transformMv(pmv, tmpBox); - pmv.glPopMatrix(); - box.resize(tmpBox); + } + if( null != layouter ) { + layouter.layout(this, box, pmv); + } else { + for(final Shape s : shapes) { + pmv.glPushMatrix(); + s.setTransform(pmv); + s.getBounds().transformMv(pmv, tmpBox); + pmv.glPopMatrix(); + box.resize(tmpBox); + } } } } @@ -248,6 +258,11 @@ public class Group extends Shape implements Container { return res; } + @Override + public String getSubString() { + return super.getSubString()+", shapes "+shapes.size(); + } + @Override public boolean forOne(final PMVMatrix pmv, final Shape shape, final Runnable action) { return TreeTool.forOne(shapes, pmv, shape, action); diff --git a/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java b/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java index bd2fd2d76..7d1739dd6 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java +++ b/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java @@ -1,5 +1,5 @@ /** - * Copyright 2010-2023 JogAmp Community. All rights reserved. + * 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: @@ -27,43 +27,135 @@ */ package com.jogamp.graph.ui.layout; +import java.util.List; + +import com.jogamp.graph.ui.GraphShape; import com.jogamp.graph.ui.Group; import com.jogamp.graph.ui.Shape; -import com.jogamp.graph.ui.Group.Layout; +import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.util.PMVMatrix; +/** + * GraphUI Grid {@link Group.Layout}. + */ public class GridLayout implements Group.Layout { - private final int columns; - private final float padX, padY; + /** Layout order for {@link Group#getShapes()}} after population. */ + public static enum Order { + /** COLUMN layout order of {@link Group#getShapes()}} is left to right and top to bottom. */ + COLUMN, + /** ROW layout order of {@link Group#getShapes()}} is top to bottom and left to right. */ + ROW + } + private final Order order; + private final int col_limit; + private final int row_limit; + private final float cellWidth, cellHeight; + private final Padding padding; + private int row_count, col_count; + + + /** + * Default layout order of {@link Group#getShapes()}} is {@link Order#COLUMN}. + * @param column_limit [1..inf) + * @param cellWidth + * @param cellHeight + */ + public GridLayout(final int column_limit, final float cellWidth, final float cellHeight) { + this(Math.max(1, column_limit), -1, cellWidth, cellHeight, new Padding()); + } + + /** + * Default layout order of {@link Group#getShapes()}} is {@link Order#COLUMN}. + * @param column_limit [1..inf) + * @param cellWidth + * @param cellHeight + * @param padding + */ + public GridLayout(final int column_limit, final float cellWidth, final float cellHeight, final Padding padding) { + this(Math.max(1, column_limit), -1, cellWidth, cellHeight, padding); + } /** - * - * @param columns [1..inf) - * @param padX - * @param padY + * Default layout order of {@link Group#getShapes()}} is {@link Order#ROW}. + * @param cellWidth + * @param cellHeight + * @param padding + * @param row_limit [1..inf) */ - public GridLayout(final int columns, final float padX, final float padY) { - this.columns = Math.max(1, columns); - this.padX = padX; - this.padY = padY; + public GridLayout(final float cellWidth, final float cellHeight, final Padding padding, final int row_limit) { + this(-1, Math.max(1, row_limit), cellWidth, cellHeight, padding); + } + + private GridLayout(final int column_limit, final int row_limit, final float cellWidth, final float cellHeight, final Padding padding) { + this.order = 0 < column_limit ? Order.COLUMN : Order.ROW; + this.col_limit = column_limit; + this.row_limit = row_limit; + this.cellWidth = cellWidth; + this.cellHeight = cellHeight; + this.padding = padding;; + row_count = 0; + col_count = 0; } + public Order getOrder() { return order; } + public int getColumnCount() { return col_count; } + public int getRowCount() { return row_count; } + @Override - public void layout(final Group g) { - int col = 0; - float x=0; - float y=0; - for(final Shape s : g.getShapes()) { - s.moveTo(x, y, 0); - if( col + 1 == columns ) { - col = 0; - // row++; - x = 0; - y -= padY; - } else { - col++; - x += padX; + public void layout(final Group g, final AABBox box, final PMVMatrix pmv) { + final List shapes = g.getShapes(); + if( Order.COLUMN == order ) { + row_count = (int) Math.ceil( (double)shapes.size() / (double)col_limit ); + col_count = col_limit; + } else { // Order.ROW_MAJOR == order + row_count = row_limit; + col_count = (int) Math.ceil( (double)shapes.size() / (double)row_limit ); + } + int col_i = 0, row_i = 0; + final AABBox sbox = new AABBox(); + for(final Shape s : shapes) { + // measure size + pmv.glPushMatrix(); + s.setTransform(pmv); + s.getBounds().transformMv(pmv, sbox); + pmv.glPopMatrix(); + + // adjust size and position (centered) + final float x = ( ( col_i ) * ( cellWidth + padding.width() ) ) + padding.left; + final float y = ( ( row_count - row_i - 1 ) * ( cellHeight + padding.height() ) ) + padding.bottom; + final float sx = cellWidth / sbox.getWidth(); + final float sy = cellHeight/ sbox.getHeight(); + final float sxy = sx < sy ? sx : sy; + final float dxh = sbox.getWidth() * ( sx - sxy ) * 0.5f; + final float dyh = sbox.getHeight() * ( sy - sxy ) * 0.5f; + s.moveTo( x + dxh, y + dyh, 0f ); + s.scale( sxy, sxy, 1f); + box.resize( x + cellWidth + padding.right, y + cellHeight + padding.top, 0); + box.resize( x - padding.left, y - padding.bottom, 0); + // System.err.println("["+row_i+"]["+col_i+"]: "+x+" / "+y); + + // position for next cell + if( Order.COLUMN == order ) { + if( col_i + 1 == col_limit ) { + col_i = 0; + row_i++; + } else { + col_i++; + } + } else { // Order.ROW_MAJOR == order + if( row_i + 1 == row_limit ) { + row_i = 0; + col_i++; + } else { + row_i++; + } } } } + + @Override + public String toString() { + return "Grid["+row_count+"x"+col_count+", "+order+", cell["+cellWidth+" x "+cellHeight+"], "+padding+"]"; + } } diff --git a/src/graphui/classes/com/jogamp/graph/ui/layout/Padding.java b/src/graphui/classes/com/jogamp/graph/ui/layout/Padding.java new file mode 100644 index 000000000..f4b35e723 --- /dev/null +++ b/src/graphui/classes/com/jogamp/graph/ui/layout/Padding.java @@ -0,0 +1,49 @@ +/** + * 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; + +/** + * GraphUI Padding of cells, e.g. {@link GridLayout}. + */ +public class Padding { + public final float left, right, bottom, top; + + public Padding() { + left = 0f; right = 0f; bottom = 0f; top = 0f; + } + public Padding(final float width, final float height) { + left = width/2f; right = width/2f; bottom = height/2f; top = height/2f; + } + public Padding(final float left, final float right, final float bottom, final float top) { + this.left = left; this.right = right; this.bottom = bottom; this.top = top; + } + public float width() { return left + right; } + public float height() { return bottom + top; } + @Override + public String toString() { return "Padding[l "+left+", r "+right+", b "+bottom+", t "+top+"]"; } +} -- cgit v1.2.3