aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Göthel <[email protected]>2024-01-19 06:11:46 +0100
committerSven Göthel <[email protected]>2024-01-19 06:11:46 +0100
commit95de4407faf91f30ccecb3af1aad4242172a6e0f (patch)
tree555ba6ff89995f0f686b1b3cc9a1b0179bf83e86 /src
parenta516c694031e77c0e94d30f769c66495c4bf72ea (diff)
GraphUI RangeSlider/RangedGroup: Fix overall integration, adding more API comments; UIMediaGrid01 now uses RangedGroup
RangeSlider - Fix pageSize, i.e. allow valid content.getBounds() be pending @ validateImpl() - Clip value [0, maximum - pageSize] - Has to use Float.isFinite() to capture both, NaN and Infinity -- used for pageSize, determining whether slider uses pageSize rect-knob or position round-knob -- used for minMax, val, val_pct and temp range + pageSize_pct -- don't overwrite valid pageSize if leading to !isFinite() - Reuse setKnobSize() for ctor as well, where padding is be done once (FIXME?) - Tested via RangedGroup w/ UIMediaGrid01 and FontView0 -- vertical slider, inverse and !inverse
Diffstat (limited to 'src')
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java23
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UIMediaGrid01.java105
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java333
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java40
4 files changed, 359 insertions, 142 deletions
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java
index bf8d96f9e..9a882a902 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java
@@ -139,7 +139,7 @@ public class FontView01 {
showLabel = true;
} else if(args[idx[0]].equals("-perf")) {
perfanal = true;
- } else if(args[idx[0]].equals("-maxGlyphs")) {
+ } else if(args[idx[0]].equals("-max")) {
idx[0]++;
max_glyph_count = MiscUtils.atoi(args[idx[0]], max_glyph_count);
}
@@ -190,19 +190,20 @@ public class FontView01 {
scene.attachInputListenerTo(window);
window.addGLEventListener(scene);
- final float[] ppmm = window.getPixelsPerMM(new float[2]);
+ final int glyphGridRowsPerPage;
{
+ final float[] ppmm = window.getPixelsPerMM(new float[2]);
final float[] dpi = FontScale.ppmmToPPI( new float[] { ppmm[0], ppmm[1] } );
System.err.println("DPI "+dpi[0]+" x "+dpi[1]+", "+ppmm[0]+" x "+ppmm[1]+" pixel/mm");
final float[] hasSurfacePixelScale1 = window.getCurrentSurfaceScale(new float[2]);
System.err.println("HiDPI PixelScale: "+hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
System.err.println("mmPerCell "+mmPerCell);
+ glyphGridRowsPerPage = (int)( ( window.getSurfaceHeight() / ppmm[1] ) / mmPerCell );
+ if( 0 >= gridColumns ) {
+ gridColumns = (int)( ( window.getSurfaceWidth() * GlyphGridWidth / ppmm[0] ) / mmPerCell );
+ }
}
- if( 0 >= gridColumns ) {
- gridColumns = (int)( ( window.getSurfaceWidth() * GlyphGridWidth / ppmm[0] ) / mmPerCell );
- }
- final int glyphGridRowsPerPage = (int)( ( window.getSurfaceHeight() / ppmm[1] ) / mmPerCell );
final float glyphGridCellSize = GlyphGridWidth / gridColumns;
final Vec2f glyphGridSize = new Vec2f(GlyphGridWidth, glyphGridRowsPerPage * glyphGridCellSize);
@@ -296,9 +297,9 @@ public class FontView01 {
glyphShapeHolder.addShape(gs);
}
}
- final RangedGroup glyphView = new RangedGroup(options.renderModes, glyphGrid, glyphGridSize,
- null,
- new SliderParam(new Vec2f(glyphGridCellSize/4f, glyphGridSize.y()), glyphGridCellSize/10f, true));
+ final RangedGroup glyphView = new RangedGroup( options.renderModes, glyphGrid, glyphGridSize,
+ null,
+ new SliderParam( new Vec2f(glyphGridCellSize/4f, glyphGridSize.y()), glyphGridCellSize/10f, true ) );
glyphView.getVertSlider().setColor(0.3f, 0.3f, 0.3f, 0.7f).setName("GlyphView");
if( VERBOSE_UI ) {
glyphView.getVertSlider().addSliderListener(new SliderAdapter() {
@@ -306,7 +307,9 @@ public class FontView01 {
public void dragged(final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct) {
final Vec2f minmax = w.getMinMax();
final float row_f = val / glyphGridCellSize;
- System.err.println("VertSlider: row "+row_f+", val["+old_val+" -> "+val+"], pct["+(100*old_val_pct)+"% -> "+(100*val_pct)+"%], minmax "+minmax);
+ System.err.println("VertSlider: row["+row_f+".."+(row_f+gridDim.rowsPerPage-1)+"]/"+gridDim.rows+
+ ", val["+old_val+" -> "+val+"]/"+minmax.y()+", pct["+(100*old_val_pct)+"% -> "+(100*val_pct)+"%], cellSz "+glyphGridCellSize);
+ System.err.println("VertSlider: "+w.getDescription());
}
});
}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UIMediaGrid01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UIMediaGrid01.java
index f4a2898a1..344e42bdd 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UIMediaGrid01.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UIMediaGrid01.java
@@ -48,6 +48,11 @@ import com.jogamp.graph.ui.shapes.Button;
import com.jogamp.graph.ui.shapes.MediaButton;
import com.jogamp.graph.ui.shapes.Rectangle;
import com.jogamp.graph.ui.widgets.MediaPlayer;
+import com.jogamp.graph.ui.widgets.RangeSlider;
+import com.jogamp.graph.ui.widgets.RangedGroup;
+import com.jogamp.graph.ui.widgets.RangeSlider.SliderAdapter;
+import com.jogamp.graph.ui.widgets.RangedGroup.SliderParam;
+import com.jogamp.math.Vec2f;
import com.jogamp.math.Vec2i;
import com.jogamp.math.geom.AABBox;
import com.jogamp.newt.event.KeyAdapter;
@@ -66,19 +71,23 @@ import com.jogamp.opengl.util.av.GLMediaPlayer;
import com.jogamp.opengl.util.av.GLMediaPlayerFactory;
/**
- * MediaButtons in a grid, filled by media files from a directory.
+ * MediaButtons in a {@link RangedGroup} w/ vertical slider, filled by media files from a directory.
*/
public class UIMediaGrid01 {
+ private static final float MediaGridWidth = 1f;
+
static CommandlineOptions options = new CommandlineOptions(1920, 1080, Region.VBAA_RENDERING_BIT);
- public static final List<String> MEDIA_SUFFIXES = Arrays.asList("mp4", "mkv", "m2v", "avi");
- public static int aid = GLMediaPlayer.STREAM_ID_AUTO;
- public static float boxRatio = 16f/9f;
- public static boolean letterBox = true;
+ private static final boolean VERBOSE_UI = false;
+ private static final List<String> MEDIA_SUFFIXES = Arrays.asList("mp4", "mkv", "m2v", "avi");
+ private static int aid = GLMediaPlayer.STREAM_ID_AUTO;
+ private static float boxRatio = 16f/9f;
+ private static boolean letterBox = true;
public static void main(final String[] args) throws IOException {
- int maxMediaFiles = 12; // Integer.MAX_VALUE;
- int columns = -1;
+ float mmPerCellWidth = 75f;
+ int maxMediaFiles = 10000; // Integer.MAX_VALUE;
+ int gridColumns = -1;
String mediaDir = null;
if( 0 != args.length ) {
final int[] idx = { 0 };
@@ -99,9 +108,12 @@ public class UIMediaGrid01 {
boxRatio = MiscUtils.atof(args[idx[0]], boxRatio);
} else if(args[idx[0]].equals("-zoom")) {
letterBox = false;
+ } else if(args[idx[0]].equals("-mmPerCell")) {
+ idx[0]++;
+ mmPerCellWidth = MiscUtils.atof(args[idx[0]], mmPerCellWidth);
} else if(args[idx[0]].equals("-col")) {
idx[0]++;
- columns = MiscUtils.atoi(args[idx[0]], columns);
+ gridColumns = MiscUtils.atoi(args[idx[0]], gridColumns);
}
}
}
@@ -111,7 +123,7 @@ public class UIMediaGrid01 {
System.err.println("aid "+aid);
System.err.println("boxRatio "+boxRatio);
System.err.println("letterBox "+letterBox);
- System.err.println("columns "+columns);
+ System.err.println("columns "+gridColumns);
final List<Uri> mediaFiles = new ArrayList<Uri>();
if( null != mediaDir && mediaDir.length() > 0 ) {
@@ -153,16 +165,7 @@ public class UIMediaGrid01 {
System.err.println("No media files, exit.");
return;
}
- final Vec2i gridDim = new Vec2i(-1, -1);
- {
- // final int w = (int)( Math.round( Math.sqrt( mediaFiles.size() ) ) );
- // final int h = (int)( Math.ceil( mediaFiles.size() / w ) );
- final int w = columns > 0 ? columns : (int)( Math.round( Math.sqrt( mediaFiles.size() ) ) );
- final int h = ( Math.round( mediaFiles.size() / w ) );
- gridDim.set(w, h);
- System.err.println("Media files: Count "+mediaFiles.size()+", grid "+gridDim);
- }
-
+ System.err.println("Media Files Count "+mediaFiles.size());
final GLCapabilities reqCaps = options.getGLCaps();
System.out.println("Requested: " + reqCaps);
@@ -193,30 +196,70 @@ public class UIMediaGrid01 {
window.addGLEventListener(scene);
- final float[] ppmm = window.getPixelsPerMM(new float[2]);
+ final Vec2i gridDim;
+ final int mediaRowsPerPage;
{
+ final float[] ppmm = window.getPixelsPerMM(new float[2]);
final float[] dpi = FontScale.ppmmToPPI( new float[] { ppmm[0], ppmm[1] } );
System.err.println("DPI "+dpi[0]+" x "+dpi[1]+", "+ppmm[0]+" x "+ppmm[1]+" pixel/mm");
final float[] hasSurfacePixelScale1 = window.getCurrentSurfaceScale(new float[2]);
System.err.println("HiDPI PixelScale: "+hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)");
+ final float mmPerCellHeight = mmPerCellWidth / boxRatio;
+ int _mediaRowsPerPage = (int)( ( window.getSurfaceHeight() / ppmm[1] ) / mmPerCellHeight );
+ if( 0 >= gridColumns ) {
+ gridColumns = (int)( ( window.getSurfaceWidth() * MediaGridWidth / ppmm[0] ) / mmPerCellWidth );
+ }
+ if( mediaFiles.size() < gridColumns * _mediaRowsPerPage ) {
+ gridColumns = (int)Math.floor( Math.sqrt ( mediaFiles.size() ) );
+ _mediaRowsPerPage = gridColumns;
+ }
+ mediaRowsPerPage = _mediaRowsPerPage;
+ gridDim = new Vec2i(gridColumns, mediaRowsPerPage);
}
- final Group mediaGrid;
+ final float mediaCellWidth = MediaGridWidth / gridColumns;
+ final float mediaCellHeight = mediaCellWidth/boxRatio;
+ final Vec2f mediaGridSize = new Vec2f(MediaGridWidth, mediaRowsPerPage * mediaCellHeight);
+ System.err.println("GridDim "+gridDim);
+ System.err.println("GridSize "+mediaGridSize);
+ System.err.println("CellSize "+mediaCellWidth+" x "+mediaCellHeight+", boxRatio "+boxRatio);
+
+ final RangedGroup mediaView;
{
- final float cellWidth = boxRatio;
- final float cellHeight = 1f;
- mediaGrid = new Group(new GridLayout(gridDim.x(), cellWidth*0.9f, cellHeight*0.9f, Alignment.FillCenter, new Gap(cellHeight*0.1f, cellWidth*0.1f)));
- mediaGrid.setRelayoutOnDirtyShapes(false);
+ final Group mediaGrid = new Group(new GridLayout(gridDim.x(), mediaCellWidth*0.9f, mediaCellHeight*0.9f, Alignment.FillCenter,
+ new Gap(mediaCellHeight*0.1f, mediaCellWidth*0.1f)));
+ mediaGrid.setInteractive(true).setDragAndResizeable(false).setToggleable(false).setName("MediaGrid");
+ addMedia(scene, reqCaps.getGLProfile(), mediaGrid, mediaFiles, boxRatio);
+ mediaGrid.setRelayoutOnDirtyShapes(false); // avoid group re-validate to ease load in Group.isShapeDirty() w/ thousands of glyphs
+ if( VERBOSE_UI ) {
+ mediaGrid.validate(reqCaps.getGLProfile());
+ System.err.println("MediaGrid "+mediaGrid);
+ System.err.println("MediaGrid "+mediaGrid.getLayout());
+ }
+ mediaView = new RangedGroup(options.renderModes, mediaGrid, mediaGridSize,
+ null,
+ new SliderParam(new Vec2f(mediaCellWidth/20f, mediaGridSize.y()), mediaCellHeight/30f, true));
+ mediaView.getVertSlider().setColor(0.3f, 0.3f, 0.3f, 0.7f).setName("MediaView");
+ // mediaView.setRelayoutOnDirtyShapes(false); // avoid group re-validate to ease load in Group.isShapeDirty() w/ thousands of glyphs
+ if( VERBOSE_UI ) {
+ mediaView.getVertSlider().addSliderListener(new SliderAdapter() {
+ @Override
+ public void dragged(final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct) {
+ final Vec2f minmax = w.getMinMax();
+ final float row_f = val / mediaCellHeight;
+ System.err.println("VertSlider: row "+row_f+", val["+old_val+" -> "+val+"], pct["+(100*old_val_pct)+"% -> "+(100*val_pct)+"%], minmax "+minmax);
+ }
+ });
+ }
+ if( VERBOSE_UI ) {
+ mediaView.validate(reqCaps.getGLProfile());
+ System.err.println("GlyphView "+mediaView);
+ }
}
- mediaGrid.setName("MediaGrid");
- addMedia(scene, reqCaps.getGLProfile(), mediaGrid, mediaFiles, boxRatio);
- mediaGrid.validate(reqCaps.getGLProfile());
- System.err.println("MediaGrid "+mediaGrid);
- System.err.println("MediaGrid "+mediaGrid.getLayout());
final Group mainGrid = new Group(new GridLayout(1, 0f, 0f, Alignment.None));
mainGrid.setName("MainGrid");
- mainGrid.addShape(mediaGrid);
+ mainGrid.addShape(mediaView);
scene.addShape(mainGrid);
window.addKeyListener(new KeyAdapter() {
diff --git a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java
index 6c8398081..afcc1faef 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java
@@ -39,6 +39,7 @@ import com.jogamp.graph.ui.layout.Padding;
import com.jogamp.graph.ui.shapes.BaseButton;
import com.jogamp.graph.ui.shapes.Button;
import com.jogamp.graph.ui.shapes.Rectangle;
+import com.jogamp.math.FloatUtil;
import com.jogamp.math.Vec2f;
import com.jogamp.math.Vec3f;
import com.jogamp.math.Vec4f;
@@ -91,12 +92,13 @@ public final class RangeSlider extends Widget {
public void run(SliderListener l);
}
+ private static final boolean DEBUG = false;
private static final float pageKnobScale = 0.6f; // 0.6 * barWidth
private static final float pageBarLineScale = 0.25f; // 1/4 * ( barWidth - pageKnobWidth )
private static final float pageKnobSizePctMin = 5f/100f;
private final boolean horizontal;
- /** Knob height orthogonal to sliding direction */
- private float knobHeight;
+ /** Knob thickness orthogonal to sliding direction */
+ private float knobThickn;
/** Knob length in sliding direction */
private float knobLength;
private final Vec2f size;
@@ -105,6 +107,7 @@ public final class RangeSlider extends Widget {
private final GraphShape knob;
private ArrayList<SliderListener> sliderListeners = new ArrayList<SliderListener>();
private final Vec2f minMax = new Vec2f(0, 100);
+ private final float knobScale;
private float pageSize;
private float val=0, val_pct=0;
private boolean inverted=false;
@@ -116,9 +119,12 @@ public final class RangeSlider extends Widget {
* This slider comprises a background bar and a positional round knob,
* with {@link #getValue()} at center position.
* </p>
+ * <p>
+ * The spatial {@code size} gets automatically updated at {@link #validate(GL2ES2)}
+ * </p>
* @param renderModes Graph's {@link Region} render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}.
- * @param size width and height of this slider box. A horizontal slider has width >= height.
- * @param knobScale multiple of slider-bar height for {@link #getKnobHeight()}
+ * @param size spatial dimension of this slider box. A horizontal slider has width >= height.
+ * @param knobScale multiple of slider-bar height for {@link #getKnobThickness()}
* @param minMax minimum- and maximum-value of slider
* @param unitSize size of one unit (element) in sliding direction
* @param value current value of slider
@@ -133,8 +139,11 @@ public final class RangeSlider extends Widget {
* This slider comprises a framing bar and a rectangular page-sized knob,
* with {@link #getValue()} at page-start position.
* </p>
+ * <p>
+ * The spatial {@code size} and {@code pageSize} gets automatically updated at {@link #validate(GL2ES2)}
+ * </p>
* @param renderModes Graph's {@link Region} render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}.
- * @param size width and height of this slider box. A horizontal slider has width >= height.
+ * @param size spatial dimension of this slider box. A horizontal slider has width >= height.
* @param minMax minimum- and maximum-value of slider
* @param unitSize size of one unit (element) in sliding direction
* @param pageSize size of one virtual-page, triggers rendering mode from knob to rectangle
@@ -147,6 +156,7 @@ public final class RangeSlider extends Widget {
private RangeSlider(final int renderModes_, final Vec2f size, final float knobScale,
final Vec2f minMax, final float unitSize, final float pageSz, final float value) {
final int renderModes = renderModes_ & ~(Region.COLORCHANNEL_RENDERING_BIT);
+ this.knobScale = knobScale;
this.unitSize = unitSize;
this.pageSize = pageSz;
this.horizontal = size.x() >= size.y();
@@ -156,41 +166,26 @@ public final class RangeSlider extends Widget {
marks.setInteractive(false).setToggleable(false).setDraggable(false).setResizable(false);
this.size = new Vec2f(size);
- if( Float.isNaN(pageSize) ) {
- if( horizontal ) {
- knobHeight = size.y()*knobScale;
- setPaddding(new Padding(knobHeight/2f, 0, knobHeight/2f, 0));
- } else {
- knobHeight = size.x()*knobScale;
- setPaddding(new Padding(0, knobHeight/2f, 0, knobHeight/2f));
- }
- knobLength = knobHeight;
- bar = new Rectangle(renderModes, this.size.x(), this.size.y(), 0);
- knob = new BaseButton(renderModes , knobHeight*1.01f, knobHeight);
- setBackgroundBarColor(0.60f, 0.60f, 0.60f, 0.5f);
- } else {
- final float pageSizePct = Math.max(pageKnobSizePctMin, pageSize / getRange(minMax));
+ if( DEBUG ) { System.err.println("RangeSlider.ctor0 "+getDescription()); }
+ setMinMaxImpl(minMax.x(), minMax.y()); // pre-set for setKnobSize()
+ setKnobSize(pageSize, false, false);
+ if( DEBUG ) { System.err.println("RangeSlider.ctor1 "+getDescription()); }
+ if( Float.isFinite(pageSize) ) {
final float barLineWidth;
- final float width, height;
if( horizontal ) {
- height = size.y() * pageKnobScale;
- width = pageSizePct * this.size.x();
- barLineWidth = ( size.y() - height ) * pageBarLineScale;
- knobLength = width;
- knobHeight = height;
- setPaddding(new Padding(size.y()/2f, 0, size.y()/2f, 0));
+ barLineWidth = ( size.y() - knobThickn ) * pageBarLineScale;
+ knob = new Rectangle(renderModes, knobLength, knobThickn, 0);
} else {
- width = size.x() * pageKnobScale;
- height = pageSizePct * this.size.y();
- barLineWidth = ( size.x() - width ) * pageBarLineScale;
- knobLength = height;
- knobHeight = width;
- setPaddding(new Padding(0, size.x()/2f, 0, size.x()/2f));
- // System.err.println("ZZZ minMax "+minMax+", pageSize "+pageSize+" "+(pageSizePct*100f)+"% -> "+knobHeight+"/"+this.size.y());
+ barLineWidth = ( size.x() - knobThickn ) * pageBarLineScale;
+ knob = new Rectangle(renderModes, knobThickn, knobLength, 0);
}
bar = new Rectangle(renderModes, this.size.x(), this.size.y(), barLineWidth);
- knob = new Rectangle(renderModes, width, height, 0);
+ } else {
+ bar = new Rectangle(renderModes, this.size.x(), this.size.y(), 0);
+ knob = new BaseButton(renderModes , knobThickn*1.01f, knobThickn);
+ setBackgroundBarColor(0.60f, 0.60f, 0.60f, 0.5f);
}
+ if( DEBUG ) { System.err.println("RangeSlider.ctor3 "+getDescription()); }
setColor(0.80f, 0.80f, 0.80f, 0.7f);
bar.setToggleable(false).setInteractive(false);
@@ -203,16 +198,16 @@ public final class RangeSlider extends Widget {
barAndKnob.addShape( knob );
addShape(barAndKnob);
- setMinMax(minMax, value);
+ reconfig(minMax, true, value, false, 0);
knob.onMove((final Shape s, final Vec3f origin, final Vec3f dest) -> {
final float old_val = val;
final float old_val_pct = val_pct;
- if( Float.isNaN(pageSize) ) {
- setValuePct( getKnobValuePct( dest.x(), dest.y(), knobLength/2f ) ); // centered
- } else {
+ if( Float.isFinite(pageSize) ) {
final float dy = inverted ? +knobLength: 0; // offset to knob start
setValuePct( getKnobValuePct( dest.x(), dest.y(), dy ) );
+ } else {
+ setValuePct( getKnobValuePct( dest.x(), dest.y(), knobLength/2f ) ); // centered
}
dispatchToListener( (final SliderListener l) -> {
l.dragged(RangeSlider.this, old_val, val, old_val_pct, val_pct);
@@ -249,7 +244,7 @@ public final class RangeSlider extends Widget {
v+=unitSize;
}
}
- } else if( !Float.isNaN(pageSize) ){
+ } else if( Float.isFinite(pageSize) ){
if( e.getRotation()[1] < 0f ) {
if( inverted ) {
v+=pageSize;
@@ -331,7 +326,7 @@ public final class RangeSlider extends Widget {
}
}
}
- if( !action && !Float.isNaN(pageSize) ) {
+ if( !action && Float.isFinite(pageSize) ) {
if( keySym == KeyEvent.VK_PAGE_DOWN ) {
action = true;
if( inverted ) {
@@ -437,44 +432,137 @@ public final class RangeSlider extends Widget {
return mark;
}
+ /** Returns spatial dimension of this slider */
public final Vec2f getSize() { return size; }
- /** Knob height orthogonal to sliding direction */
- public final float getKnobHeight() { return knobHeight; }
- /** Knob length in sliding direction */
+ /** Returns spatial knob thickness orthogonal to sliding direction */
+ public final float getKnobThickness() { return knobThickn; }
+ /** Returns spatial knob length in sliding direction */
public final float getKnobLength() { return knobLength; }
/** Returns slider value range, see {@link #setMinMax(Vec2f, float)} */
public Vec2f getMinMax() { return minMax; }
+ /** Returns {@link #getMinMax()} range. */
public float getRange() { return minMax.y() - minMax.x(); }
- private float getRange(final Vec2f minMax) { return minMax.y() - minMax.x(); }
+ private static float getRange(final Vec2f minMax) { return minMax.y() - minMax.x(); }
+ /** Returns current slider value */
public float getValue() { return val; }
+ /** Returns current slider {@link #getValue() value} in percentage of {@link #getRange()}, */
public float getValuePct() { return val_pct; }
- public RangeSlider setPageSize(final float v) {
- if( Float.isNaN(this.pageSize) || Float.isNaN(v) ) {
- return this;
- }
- this.pageSize = v;
- final float pageSizePct = Math.max(pageKnobSizePctMin, pageSize / getRange());
- final float width, height;
- if( horizontal ) {
- height = size.y() * pageKnobScale;
- width = pageSizePct * this.size.x();
- knobLength = width;
- knobHeight = height;
+ /**
+ * Sets the page-size if a rectangular knob is being used, i.e. {@link #RangeSlider(int, Vec2f, Vec2f, float, float, float)},
+ * otherwise does nothing.
+ * @param pageSz the page-size, which will be clipped to {@link #getMinMax()}.
+ * @return this instance of chaining
+ * @see #getPageSize()
+ * @see #RangeSlider(int, Vec2f, Vec2f, float, float, float)
+ */
+ public RangeSlider setPageSize(final float pageSz) {
+ return setKnobSize(pageSz, true, true);
+ }
+ private RangeSlider setKnobSize(final float pageSz, final boolean adjKnob, final boolean adjValue) {
+ if( Float.isFinite(pageSize) && Float.isFinite(pageSz) ) {
+ final float range = getRange(minMax);
+ if( Float.isFinite(range) && !FloatUtil.isZero(range) ) {
+ pageSize = Math.min(minMax.y(), Math.max(minMax.x(), pageSz));
+ }
+ final float pageSizePct = getPageSizePct(pageKnobSizePctMin);
+ final float width, height;
+ if( horizontal ) {
+ width = pageSizePct * this.size.x();
+ height = size.y() * pageKnobScale;
+ knobLength = width;
+ knobThickn = height;
+ if( !paddingSet ) {
+ setPaddding(new Padding(size.y()/2f, 0, size.y()/2f, 0));
+ paddingSet = true;
+ }
+ } else {
+ width = size.x() * pageKnobScale;
+ height = pageSizePct * this.size.y();
+ knobLength = height;
+ knobThickn = width;
+ if( !paddingSet ) {
+ setPaddding(new Padding(0, size.x()/2f, 0, size.x()/2f));
+ paddingSet = true;
+ }
+ }
+ if( adjKnob ) {
+ ((Rectangle)knob).setDimension(width, height, 0);
+ }
+ if( adjValue ) {
+ setValue( val );
+ }
+ } else if( Float.isFinite(pageSize) ) {
+ // nop w/ invalid pageSz but valid pageSize
} else {
- width = size.x() * pageKnobScale;
- height = pageSizePct * this.size.y();
- knobLength = height;
- knobHeight = width;
+ if( horizontal ) {
+ knobThickn = size.y()*knobScale;
+ if( !paddingSet ) {
+ setPaddding(new Padding(knobThickn/2f, 0, knobThickn/2f, 0));
+ paddingSet = true;
+ }
+ } else {
+ knobThickn = size.x()*knobScale;
+ if( !paddingSet ) {
+ setPaddding(new Padding(0, knobThickn/2f, 0, knobThickn/2f));
+ paddingSet = true;
+ }
+ }
+ knobLength = knobThickn;
}
- ((Rectangle)knob).setDimension(width, height, 0);
return this;
}
- public float getPageSize() { return this.pageSize; }
+ private boolean paddingSet = false;
- public void setUnitSize(final float v) { this.unitSize = v; }
- public float getUnitSize() { return this.unitSize; }
+ private void setMinMaxImpl(final float min, final float max) {
+ this.minMax.set(Float.isFinite(min) ? min : 0, Float.isFinite(max) ? max : 0);
+ }
+ private RangeSlider reconfig(final Vec2f minMax,
+ final boolean modValue, final float value,
+ final boolean modKnobSz, final float pageSz)
+ {
+ if( null != minMax ) {
+ setMinMaxImpl(minMax.x(), minMax.y());
+ }
+ if( modKnobSz ) {
+ setKnobSize(pageSz, true, !modValue);
+ }
+ if( modValue ) {
+ setValue( value );
+ }
+ if( DEBUG ) { System.err.println("RangeSlider.cfg "+getDescription()); }
+ return this;
+ }
+
+ /**
+ * Returns the page-size if a rectangular knob is being used, i.e. {@link #RangeSlider(int, Vec2f, Vec2f, float, float, float)},
+ * otherwise returns {@link Float#NaN}.
+ * @see #setPageSize(float)
+ * @see #RangeSlider(int, Vec2f, Vec2f, float, float, float)
+ */
+ public float getPageSize() { return pageSize; }
+
+ /**
+ * Returns the page-size percentage if a rectangular knob is being used, i.e. {@link #RangeSlider(int, Vec2f, Vec2f, float, float, float)},
+ * otherwise returns {@link Float#NaN}.
+ * @param minPct minimum percentage to be returned, should be >= 0
+ * @see #setPageSize(float)
+ * @see #RangeSlider(int, Vec2f, Vec2f, float, float, float)
+ */
+ public float getPageSizePct(final float minPct) {
+ if( Float.isFinite(pageSize) ) {
+ final float range = getRange(minMax);
+ return Float.isFinite(range) && !FloatUtil.isZero(range) ? Math.max(minPct, pageSize / range) : minPct;
+ } else {
+ return Float.NaN;
+ }
+ }
+
+ /** Sets the size of one unit (element) in sliding direction */
+ public RangeSlider setUnitSize(final float v) { unitSize = v; return this; }
+ /** Returns the size of one unit (element) in sliding direction */
+ public float getUnitSize() { return unitSize; }
/**
* Sets whether this slider uses an inverted value range,
@@ -486,24 +574,71 @@ public final class RangeSlider extends Widget {
public boolean isInverted() { return inverted; }
/**
- * Sets slider value range and current value
+ * Sets slider value range and current value, also updates related pageSize parameter if used.
* @param minMax minimum- and maximum-value of slider
- * @param value current value of slider
+ * @param value new value of slider, clipped against {@link #getMinMax()}
* @return this instance of chaining
*/
public RangeSlider setMinMax(final Vec2f minMax, final float value) {
- this.minMax.set(minMax);
- return setValue( value );
+ return reconfig(minMax, true, value, true, pageSize);
+ }
+
+ /**
+ * Sets slider value range, also updates related pageSize parameter if used.
+ * @param minMax minimum- and maximum-value of slider
+ * @return this instance of chaining
+ */
+ public RangeSlider setMinMax(final Vec2f minMax) {
+ return reconfig(minMax, false, 0, true, pageSize);
+ }
+
+ /**
+ * Calls {@link #setMinMax(Vec2f, float)} and {@link #setPageSize(float)}.
+ * @param minMax minimum- and maximum-value of slider
+ * @param value new value of slider, clipped against {@code minMax}
+ * @param pageSz the page-size, which will be clipped to {@code minMax}
+ * @return this instance of chaining
+ */
+ public RangeSlider setMinMaxPgSz(final Vec2f minMax, final float value, final float pageSz) {
+ return reconfig(minMax, true, value, true, pageSz);
+ }
+
+ /**
+ * Calls {@link #setMinMax(Vec2f, float)} and {@link #setPageSize(float)}.
+ * @param minMax minimum- and maximum-value of slider
+ * @param pageSz the page-size, which will be clipped to {@code minMax}
+ * @return this instance of chaining
+ */
+ public RangeSlider setMinMaxPgSz(final Vec2f minMax, final float pageSz) {
+ return reconfig(minMax, false, 0, true, pageSz);
}
public RangeSlider setValuePct(final float v) {
- final float pct = Math.max(0.0f, Math.min(1.0f, v));
- return setValue( minMax.x() + ( pct * getRange() ) );
+ final float range = getRange();
+ if( Float.isFinite(v) && Float.isFinite(range) && !FloatUtil.isZero(range) ) {
+ final float pgsz_pct = Float.isFinite(pageSize) ? pageSize / range : 0f;
+ final float pct = Math.max(0f, Math.min(1f - pgsz_pct, v));
+ return setValue( minMax.x() + ( pct * range ) );
+ } else {
+ return setValue( 0f );
+ }
}
+ /**
+ * Sets slider value
+ * @param v new value of slider, clipped against {@link #getMinMax()}
+ * @return this instance of chaining
+ */
public RangeSlider setValue(final float v) {
- val = Math.max(minMax.x(), Math.min(minMax.y(), v));
- val_pct = ( val - minMax.x() ) / getRange();
+ final float v1 = Float.isFinite(v) ? v : 0f;
+ final float pgsz = Float.isFinite(pageSize) ? pageSize : 0f;
+ final float range = getRange();
+ val = Math.max(minMax.x(), Math.min(minMax.y() - pgsz, v1));
+ if( Float.isFinite(range) && !FloatUtil.isZero(range) ) {
+ val_pct = ( val - minMax.x() ) / range;
+ } else {
+ val_pct = 0f;
+ }
setKnob();
return this;
}
@@ -523,25 +658,25 @@ public final class RangeSlider extends Widget {
* @param posRes {@link Vec2f} result storage
* @param val_pct value percentage within [0..1]
* @param itemLen item length in sliding direction
- * @param itemHeight item height orthogonal to sliding direction
+ * @param itemThickn item thickness orthogonal to sliding direction
*/
- private Vec2f getItemPctPos(final Vec2f posRes, final float val_pct, final float itemLen, final float itemHeight) {
+ private Vec2f getItemPctPos(final Vec2f posRes, final float val_pct, final float itemLen, final float itemThickn) {
final float v = inverted ? 1f - val_pct : val_pct;
final float itemAdjust;
- if( Float.isNaN(pageSize) ) {
- itemAdjust = itemLen * 0.5f; // centered
- } else {
+ if( Float.isFinite(pageSize) ) {
if( inverted ) {
itemAdjust = itemLen; // top-edge
} else {
itemAdjust = 0; // bottom-edge
}
+ } else {
+ itemAdjust = itemLen * 0.5f; // centered
}
if( horizontal ) {
posRes.setX( Math.max(0, Math.min(size.x() - itemLen, v*size.x() - itemAdjust)) );
- posRes.setY( -( itemHeight - size.y() ) * 0.5f );
+ posRes.setY( -( itemThickn - size.y() ) * 0.5f );
} else {
- posRes.setX( -( itemHeight - size.x() ) * 0.5f );
+ posRes.setX( -( itemThickn - size.x() ) * 0.5f );
posRes.setY( Math.max(0, Math.min(size.y() - itemLen, v*size.y() - itemAdjust)) );
}
return posRes;
@@ -557,7 +692,7 @@ public final class RangeSlider extends Widget {
}
private void setKnob() {
- final Vec2f pos = getItemPctPos(new Vec2f(), val_pct, knobLength, knobHeight);
+ final Vec2f pos = getItemPctPos(new Vec2f(), val_pct, knobLength, knobThickn);
knob.moveTo(pos.x(), pos.y(), Button.DEFAULT_LABEL_ZOFFSET);
}
@@ -578,7 +713,7 @@ public final class RangeSlider extends Widget {
public final Shape setColor(final float r, final float g, final float b, final float a) {
super.setColor(r, g, b, a);
knob.setColor(r, g, b, a);
- if( !Float.isNaN(pageSize) ) {
+ if( Float.isFinite(pageSize) ) {
bar.setColor(r, g, b, 1.0f);
}
return this;
@@ -601,7 +736,7 @@ public final class RangeSlider extends Widget {
public Shape setColor(final Vec4f c) {
this.rgbaColor.set(c);
knob.setColor(c);
- if( !Float.isNaN(pageSize) ) {
+ if( Float.isFinite(pageSize) ) {
bar.setColor(c.x(), c.y(), c.z(), 1.0f);
}
return this;
@@ -614,7 +749,7 @@ public final class RangeSlider extends Widget {
* </p>
*/
public Shape setBackgroundBarColor(final float r, final float g, final float b, final float a) {
- if( Float.isNaN(pageSize) ) {
+ if( !Float.isFinite(pageSize) ) {
bar.setColor(r, g, b, a);
}
return this;
@@ -626,7 +761,7 @@ public final class RangeSlider extends Widget {
* </p>
*/
public Shape setBackgroundBarColor(final Vec4f c) {
- if( Float.isNaN(pageSize) ) {
+ if( !Float.isFinite(pageSize) ) {
bar.setColor(c);
}
return this;
@@ -646,8 +781,36 @@ public final class RangeSlider extends Widget {
return this;
}
+ /** Return string description of current slider setting. */
+ public String getDescription() {
+ final String pre = "value "+val+" "+(100f*val_pct)+"%, range "+minMax;
+ final String post = ", ssize "+size+", knob[l "+knobLength+", t "+knobThickn+"]";
+ if( Float.isFinite(pageSize) ) {
+ final float pageSizePct = getPageSizePct(pageKnobSizePctMin);
+ final String detail = ", pageSize "+pageSize+" "+(pageSizePct*100f)+"% -> "+knobLength;
+ if( horizontal ) {
+ return "H "+pre+detail+"/"+size.x()+post;
+ } else {
+ return "V "+pre+detail+"/"+size.y()+post;
+ }
+ } else {
+ if( horizontal ) {
+ return "H "+pre+post;
+ } else {
+ return "V "+pre+post;
+ }
+ }
+ }
@Override
public String getSubString() {
- return super.getSubString()+", range["+minMax+"] @ "+val+", "+(100f*val_pct)+"%";
+ return super.getSubString()+", "+getDescription()+" @ "+val+", "+(100f*val_pct)+"%";
+ }
+ @Override
+ protected void validateImpl(final GL2ES2 gl, final GLProfile glp) {
+ if( isShapeDirty() ) {
+ super.validateImpl(gl, glp);
+ setKnobSize(pageSize, true, true);
+ if( DEBUG ) { System.err.println("RangeSlider.val "+getDescription()); }
+ }
}
}
diff --git a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java
index 9143a9431..211ebe7e9 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java
@@ -58,12 +58,12 @@ public class RangedGroup extends Widget {
/** {@link RangeSlider} configuration parameter for {@link RangedGroup}. */
public static final class SliderParam {
- /** width and height of this slider box. A horizontal slider has width >= height. */
+ /** spatial dimension of the slider box. A horizontal slider has width >= height. */
public final Vec2f size;
/** size of one unit (element) in sliding direction */
public final float unitSize;
/**
- * Toggle whether this slider uses an inverted value range,
+ * Toggle whether the slider uses an inverted value range,
* e.g. top 0% and bottom 100% for an vertical inverted slider
* instead of bottom 0% and top 100% for a vertical non-inverted slider.
*/
@@ -71,7 +71,7 @@ public class RangedGroup extends Widget {
/**
*
- * @param size width and height of this slider box. A horizontal slider has width >= height.
+ * @param size spatial dimension of this slider box. A horizontal slider has width >= height.
* @param unitSize size of one unit (element) in sliding direction
* @param inverted toggle to invert value range, see {@link #inverted}
*/
@@ -86,12 +86,12 @@ public class RangedGroup extends Widget {
* Construct a {@link RangedGroup}
* @param renderModes Graph's {@link Region} render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}.
* @param content the {@link Group} with content to view
- * @param contentSize the fixed size of the clipped content to view, i.e. page-size
- * @param horizSliderParam optional horizontal slider parameters, null for none
- * @param vertSliderParam optional vertical slider parameters, null for none
+ * @param contentSize the fixed spatial size of the clipped content to view, i.e. page-size
+ * @param horizSliderParam optional initial horizontal slider parameters, null for none
+ * @param vertSliderParam optional initial vertical slider parameters, null for none
*/
public RangedGroup(final int renderModes, final Group content, final Vec2f contentSize,
- final SliderParam horizSliderParam, final SliderParam vertSliderParam)
+ final SliderParam horizSliderParam, final SliderParam vertSliderParam)
{
super( new GridLayout(1 + (null != vertSliderParam ? 1 : 0), 0f, 0f, Alignment.None)); // vertical slider adds to the right column
this.content = content;
@@ -108,8 +108,11 @@ public class RangedGroup extends Widget {
@Override
public void dragged(final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct) {
final Vec3f oldPos = content.getPosition();
- final float newXPos = w.getValue();
- content.moveTo(contentPosZero.x()+newXPos, oldPos.y(), oldPos.z());
+ if( vertSlider.isInverted() ) {
+ content.moveTo(contentPosZero.x()-val, oldPos.y(), oldPos.z());
+ } else {
+ content.moveTo(contentPosZero.x()+val, oldPos.y(), oldPos.z());
+ }
}
} );
} else {
@@ -123,8 +126,11 @@ public class RangedGroup extends Widget {
@Override
public void dragged(final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct) {
final Vec3f oldPos = content.getPosition();
- final float newYPos = w.getValue();
- content.moveTo(oldPos.x(), contentPosZero.y()+newYPos, oldPos.z());
+ if( vertSlider.isInverted() ) {
+ content.moveTo(oldPos.x(), contentPosZero.y()+val, oldPos.z());
+ } else {
+ content.moveTo(oldPos.x(), contentPosZero.y()-val, oldPos.z());
+ }
}
} );
} else {
@@ -139,7 +145,9 @@ public class RangedGroup extends Widget {
public Group getContent() { return content; }
public Vec2f getContentSize(final Vec2f out) { return clippedContent.getFixedSize(out); }
public Group getClippedContent() { return clippedContent; }
+ /** Returns the used horizontal {@link RangeSlider} or {@code null}. */
public RangeSlider getHorizSlider() { return horizSlider; }
+ /** Returns the used vertical {@link RangeSlider} or {@code null}. */
public RangeSlider getVertSlider() { return vertSlider; }
@Override
@@ -147,19 +155,19 @@ public class RangedGroup extends Widget {
if( isShapeDirty() ) {
super.validateImpl(gl, glp);
- final AABBox b = content.getBounds();
+ final AABBox cb = content.getBounds();
final Vec3f contentSize = clippedContent.getFixedSize();
contentPosZero.set(0, 0);
if( null != horizSlider ) {
- horizSlider.setMinMax(new Vec2f(0, content.getBounds().getWidth()), 0);
+ horizSlider.setMinMax(new Vec2f(0, content.getBounds().getWidth()));
if( horizSlider.isInverted() ) {
- contentPosZero.setX( contentSize.x() - b.getWidth() );
+ contentPosZero.setX( cb.getWidth() - contentSize.x() );
}
}
if( null != vertSlider ) {
- vertSlider.setMinMax(new Vec2f(0, content.getBounds().getHeight()), 0);
+ vertSlider.setMinMax(new Vec2f(0, content.getBounds().getHeight()));
if( vertSlider.isInverted() ) {
- contentPosZero.setY( contentSize.y() - b.getHeight() );
+ contentPosZero.setY( contentSize.y() - cb.getHeight() );
}
}
}