/** * 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.graph.ui.shapes; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLProfile; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.TextRegionUtil; import com.jogamp.graph.font.Font; import com.jogamp.graph.geom.plane.AffineTransform; import com.jogamp.graph.ui.GraphShape; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Vec2f; import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.math.Vec4f; import com.jogamp.opengl.math.geom.AABBox; import jogamp.graph.ui.shapes.Label0; /** * A GraphUI text labeled {@link BaseButton} {@link GraphShape} *

* GraphUI is GPU based and resolution independent. *

*

* This button is rendered with a round oval shape. * To render it rectangular, {@link #setCorner(float)} to zero. *

*/ public class Button extends BaseButton { /** {@value} */ public static final float DEFAULT_SPACING_X = 0.12f; /** {@value} */ public static final float DEFAULT_SPACING_Y = 0.42f; private static final float DEFAULT_LABEL_ZOFFSET = 0.005f; // 0.05f; private float labelZOffset; private final Label0 label; private float spacingX = DEFAULT_SPACING_X; private float spacingY = DEFAULT_SPACING_Y; private final AffineTransform tempT1 = new AffineTransform(); private final AffineTransform tempT2 = new AffineTransform(); private final AffineTransform tempT3 = new AffineTransform(); public Button(final int renderModes, final Font labelFont, final String labelText, final float width, final float height) { super(renderModes | Region.COLORCHANNEL_RENDERING_BIT, width, height); this.labelZOffset = DEFAULT_LABEL_ZOFFSET; this.label = new Label0(labelFont, labelText, new Vec4f( 1.33f, 1.33f, 1.33f, 1.0f )); // 0.75 * 1.33 = 1.0 setColor(0.75f, 0.75f, 0.75f, 1.0f); setPressedColorMod(0.9f, 0.9f, 0.9f, 0.7f); setToggleOffColorMod(0.65f, 0.65f, 0.65f, 1.0f); setToggleOnColorMod(0.85f, 0.85f, 0.85f, 1.0f); } public Font getFont() { return label.getFont(); } public String getLaben() { return label.getText(); } @Override public void draw(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) { // No need to setup an poly offset for z-fighting, using one region now // Setup poly offset for z-fighting // gl.glEnable(GL.GL_POLYGON_OFFSET_FILL); // gl.glPolygonOffset(0f, 1f); super.draw(gl, renderer, sampleCount); // gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); } @Override protected void addShapeToRegion(final GLProfile glp, final GL2ES2 gl) { final OutlineShape shape = createBaseShape( FloatUtil.isZero(labelZOffset) ? 0f : -labelZOffset ); box.resize(shape.getBounds()); setRotationPivot( box.getCenter() ); // Sum Region buffer size of base-shape + text final int[/*2*/] vertIndexCount = Region.countOutlineShape(shape, new int[2]); TextRegionUtil.countStringRegion(label.getFont(), label.getText(), vertIndexCount); resetGLRegion(glp, gl, null, vertIndexCount[0], vertIndexCount[1]); region.addOutlineShape(shape, null, rgbaColor); // Precompute text-box size .. guessing pixelSize final float lw = box.getWidth() * ( 1f - spacingX ) ; final float lh = box.getHeight() * ( 1f - spacingY ) ; final AABBox lbox0_em = label.getFont().getGlyphBounds(label.getText(), tempT1, tempT2); // final AABBox lbox0_em = label.getFont().getGlyphShapeBounds(null, label.getText(), tempT1, tempT2); final float lsx = lw / lbox0_em.getWidth(); final float lsy = lh / lbox0_em.getHeight(); final float lScale = lsx < lsy ? lsx : lsy; // Setting left-corner transform using text-box in font em-size [0..1] final AABBox lbox1_s = new AABBox(lbox0_em).scale2(lScale); // Center text .. (share same center w/ button) final Vec3f lctr = lbox1_s.getCenter(); final Vec3f ctr = box.getCenter(); final Vec2f ltxy = new Vec2f(ctr.x() - lctr.x(), ctr.y() - lctr.y() ); if( DEBUG_DRAW ) { System.err.println("Button: dim "+width+" x "+height+", spacing "+spacingX+", "+spacingY); System.err.println("Button: net-text "+lw+" x "+lh); System.err.println("Button: shape "+box); System.err.println("Button: text_em "+lbox0_em+" em, "+label.getText()); System.err.println("Button: lscale "+lsx+" x "+lsy+" -> "+lScale); System.err.printf ("Button: text_s %s%n", lbox1_s); System.err.printf ("Button: ltxy %s, %f / %f%n", ltxy, ltxy.x() * lScale, ltxy.y() * lScale); } final AABBox lbox2 = label.addShapeToRegion(lScale, region, ltxy, tempT1, tempT2, tempT3); if( DEBUG_DRAW ) { System.err.printf("Button.X: lbox2 %s%n", lbox2); } } public float getLabelZOffset() { return labelZOffset; } public Button setLabelZOffset(final float v) { labelZOffset = v; markShapeDirty(); return this; } public final float getSpacingX() { return spacingX; } public final float getSpacingY() { return spacingY; } /** * In percent of text label * @param spacingX spacing in percent on X, default is {@link #DEFAULT_SPACING_X} * @param spacingY spacing in percent on Y, default is {@link #DEFAULT_SPACING_Y} */ public final Button setSpacing(final float spacingX, final float spacingY) { if ( spacingX < 0.0f ) { this.spacingX = 0.0f; } else if ( spacingX > 1.0f ) { this.spacingX = 1.0f; } else { this.spacingX = spacingX; } if ( spacingY < 0.0f ) { this.spacingY = 0.0f; } else if ( spacingY > 1.0f ) { this.spacingY = 1.0f; } else { this.spacingY = spacingY; } markShapeDirty(); return this; } public final Vec4f getLabelColor() { return label.getColor(); } public final Button setLabelColor(final float r, final float g, final float b) { label.setColor(r, g, b, 1.0f); markShapeDirty(); return this; } public final Button setFont(final Font labelFont) { if( !label.getFont().equals(labelFont) ) { label.setFont(labelFont); markShapeDirty(); } return this; } public final Button setLabel(final String labelText) { if( !label.getText().equals(labelText) ) { label.setText(labelText); markShapeDirty(); } return this; } public final Button setLabel(final Font labelFont, final String labelText) { if( !label.getText().equals(labelText) || !label.getFont().equals(labelFont) ) { label.setFont(labelFont); label.setText(labelText); markShapeDirty(); } return this; } @Override public String getSubString() { return super.getSubString()+", "+ label + ", " + "spacing["+spacingX+", "+spacingY+"]"; } }