aboutsummaryrefslogtreecommitdiffstats
path: root/src/graphui
diff options
context:
space:
mode:
authorSven Göthel <[email protected]>2024-01-19 00:30:04 +0100
committerSven Göthel <[email protected]>2024-01-19 00:30:04 +0100
commit30369089cd02e9a0a4875c2a5f5958bcf497c701 (patch)
tree3482d60d2d4e43f4d7aba07f89518ca95c1ca11d /src/graphui
parentf6ae0ff2e4bad67c929a53d705af02e7d92368bc (diff)
GraphUI Tooltip*: Generalize Tooltip base (more versatile) and add TooltipShape supporting general Shapes to be added
Diffstat (limited to 'src/graphui')
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Scene.java19
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Shape.java12
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Tooltip.java79
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/TooltipShape.java125
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/TooltipText.java46
5 files changed, 240 insertions, 41 deletions
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/Scene.java
index 3541473ea..698aa2d2f 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Scene.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Scene.java
@@ -127,7 +127,7 @@ public final class Scene implements Container, GLEventListener {
private static final boolean DEBUG = false;
private final List<Shape> shapes = new CopyOnWriteArrayList<Shape>();
- private final AtomicReference<Tooltip> toolTipStarted = new AtomicReference<Tooltip>();
+ private final AtomicReference<Tooltip> toolTipActive = new AtomicReference<Tooltip>();
private final AtomicReference<Shape> toolTipHUD = new AtomicReference<Shape>();
private boolean doFrustumCulling = false;
@@ -492,10 +492,14 @@ public final class Scene implements Container, GLEventListener {
displayedOnce = true;
syncDisplayedOnce.notifyAll();
}
- final Tooltip tt = toolTipStarted.get();
+ final Tooltip tt = toolTipActive.get();
if( null != tt && null == toolTipHUD.get() ) {
final Shape[] hud = { null };
- if( tt.tick() && forOne(pmv, tt.getTool(), () -> { hud[0] = tt.createTip(Scene.this, pmv); }) ) {
+ if( tt.tick() && forOne(pmv, tt.getTool(), () -> {
+ final AABBox toolMvBounds = tt.getToolMvBounds(pmv);
+ hud[0] = tt.createTip(drawable, Scene.this, pmv, toolMvBounds);
+ }) )
+ {
setToolTip( hud[0] );
}
}
@@ -1190,7 +1194,7 @@ public final class Scene implements Container, GLEventListener {
final Shape s = dispatchMouseEventPickShape(e, glWinX, glWinY);
if( null != s ) {
mouseOver = true;
- toolTipStarted.set( s.startToolTip() );
+ toolTipActive.set( s.startToolTip() );
} else {
mouseOver = false;
}
@@ -1221,20 +1225,21 @@ public final class Scene implements Container, GLEventListener {
}
private void setToolTip(final Shape hud) {
- toolTipStarted.set( null );
addShape( hud );
toolTipHUD.set( hud );
}
private void clearToolTip() {
- final Tooltip tt = toolTipStarted.getAndSet(null);
+ final Tooltip tt = toolTipActive.getAndSet(null);
if( null != tt ) {
tt.stop();
}
final Shape s = toolTipHUD.getAndSet(null);
if( null != s ) {
invoke(false, (final GLAutoDrawable drawable) -> {
- removeShape(drawable.getGL().getGL2ES2(), s);
+ if( s == removeShape(s) ) {
+ tt.destroyTip(drawable.getGL().getGL2ES2(), renderer, s);
+ }
return true;
});
}
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Shape.java b/src/graphui/classes/com/jogamp/graph/ui/Shape.java
index b48163753..672aab9a7 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Shape.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Shape.java
@@ -1418,14 +1418,20 @@ public abstract class Shape {
return position.z() * getScale().z() + zOffset;
}
- /** Set's a new {@link Tooltip} for this shape. */
+ /**
+ * Set's a new {@link Tooltip} for this shape.
+ * <p>
+ * The {@link Shape} must be set {@link #setInteractive(boolean) interactive}
+ * to receive the mouse-over signal, i.e. being picked.
+ * </p>
+ */
public Tooltip setToolTip(final Tooltip newTooltip) {
final Tooltip oldTT = this.tooltip;
this.tooltip = null;
if( null != oldTT ) {
oldTT.stop();
}
- newTooltip.setToolOwner(this);
+ newTooltip.setTool(this);
this.tooltip = newTooltip;
return newTooltip;
}
@@ -1434,7 +1440,7 @@ public abstract class Shape {
tooltip = null;
if( null != tt ) {
tt.stop();
- tt.setToolOwner(null);
+ tt.setTool(null);
}
}
private void stopToolTip() {
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Tooltip.java b/src/graphui/classes/com/jogamp/graph/ui/Tooltip.java
index b8e2467fe..7bed984f5 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Tooltip.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Tooltip.java
@@ -28,7 +28,15 @@
package com.jogamp.graph.ui;
import com.jogamp.common.os.Clock;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.math.Vec2f;
+import com.jogamp.math.Vec4f;
+import com.jogamp.math.geom.AABBox;
import com.jogamp.math.util.PMVMatrix4f;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAutoDrawable;
/** A HUD tooltip for {@link Shape}, see {@link Shape#setToolTip(Tooltip)}. */
public abstract class Tooltip {
@@ -41,13 +49,30 @@ public abstract class Tooltip {
private volatile long delayT1;
/** Shape 'tool' owning this tooltip. */
private Shape tool;
+ protected final int renderModes;
+ protected final Vec4f backColor = new Vec4f(1, 1, 0, 1);
+ protected final Vec4f frontColor = new Vec4f(0.1f, 0.1f, 0.1f, 1);
- protected Tooltip(final long delayMS) {
+ /**
+ *
+ * @param backColor optional HUD tip background color
+ * @param frontColor optional HUD tip front color
+ * @param delayMS delay until HUD tip is visible after timer start (mouse moved)
+ * @param renderModes Graph's {@link Region} render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}.
+ */
+ protected Tooltip(final Vec4f backColor, final Vec4f frontColor, final long delayMS, final int renderModes) {
this.delayMS = delayMS;
this.delayT1 = 0;
this.tool = null;
+ this.renderModes = renderModes;
+ if( null != backColor ) {
+ this.backColor.set(backColor);
+ }
+ if( null != frontColor ) {
+ this.frontColor.set(frontColor);
+ }
}
- /* pp */ final void setToolOwner(final Shape owner) { tool = owner; }
+ /* pp */ final void setTool(final Shape tool) { this.tool = tool; }
/** Returns {@link Shape} 'tool' owning this tooltip, set after {@link Shape#setToolTip(Tooltip)}. */
public final Shape getTool() {
@@ -79,12 +104,60 @@ public abstract class Tooltip {
return true;
}
+ /** Little helper for {@link #createTip(GLAutoDrawable, Scene, PMVMatrix4f, AABBox)} returning the Mv {@link AABBox} of the tool within {@link Scene} Mv space. */
+ public AABBox getToolMvBounds(final PMVMatrix4f pmv) {
+ return getTool().getBounds().transform(pmv.getMv(), new AABBox());
+ }
+ /** Little helper for {@link #createTip(GLAutoDrawable, Scene, PMVMatrix4f, AABBox)} returning the Mv position of the tip within {@link Scene} Mv space. */
+ public Vec2f getTipMvPosition(final Scene scene, final PMVMatrix4f pmv, final float tipWidth, final float tipHeight) {
+ return getTipMvPosition(scene, getToolMvBounds(pmv), tipWidth, tipHeight);
+ }
+ /** Little helper for {@link #createTip(GLAutoDrawable, Scene, PMVMatrix4f, AABBox)} returning the Mv position of the tip within {@link Scene} Mv space. */
+ public Vec2f getTipMvPosition(final Scene scene, final AABBox toolMvBounds, final float tipWidth, final float tipHeight) {
+ final AABBox sceneAABox = scene.getBounds();
+ final Vec2f pos = new Vec2f();
+ if( toolMvBounds.getCenter().x() - tipWidth/2 >= sceneAABox.getLow().x() ) {
+ pos.setX( toolMvBounds.getCenter().x()-tipWidth/2 );
+ } else {
+ pos.setX( sceneAABox.getLow().x() );
+ }
+ if( toolMvBounds.getHigh().y() + tipHeight <= sceneAABox.getHigh().y() ) {
+ pos.setY( toolMvBounds.getHigh().y() );
+ } else if( toolMvBounds.getHigh().y() >= tipHeight ) {
+ pos.setY( toolMvBounds.getHigh().y() - tipHeight );
+ } else {
+ pos.setY( sceneAABox.getHigh().y() - tipHeight );
+ }
+ return pos;
+ }
+
/**
* Create a new HUD tip shape, usually called by {@link Scene}
+ * @param drawable current {@link GLAutoDrawable}
* @param scene the {@link Scene} caller for which this HUD tip shape is created
* @param pmv {@link PMVMatrix4f}, which shall be properly initialized, e.g. via {@link Scene#setupMatrix(PMVMatrix4f)}
+ * @param toolMvBounds TODO
* @return newly created HUD tip shape
+ * @see #destroyTip(GL2ES2, RegionRenderer, Shape)
*/
- public abstract Shape createTip(final Scene scene, final PMVMatrix4f pmv);
+ public abstract Shape createTip(final GLAutoDrawable drawable, final Scene scene, final PMVMatrix4f pmv, AABBox toolMvBounds);
+
+ /**
+ * Destroy a {@link #createTip(GLAutoDrawable, Scene, PMVMatrix4f, AABBox) created} HUD tip.
+ * <p>
+ * Called after {@link Scene#removeShape(Shape)}, allowing implementation to perform certain
+ * resource cleanup tasks. Even keeping the {@link Shape} tip alive is possible.
+ * </p>
+ * <p>
+ * This default implementation simply calls {@link Shape#destroy(GL2ES2, RegionRenderer)}.
+ * </p>
+ * @param gl
+ * @param renderer
+ * @param tip
+ * @see #createTip(GLAutoDrawable, Scene, PMVMatrix4f, AABBox)
+ */
+ public void destroyTip(final GL2ES2 gl, final RegionRenderer renderer, final Shape tip) {
+ tip.destroy(gl, renderer);
+ }
} \ No newline at end of file
diff --git a/src/graphui/classes/com/jogamp/graph/ui/TooltipShape.java b/src/graphui/classes/com/jogamp/graph/ui/TooltipShape.java
new file mode 100644
index 000000000..ec3dbf45f
--- /dev/null
+++ b/src/graphui/classes/com/jogamp/graph/ui/TooltipShape.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright 2023-2024 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;
+
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.ui.layout.Alignment;
+import com.jogamp.graph.ui.layout.BoxLayout;
+import com.jogamp.graph.ui.layout.GridLayout;
+import com.jogamp.graph.ui.shapes.Rectangle;
+import com.jogamp.math.Vec2f;
+import com.jogamp.math.Vec4f;
+import com.jogamp.math.geom.AABBox;
+import com.jogamp.math.util.PMVMatrix4f;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAutoDrawable;
+
+/** A HUD {@link Shape} {@link Tooltip} for {@link Shape}, see {@link Shape#setToolTip(Tooltip)}. */
+public class TooltipShape extends Tooltip {
+ /**
+ * Optional HUD tip destroy callback for the user provided {@link Shape}, see {@link #Tooltip#destroyTip(GL2ES2, RegionRenderer, Shape)}.
+ * <p>
+ * In case no callback is being set via {@link TooltipShape#TooltipShape(Vec2f, long, Shape, DestroyCallback)}
+ * {@link #Tooltip#destroyTip(GL2ES2, RegionRenderer, Shape)} destroys the shape.
+ * Otherwise the callback gets invoked.
+ * </p>
+ * @param gl
+ * @param renderer
+ * @param tip
+ * @see TooltipShape#TooltipShape(Vec2f, long, Shape, DestroyCallback)
+ * @see TooltipShape#createTip(GLAutoDrawable, Scene, PMVMatrix4f, AABBox)
+ */
+ public static interface DestroyCallback {
+ public void destroyTip(final GL2ES2 gl, final RegionRenderer renderer, final Shape tip);
+ }
+
+ /** Text of this tooltip */
+ private final Shape tip;
+ private final Vec2f scale;
+ private final float borderThickness;
+ private final DestroyCallback dtorCallback;
+
+ /**
+ * Ctor of {@link TooltipShape}.
+ * <p>
+ * The {@link Shape} is destroyed via {@link #destroyTip(GL2ES2, RegionRenderer, Shape)},
+ * since no {@link DestroyCallback} is being provided via {@link TooltipShape#TooltipShape(Vec2f, long, Shape, DestroyCallback)}.
+ * </p>
+ * @param scale HUD tip scale for the tip shape
+ * @param delayMS delay until HUD tip is visible after timer start (mouse moved)
+ * @param tip HUD tip shape
+ */
+ public TooltipShape(final Vec2f scale, final long delayMS, final int renderModes, final Shape tip) {
+ this(null, null, 0, scale, delayMS, renderModes, tip, null);
+ }
+
+ /**
+ * Ctor of {@link TooltipShape}.
+ * <p>
+ * The {@link Shape} is not destroyed via {@link #destroyTip(GL2ES2, RegionRenderer, Shape)},
+ * if {@link DestroyCallback} {@code dtor} is not {@code null}, otherwise it is destroyed.
+ * </p>
+ * @param scale HUD tip scale for the tip shape
+ * @param delayMS delay until HUD tip is visible after timer start (mouse moved)
+ * @param tip HUD tip shape
+ * @param dtor
+ */
+ public TooltipShape(final Vec4f backColor, final Vec4f borderColor, final float borderThickness,
+ final Vec2f scale, final long delayMS, final int renderModes, final Shape tip, final DestroyCallback dtor) {
+ super(backColor, borderColor, delayMS, renderModes);
+ this.tip = tip;
+ this.scale = scale;
+ this.borderThickness = borderThickness;
+ this.dtorCallback = dtor;
+ }
+
+ @Override
+ public Shape createTip(final GLAutoDrawable drawable, final Scene scene, final PMVMatrix4f pmv, final AABBox toolMvBounds) {
+ final float zEps = scene.getZEpsilon(16);
+
+ final float w = toolMvBounds.getWidth()*scale.x();
+ final float h = toolMvBounds.getHeight()*scale.y();
+
+ final Group g = new Group(new BoxLayout(w, h, Alignment.FillCenter));
+ g.addShape(new Rectangle(renderModes, 1*w/h, 1, 0).setColor(backColor).setBorder(borderThickness).setBorderColor(frontColor));
+ g.addShape(tip.move(0, 0, zEps)); // above back
+ g.setInteractive(false);
+
+ final Vec2f pos = getTipMvPosition(scene, toolMvBounds, w, h);
+ g.moveTo(pos.x(), pos.y(), 100*zEps);
+ return g;
+ }
+ @Override
+ public void destroyTip(final GL2ES2 gl, final RegionRenderer renderer, final Shape tip) {
+ if( null != dtorCallback ) {
+ dtorCallback.destroyTip(gl, renderer, tip);
+ } else {
+ super.destroyTip(gl, renderer, tip);
+ }
+ }
+}
diff --git a/src/graphui/classes/com/jogamp/graph/ui/TooltipText.java b/src/graphui/classes/com/jogamp/graph/ui/TooltipText.java
index b421604c9..77386d224 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/TooltipText.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/TooltipText.java
@@ -31,10 +31,12 @@ import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.GLRegion;
import com.jogamp.graph.font.Font;
import com.jogamp.graph.ui.shapes.Button;
+import com.jogamp.math.Vec2f;
import com.jogamp.math.Vec4f;
import com.jogamp.math.geom.AABBox;
import com.jogamp.math.geom.plane.AffineTransform;
import com.jogamp.math.util.PMVMatrix4f;
+import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLProfile;
/** A HUD text {@link Tooltip} for {@link Shape}, see {@link Shape#setToolTip(Tooltip)}. */
@@ -44,9 +46,6 @@ public class TooltipText extends Tooltip {
/** Font of this tooltip */
private final Font tipFont;
private final float scaleY;
- private final int renderModes;
- private final Vec4f backColor = new Vec4f();
- private final Vec4f labelColor = new Vec4f();
/**
* Ctor of {@link TooltipText}.
@@ -61,13 +60,10 @@ public class TooltipText extends Tooltip {
public TooltipText(final CharSequence tipText, final Font tipFont, final Vec4f backColor, final Vec4f labelColor,
final float scaleY, final long delayMS, final int renderModes)
{
- super(delayMS);
+ super(backColor, labelColor, delayMS, renderModes);
this.tipText = tipText;
this.tipFont = tipFont;
this.scaleY = scaleY;
- this.renderModes = renderModes;
- this.backColor.set(backColor);
- this.labelColor.set(labelColor);
}
/**
* Ctor of {@link TooltipText} using {@link Tooltip#DEFAULT_DELAY}, {@link Region#VBAA_RENDERING_BIT}
@@ -78,43 +74,37 @@ public class TooltipText extends Tooltip {
* @param tool the tool shape for this tip
*/
public TooltipText(final CharSequence tipText, final Font tipFont, final float scaleY) {
- this(tipText, tipFont, new Vec4f(1, 1, 0, 0.80f), new Vec4f(0.1f, 0.1f, 0.1f, 1), scaleY, Tooltip.DEFAULT_DELAY, Region.VBAA_RENDERING_BIT);
+ this(tipText, tipFont, null, null, scaleY, Tooltip.DEFAULT_DELAY, Region.VBAA_RENDERING_BIT);
}
@Override
- public Shape createTip(final Scene scene, final PMVMatrix4f pmv) {
+ public Shape createTip(final GLAutoDrawable gl, final Scene scene, final PMVMatrix4f pmv, final AABBox toolMvBounds) {
final float zEps = scene.getZEpsilon(16);
// Precompute text-box size .. guessing pixelSize
+ final AABBox sceneAABox = scene.getBounds();
final AffineTransform tempT1 = new AffineTransform();
final AffineTransform tempT2 = new AffineTransform();
final AABBox tipBox_em = tipFont.getGlyphBounds(tipText, tempT1, tempT2);
- // final AABBox toolAABox = scene.getBounds(new PMVMatrix4f(), tool);
- final AABBox toolAABox = getTool().getBounds().transform(pmv.getMv(), new AABBox());
-
- final float h = toolAABox.getHeight() * scaleY;
- final float w = tipBox_em.getWidth() / tipBox_em.getHeight() * h;
-
- final AABBox sceneAABox = scene.getBounds();
- final float xpos, ypos;
- if( toolAABox.getCenter().x()-w/2 < sceneAABox.getLow().x() ) {
- xpos = sceneAABox.getLow().x();
- } else {
- xpos = toolAABox.getCenter().x()-w/2;
- }
- if( toolAABox.getHigh().y() > sceneAABox.getHigh().y() - h ) {
- ypos = sceneAABox.getHigh().y() - h;
- } else {
- ypos = toolAABox.getHigh().y();
+ float h = toolMvBounds.getHeight() * scaleY;
+ float w = tipBox_em.getWidth() * h / tipBox_em.getHeight();
+ if( w > sceneAABox.getWidth() * 0.9f) {
+ w = sceneAABox.getWidth() * 0.9f;
+ h = tipBox_em.getHeight() * w / tipBox_em.getWidth();
+ } else if( h > sceneAABox.getHeight() * 0.9f) {
+ h = sceneAABox.getHeight() * 0.9f;
+ w = tipBox_em.getWidth() * h / tipBox_em.getHeight();
}
+ final Vec2f pos = getTipMvPosition(scene, toolMvBounds, w, h);
+
final Button ntip = (Button) new Button(renderModes, tipFont, tipText, w, h, zEps)
.setPerp()
- .moveTo(xpos, ypos, 100*zEps)
+ .moveTo(pos.x(), pos.y(), 100*zEps)
.setColor(backColor)
// .setBorder(0.05f).setBorderColor(0, 0, 0, 1)
.setInteractive(false);
- ntip.setLabelColor(labelColor);
+ ntip.setLabelColor(frontColor);
ntip.setSpacing(0.10f, 0.10f);
return ntip;
}