aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-12-18 04:31:37 +0100
committerSven Gothel <[email protected]>2023-12-18 04:31:37 +0100
commit121260d2b7bc4f57a16ca53ed1b08082d7977bbe (patch)
tree2efc10421c93c1ce6187725cdeb38abbee7fc71c
parenta4fc84cba410e7e7082e8fd097cb0185d0aac1a2 (diff)
Bug 805: GraphUI Scene/Shape Pick-Active/Interaction: Pick shall complete traversion for most inner interactive shape; ...
Pick shall complete traversion for most inner interactive shape - Shape::dispatchMouseEvent() is only invoked for interactive shapes, impl. simplified. - Remove 'Scene::dispatchMouseEvent(..)', use 'Scene::dispatchMouseEventPickShape(..)' for given use cases - Scene::dispatchMouseEventForShape(..) used for mouseDragged() only, i.e. using activeShape. +++ This allows a 'group widget' being used, allowing to click on inner shapes like a button.
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Scene.java82
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Shape.java216
2 files changed, 156 insertions, 142 deletions
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/Scene.java
index 55c9ac680..b850fe346 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Scene.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Scene.java
@@ -619,11 +619,10 @@ public final class Scene implements Container, GLEventListener {
* @param glWinX window X coordinate, bottom-left origin
* @param glWinY window Y coordinate, bottom-left origin
* @param objPos storage for found object position in model-space of found {@link Shape}
- * @param shape storage for found {@link Shape} or null
* @param runnable the action to perform if {@link Shape} was found
- * @return picked Shape if any or null as stored in {@code shape}
+ * @return last picked (inner) Shape if any or null
*/
- public Shape pickShape(final PMVMatrix4f pmv, final int glWinX, final int glWinY, final Vec3f objPos, final Shape[] shape, final Runnable runnable) {
+ public Shape pickShape(final PMVMatrix4f pmv, final int glWinX, final int glWinY, final Vec3f objPos, final Shape.Visitor1 visitor) {
setupMatrix(pmv);
final float winZ0 = 0f;
@@ -635,25 +634,39 @@ public final class Scene implements Container, GLEventListener {
*/
final Recti viewport = getViewport();
final Ray ray = new Ray();
- shape[0] = null;
-
+ final Shape[] shape = { null };
+ final int[] shapeIdx = { -1 };
forSortedAll(Shape.ZDescendingComparator, pmv, (final Shape s, final PMVMatrix4f pmv2) -> {
+ shapeIdx[0]++;
final boolean ok = s.isInteractive() && pmv.mapWinToRay(glWinX, glWinY, winZ0, winZ1, viewport, ray);
if( ok ) {
final AABBox sbox = s.getBounds();
if( sbox.intersectsRay(ray) ) {
- // System.err.printf("Pick.0: shape %d, [%d, %d, %f/%f] -> %s%n", i, glWinX, glWinY, winZ0, winZ1, ray);
+ if( DEBUG ) {
+ System.err.printf("Pick.0: shape %d/%s/%s, [%d, %d, %f/%f] -> %s%n", shapeIdx[0], s.getClass().getSimpleName(), s.getName(), glWinX, glWinY, winZ0, winZ1, ray);
+ }
if( null == sbox.getRayIntersection(objPos, ray, FloatUtil.EPSILON, true) ) {
throw new InternalError("Ray "+ray+", box "+sbox);
}
- // System.err.printf("Pick.1: shape %d @ [%f, %f, %f], within %s%n", i, objPos[0], objPos[1], objPos[2], uiShape.getBounds());
- shape[0] = s;
- runnable.run();
- return true;
+ if( visitor.visit(s) ) {
+ if( DEBUG ) {
+ System.err.printf("Pick.S: shape %d/%s/%s @ %s, %s%n", shapeIdx[0], s.getClass().getSimpleName(), s.getName(), objPos, s);
+ }
+ shape[0] = s;
+ } else if( DEBUG ) {
+ System.err.printf("Pick.1: shape %d/%s/%s @ %s, %s%n", shapeIdx[0], s.getClass().getSimpleName(), s.getName(), objPos, s);
+ }
}
}
- return false;
+ return false; // continue traversing for most inner interactive shape
});
+ if( DEBUG ) {
+ if( null != shape[0] ) {
+ System.err.printf("Pick.X: shape %s/%s%n%n", shape[0].getClass().getSimpleName(), shape[0].getName());
+ } else {
+ System.err.printf("Pick.X: shape null%n%n");
+ }
+ }
return shape[0];
}
@@ -999,10 +1012,11 @@ public final class Scene implements Container, GLEventListener {
}
}
private void setActiveShape(final Shape shape) {
- if( activeShape != shape ) {
- releaseActiveShape();
- if( null != shape ) {
- shape.setActive(true, activeZOffsetScale * getZEpsilon(16));
+ if( activeShape != shape && null != shape &&
+ shape.setActive(true, activeZOffsetScale * getZEpsilon(16)) )
+ {
+ if( null != activeShape ) {
+ activeShape.setActive(false, 0);
}
activeShape = shape;
}
@@ -1039,33 +1053,19 @@ public final class Scene implements Container, GLEventListener {
}
/**
- * Dispatch mouse event, either directly sending to activeShape or picking one
- * @param e original Newt {@link MouseEvent}
- * @param glWinX in GL window coordinates, origin bottom-left
- * @param glWinY in GL window coordinates, origin bottom-left
- */
- final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY) {
- if( null == activeShape ) {
- dispatchMouseEventPickShape(e, glWinX, glWinY);
- } else if( activeShape.isInteractive() ) {
- dispatchMouseEventForShape(activeShape, e, glWinX, glWinY);
- }
- }
- /**
* Pick the shape using the event coordinates
* @param e original Newt {@link MouseEvent}
* @param glWinX in GL window coordinates, origin bottom-left
* @param glWinY in GL window coordinates, origin bottom-left
*/
- final boolean dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY) {
+ private final boolean dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY) {
final PMVMatrix4f pmv = new PMVMatrix4f();
final Vec3f objPos = new Vec3f();
- final Shape[] shape = { null };
- if( null != pickShape(pmv, glWinX, glWinY, objPos, shape, () -> {
- shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos);
- } ) )
- {
- setActiveShape(shape[0]);
+ final Shape shape = pickShape(pmv, glWinX, glWinY, objPos, (final Shape s) -> {
+ return s.isInteractive() && ( s.dispatchMouseEvent(e, glWinX, glWinY, objPos) || true );
+ });
+ if( null != shape ) {
+ setActiveShape(shape);
return true;
} else {
releaseActiveShape();
@@ -1079,7 +1079,7 @@ public final class Scene implements Container, GLEventListener {
* @param glWinX in GL window coordinates, origin bottom-left
* @param glWinY in GL window coordinates, origin bottom-left
*/
- final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) {
+ private final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) {
final PMVMatrix4f pmv = new PMVMatrix4f();
final Vec3f objPos = new Vec3f();
winToShapeCoord(shape, glWinX, glWinY, pmv, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); });
@@ -1106,7 +1106,7 @@ public final class Scene implements Container, GLEventListener {
// flip to GL window coordinates, origin bottom-left
final int glWinX = e.getX();
final int glWinY = getHeight() - e.getY() - 1;
- dispatchMouseEvent(e, glWinX, glWinY);
+ dispatchMouseEventPickShape(e, glWinX, glWinY);
}
@Override
@@ -1114,7 +1114,7 @@ public final class Scene implements Container, GLEventListener {
// flip to GL window coordinates, origin bottom-left
final int glWinX = e.getX();
final int glWinY = getHeight() - e.getY() - 1;
- dispatchMouseEvent(e, glWinX, glWinY);
+ dispatchMouseEventPickShape(e, glWinX, glWinY);
if( !mouseOver ) {
if( 1 == e.getPointerCount() ) {
// Release active shape: last pointer has been lifted!
@@ -1130,7 +1130,7 @@ public final class Scene implements Container, GLEventListener {
final int glWinX = e.getX();
final int glWinY = getHeight() - e.getY() - 1;
if( mouseOver ) {
- dispatchMouseEvent(e, glWinX, glWinY);
+ dispatchMouseEventPickShape(e, glWinX, glWinY);
} else {
// activeId should have been released by mouseRelease() already!
dispatchMouseEventPickShape(e, glWinX, glWinY);
@@ -1143,7 +1143,7 @@ public final class Scene implements Container, GLEventListener {
@Override
public void mouseDragged(final MouseEvent e) {
// drag activeShape, if no gesture-activity, only on 1st pointer
- if( null != activeShape && !pinchToZoomGesture.isWithinGesture() && e.getPointerId(0) == lId ) {
+ if( null != activeShape && activeShape.isInteractive() && !pinchToZoomGesture.isWithinGesture() && e.getPointerId(0) == lId ) {
lx = e.getX();
ly = e.getY();
@@ -1160,7 +1160,7 @@ public final class Scene implements Container, GLEventListener {
// flip to GL window coordinates
final int glWinX = lx;
final int glWinY = getHeight() - ly - 1;
- dispatchMouseEvent(e, glWinX, glWinY);
+ dispatchMouseEventPickShape(e, glWinX, glWinY);
}
@Override
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Shape.java b/src/graphui/classes/com/jogamp/graph/ui/Shape.java
index bfea0773f..125bb1a90 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Shape.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Shape.java
@@ -1238,6 +1238,9 @@ public abstract class Shape {
if( isActivable() ) {
this.zOffset = zOffset;
setIO(IO_ACTIVE, v);
+ if( !v ) {
+ releaseInteraction();
+ }
if( DEBUG ) {
System.err.println("XXX "+(v?" Active":"DeActive")+" "+this);
}
@@ -1441,14 +1444,28 @@ public abstract class Shape {
}
}
+ private final void releaseInteraction() {
+ setPressed(false);
+ setIO(IO_IN_MOVE, false);
+ setIO(IO_IN_RESIZE_BR, false);
+ setIO(IO_IN_RESIZE_BL, false);
+ }
+
/**
* Dispatch given NEWT mouse event to this shape
* @param e original Newt {@link MouseEvent}
* @param glWinX in GL window coordinates, origin bottom-left
* @param glWinY in GL window coordinates, origin bottom-left
* @param objPos object position of mouse event relative to this shape
+ * @return true to signal operation complete and to stop traversal, otherwise false
*/
- /* pp */ final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final Vec3f objPos) {
+ /* pp */ final boolean dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final Vec3f objPos) {
+ /**
+ * Checked at caller!
+ if( !isInteractive() ) {
+ return false;
+ } */
+ final boolean resizableOrDraggable = isResizable() || isDraggable();
final Shape.EventInfo shapeEvent = new EventInfo(glWinX, glWinY, this, objPos);
final short eventType = e.getEventType();
@@ -1461,123 +1478,119 @@ public abstract class Shape {
}
break;
case MouseEvent.EVENT_MOUSE_PRESSED:
- setIO(IO_DRAG_FIRST, true);
+ if( resizableOrDraggable ) {
+ setIO(IO_DRAG_FIRST, true);
+ }
setPressed(true);
break;
case MouseEvent.EVENT_MOUSE_RELEASED:
// Release active shape: last pointer has been lifted!
- setPressed(false);
- setIO(IO_IN_MOVE, false);
- setIO(IO_IN_RESIZE_BR, false);
- setIO(IO_IN_RESIZE_BL, false);
+ releaseInteraction();
break;
}
}
- switch( eventType ) {
- case MouseEvent.EVENT_MOUSE_DRAGGED: {
- // adjust for rotation
- final Vec3f euler = rotation.toEuler(new Vec3f());
- final boolean x_flip, y_flip;
- {
- final float x_rot = Math.abs(euler.x());
- final float y_rot = Math.abs(euler.y());
- x_flip = 1f*FloatUtil.HALF_PI <= y_rot && y_rot <= 3f*FloatUtil.HALF_PI;
- y_flip = 1f*FloatUtil.HALF_PI <= x_rot && x_rot <= 3f*FloatUtil.HALF_PI;
- }
- // 1 pointer drag and potential drag-resize
- if( isIO(IO_DRAG_FIRST) ) {
- objDraggedFirst.set(objPos);
- winDraggedLast[0] = glWinX;
- winDraggedLast[1] = glWinY;
- setIO(IO_DRAG_FIRST, false);
-
- final float ix = x_flip ? box.getWidth() - objPos.x() : objPos.x();
- final float iy = y_flip ? box.getHeight() - objPos.y() : objPos.y();
- final float minx_br = box.getMaxX() - box.getWidth() * resize_section;
- final float miny_br = box.getMinY();
- final float maxx_br = box.getMaxX();
- final float maxy_br = box.getMinY() + box.getHeight() * resize_section;
- if( minx_br <= ix && ix <= maxx_br &&
- miny_br <= iy && iy <= maxy_br ) {
- if( isInteractive() && isResizable() ) {
- setIO(IO_IN_RESIZE_BR, true);
+ if( resizableOrDraggable && MouseEvent.EVENT_MOUSE_DRAGGED == eventType ) {
+ // adjust for rotation
+ final Vec3f euler = rotation.toEuler(new Vec3f());
+ final boolean x_flip, y_flip;
+ {
+ final float x_rot = Math.abs(euler.x());
+ final float y_rot = Math.abs(euler.y());
+ x_flip = 1f*FloatUtil.HALF_PI <= y_rot && y_rot <= 3f*FloatUtil.HALF_PI;
+ y_flip = 1f*FloatUtil.HALF_PI <= x_rot && x_rot <= 3f*FloatUtil.HALF_PI;
+ }
+ // 1 pointer drag and potential drag-resize
+ if( isIO(IO_DRAG_FIRST) ) {
+ objDraggedFirst.set(objPos);
+ winDraggedLast[0] = glWinX;
+ winDraggedLast[1] = glWinY;
+ setIO(IO_DRAG_FIRST, false);
+
+ final float ix = x_flip ? box.getWidth() - objPos.x() : objPos.x();
+ final float iy = y_flip ? box.getHeight() - objPos.y() : objPos.y();
+ final float minx_br = box.getMaxX() - box.getWidth() * resize_section;
+ final float miny_br = box.getMinY();
+ final float maxx_br = box.getMaxX();
+ final float maxy_br = box.getMinY() + box.getHeight() * resize_section;
+ if( minx_br <= ix && ix <= maxx_br &&
+ miny_br <= iy && iy <= maxy_br ) {
+ if( isResizable() ) {
+ setIO(IO_IN_RESIZE_BR, true);
+ }
+ } else {
+ final float minx_bl = box.getMinX();
+ final float miny_bl = box.getMinY();
+ final float maxx_bl = box.getMinX() + box.getWidth() * resize_section;
+ final float maxy_bl = box.getMinY() + box.getHeight() * resize_section;
+ if( minx_bl <= ix && ix <= maxx_bl &&
+ miny_bl <= iy && iy <= maxy_bl ) {
+ if( isResizable() ) {
+ setIO(IO_IN_RESIZE_BL, true);
}
} else {
- final float minx_bl = box.getMinX();
- final float miny_bl = box.getMinY();
- final float maxx_bl = box.getMinX() + box.getWidth() * resize_section;
- final float maxy_bl = box.getMinY() + box.getHeight() * resize_section;
- if( minx_bl <= ix && ix <= maxx_bl &&
- miny_bl <= iy && iy <= maxy_bl ) {
- if( isInteractive() && isResizable() ) {
- setIO(IO_IN_RESIZE_BL, true);
- }
- } else {
- setIO(IO_IN_MOVE, isInteractive() && isDraggable());
- }
+ setIO(IO_IN_MOVE, isDraggable());
}
- if( DEBUG ) {
- System.err.printf("DragFirst: drag %b, resize[br %b, bl %b], obj[%s], flip[x %b, y %b]%n",
- isIO(IO_IN_MOVE), isIO(IO_IN_RESIZE_BR), isIO(IO_IN_RESIZE_BL), objPos, x_flip, y_flip);
- System.err.printf("DragFirst: %s%n", this);
- }
- return;
}
- shapeEvent.objDrag.set( objPos.x() - objDraggedFirst.x(),
- objPos.y() - objDraggedFirst.y() );
- shapeEvent.objDrag.scale(x_flip ? -1f : 1f, y_flip ? -1f : 1f);
-
- shapeEvent.winDrag[0] = glWinX - winDraggedLast[0];
- shapeEvent.winDrag[1] = glWinY - winDraggedLast[1];
- winDraggedLast[0] = glWinX;
- winDraggedLast[1] = glWinY;
- if( 1 == e.getPointerCount() ) {
- final float sdx = shapeEvent.objDrag.x() * scale.x(); // apply scale, since operation
- final float sdy = shapeEvent.objDrag.y() * scale.y(); // is from a scaled-model-viewpoint
- if( isIO(IO_IN_RESIZE_BR) || isIO(IO_IN_RESIZE_BL) ) {
- final float bw = box.getWidth();
- final float bh = box.getHeight();
- final float sdy2, sx, sy;
- if( isIO(IO_IN_RESIZE_BR) ) {
- sx = scale.x() + sdx/bw; // bottom-right
- } else {
- sx = scale.x() - sdx/bw; // bottom-left
+ if( DEBUG ) {
+ System.err.printf("DragFirst: drag %b, resize[br %b, bl %b], obj[%s], flip[x %b, y %b]%n",
+ isIO(IO_IN_MOVE), isIO(IO_IN_RESIZE_BR), isIO(IO_IN_RESIZE_BL), objPos, x_flip, y_flip);
+ System.err.printf("DragFirst: %s%n", this);
+ }
+ return true; // end signal traversal at 1st drag
+ }
+ shapeEvent.objDrag.set( objPos.x() - objDraggedFirst.x(),
+ objPos.y() - objDraggedFirst.y() );
+ shapeEvent.objDrag.scale(x_flip ? -1f : 1f, y_flip ? -1f : 1f);
+
+ shapeEvent.winDrag[0] = glWinX - winDraggedLast[0];
+ shapeEvent.winDrag[1] = glWinY - winDraggedLast[1];
+ winDraggedLast[0] = glWinX;
+ winDraggedLast[1] = glWinY;
+ if( 1 == e.getPointerCount() ) {
+ final float sdx = shapeEvent.objDrag.x() * scale.x(); // apply scale, since operation
+ final float sdy = shapeEvent.objDrag.y() * scale.y(); // is from a scaled-model-viewpoint
+ if( isIO(IO_IN_RESIZE_BR) || isIO(IO_IN_RESIZE_BL) ) {
+ final float bw = box.getWidth();
+ final float bh = box.getHeight();
+ final float sdy2, sx, sy;
+ if( isIO(IO_IN_RESIZE_BR) ) {
+ sx = scale.x() + sdx/bw; // bottom-right
+ } else {
+ sx = scale.x() - sdx/bw; // bottom-left
+ }
+ if( isFixedARatioResize() ) {
+ sy = sx;
+ sdy2 = bh * ( scale.y() - sy );
+ } else {
+ sdy2 = sdy;
+ sy = scale.y() - sdy2/bh;
+ }
+ if( resize_sxy_min <= sx && resize_sxy_min <= sy ) { // avoid scale flip
+ if( DEBUG ) {
+ System.err.printf("DragZoom: resize[br %b, bl %b], win[%4d, %4d], , flip[x %b, y %b], obj[%s], dxy +[%s], sdxy +[%.4f, %.4f], sdxy2 +[%.4f, %.4f], scale [%s] -> [%.4f, %.4f]%n",
+ isIO(IO_IN_RESIZE_BR), isIO(IO_IN_RESIZE_BL), glWinX, glWinY, x_flip, y_flip, objPos,
+ shapeEvent.objDrag, sdx, sdy, sdx, sdy2,
+ scale, sx, sy);
}
- if( isFixedARatioResize() ) {
- sy = sx;
- sdy2 = bh * ( scale.y() - sy );
+ if( isIO(IO_IN_RESIZE_BR) ) {
+ move( 0, sdy2, 0f); // bottom-right, sticky left- and top-edge
} else {
- sdy2 = sdy;
- sy = scale.y() - sdy2/bh;
+ move( sdx, sdy2, 0f); // bottom-left, sticky right- and top-edge
}
- if( resize_sxy_min <= sx && resize_sxy_min <= sy ) { // avoid scale flip
- if( DEBUG ) {
- System.err.printf("DragZoom: resize[br %b, bl %b], win[%4d, %4d], , flip[x %b, y %b], obj[%s], dxy +[%s], sdxy +[%.4f, %.4f], sdxy2 +[%.4f, %.4f], scale [%s] -> [%.4f, %.4f]%n",
- isIO(IO_IN_RESIZE_BR), isIO(IO_IN_RESIZE_BL), glWinX, glWinY, x_flip, y_flip, objPos,
- shapeEvent.objDrag, sdx, sdy, sdx, sdy2,
- scale, sx, sy);
- }
- if( isIO(IO_IN_RESIZE_BR) ) {
- move( 0, sdy2, 0f); // bottom-right, sticky left- and top-edge
- } else {
- move( sdx, sdy2, 0f); // bottom-left, sticky right- and top-edge
- }
- setScale(sx, sy, scale.z());
- }
- return; // FIXME: pass through event? Issue zoom event?
- } else if( isIO(IO_IN_MOVE) ) {
- if( DEBUG ) {
- System.err.printf("DragMove: win[%4d, %4d] +[%2d, %2d], , flip[x %b, y %b], obj[%s] +[%s], rot %s%n",
- glWinX, glWinY, shapeEvent.winDrag[0], shapeEvent.winDrag[1],
- x_flip, y_flip, objPos, shapeEvent.objDrag, euler);
- }
- move( sdx, sdy, 0f);
- // FIXME: Pass through event? Issue move event?
+ setScale(sx, sy, scale.z());
+ }
+ return true; // end signal traversal with completed drag
+ } else if( isIO(IO_IN_MOVE) ) {
+ if( DEBUG ) {
+ System.err.printf("DragMove: win[%4d, %4d] +[%2d, %2d], , flip[x %b, y %b], obj[%s] +[%s], rot %s%n",
+ glWinX, glWinY, shapeEvent.winDrag[0], shapeEvent.winDrag[1],
+ x_flip, y_flip, objPos, shapeEvent.objDrag, euler);
}
+ move( sdx, sdy, 0f);
+ return true; // end signal traversal with completed move
}
}
- break;
- }
+ } // resizableOrDraggable && EVENT_MOUSE_DRAGGED
e.setAttachment(shapeEvent);
for(int i = 0; !e.isConsumed() && i < mouseListeners.size(); i++ ) {
@@ -1611,6 +1624,7 @@ public abstract class Shape {
throw new NativeWindowException("Unexpected mouse event type " + e.getEventType());
}
}
+ return e.isConsumed(); // end signal traversal if consumed
}
/**