diff options
-rw-r--r-- | make/scripts/tests.sh | 8 | ||||
-rw-r--r-- | src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java | 158 | ||||
-rw-r--r-- | src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java | 12 | ||||
-rw-r--r-- | src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java | 420 |
4 files changed, 416 insertions, 182 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index e143d6e42..22a6b2e31 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -45,8 +45,8 @@ if [ $MOSX -eq 1 ] ; then fi # We use TempJarCache and JAR files per default now! -export USE_BUILDDIR=0 -#export USE_BUILDDIR=1 +#export USE_BUILDDIR=0 +export USE_BUILDDIR=1 if [ $USE_BUILDDIR -eq 1 ] ; then export LD_LIBRARY_PATH=$JOGAMP_LD_LIBRARY_PATH @@ -1022,8 +1022,8 @@ function testawtswt() { #testnoawt com.jogamp.opengl.demos.av.MovieSimple $* #testnoawt com.jogamp.opengl.demos.av.MovieCube $* #testnoawt com.jogamp.opengl.demos.graph.ui.UIMediaGrid00 $* -testnoawt com.jogamp.opengl.demos.graph.ui.UIMediaGrid01 $* -#testnoawt com.jogamp.opengl.demos.graph.ui.FontView01 $* +#testnoawt com.jogamp.opengl.demos.graph.ui.UIMediaGrid01 $* +testnoawt com.jogamp.opengl.demos.graph.ui.FontView01 $* #testawt com.jogamp.opengl.demos.graph.ui.UISceneDemo20 $* #testawt com.jogamp.opengl.demos.es2.GearsES2 $* #testnoawt com.jogamp.opengl.demos.graph.ui.UIGraphDemoU01a $* 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 0e7590995..dd416e58d 100644 --- a/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java +++ b/src/demos/com/jogamp/opengl/demos/graph/ui/FontView01.java @@ -52,11 +52,13 @@ import com.jogamp.graph.ui.layout.Padding; import com.jogamp.graph.ui.shapes.GlyphShape; import com.jogamp.graph.ui.shapes.Label; import com.jogamp.graph.ui.shapes.Rectangle; +import com.jogamp.graph.ui.widgets.RangeSlider; +import com.jogamp.graph.ui.widgets.RangeSlider.SliderListener; +import com.jogamp.math.Vec2f; import com.jogamp.math.Vec2i; import com.jogamp.math.geom.AABBox; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; -import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; @@ -273,7 +275,7 @@ public class FontView01 { final float infoCellWidth = 1f - GlyphGridWidth; final float infoCellHeight = glyphGrid.getBounds().getHeight() / 2f; final Group infoGrid = new Group( new GridLayout(1, infoCellWidth, infoCellHeight * 0.98f, Alignment.FillCenter, new Gap(infoCellHeight*0.02f, 0)) ); - infoGrid.setPaddding( new Padding(0, 0, 0, 0.02f) ); + infoGrid.setPaddding( new Padding(0, 0, 0, 0.01f) ); infoGrid.addShape(glyphShapeBox.setBorder(0.005f).setBorderColor(0, 0, 0, 1)); infoGrid.addShape(glyphInfoBox.setBorder(0.005f).setBorderColor(0, 0, 0, 1)); if( VERBOSE_UI ) { @@ -282,8 +284,50 @@ public class FontView01 { System.err.println("InfoGrid "+infoGrid.getLayout()); } - final Group glyphInfoGrid = new Group(new GridLayout(2, 0f, 0f, Alignment.None)); + final Group glyphInfoGrid = new Group(new GridLayout(4, 0f, 0f, Alignment.None)); glyphInfoGrid.addShape(glyphGrid); + final boolean sliderInverted = true; + final RangeSlider rs1 = new RangeSlider(options.renderModes, + new Vec2f((GlyphGridWidth/gridDim.rawSize.x())/5f, glyphGrid.getBounds().getHeight()), + new Vec2f(0, gridDim.contourChars.size()/gridDim.columns), gridDim.rows, 0).setInverted(sliderInverted); + final RangeSlider rs2 = new RangeSlider(options.renderModes, + new Vec2f((GlyphGridWidth/gridDim.rawSize.x())/5f, glyphGrid.getBounds().getHeight()), 2, + new Vec2f(0, gridDim.contourChars.size()/gridDim.columns), 0).setInverted(sliderInverted); + final SliderListener sliderListener = new SliderListener() { + @Override + public void clicked(final RangeSlider w, final MouseEvent e) { } + @Override + public void pressed(final RangeSlider w, final MouseEvent e) { } + @Override + public void released(final RangeSlider w, final MouseEvent e) { } + @Override + public void dragged(final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct) { + final int totalRows = gridDim.contourChars.size() / gridDim.columns; + if( w == rs1 ) { + System.err.println("XXX range "+w.getMinMax()+", v "+w.getValue()+"/"+totalRows+" -> "+(w.getValue()*gridDim.columns)+"/"+gridDim.contourChars.size()+", "+(100f*w.getValuePct())+"%"); + rs2.setValue(w.getValue()); + } else { + System.err.println("YYY range "+w.getMinMax()+", v "+w.getValue()+"/"+totalRows+" -> "+(w.getValue()*gridDim.columns)+"/"+gridDim.contourChars.size()+", "+(100f*w.getValuePct())+"%"); + rs1.setValue(w.getValue()); + } + gridDim.setStartRow( (int)w.getValue() ); + window.invoke(false, new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + glyphGrid.removeAllShapes(drawable.getGL().getGL2ES2(), scene.getRenderer()); + addGlyphs(reqGLP, font, glyphGrid, gridDim, showUnderline[0], showLabel[0], fontStatus, fontInfo, glyphMouseListener); + if( VERBOSE_UI ) { + System.err.println("Slider: "+gridDim); + } + return true; + } + }); + } + }; + rs1.onSlider( sliderListener ); + rs2.onSlider( sliderListener ); + glyphInfoGrid.addShape(rs1); + glyphInfoGrid.addShape(rs2); glyphInfoGrid.addShape(infoGrid); if( VERBOSE_UI ) { glyphInfoGrid.validate(reqGLP); @@ -293,7 +337,7 @@ public class FontView01 { mainGrid = new Group(new GridLayout(1, 0f, 0f, Alignment.None)); mainGrid.addShape(glyphInfoGrid); - final Label infoText = new Label(options.renderModes, fontInfo, "Key-Up/Down or Mouse-Scroll to move through glyph symbols. Page-Up/Down or Control + Mouse-Scroll to page through glyph symbols fast."); + final Label infoText = new Label(options.renderModes, fontInfo, "Slider: Key-Up/Down or Mouse-Scroll to move through glyphs. Page-Up/Down or Control + Mouse-Scroll to page through glyph symbols fast."); infoText.setColor(0.1f, 0.1f, 0.1f, 1f); { final float h = glyphGrid.getBounds().getHeight() / gridDim.rawSize.y() * 0.6f; @@ -310,85 +354,13 @@ public class FontView01 { } scene.addShape(mainGrid); - window.addMouseListener( new MouseAdapter() { - @Override - public void mouseWheelMoved(final MouseEvent e) { - if( VERBOSE_UI ) { - System.err.println("Scroll.0: "+gridDim); - } - if( e.getRotation()[1] < 0f ) { - // scroll down - if( e.isControlDown() ) { - gridDim.pageDown(); - } else { - gridDim.lineDown(); - } - } else { - // scroll up - if( e.isControlDown() ) { - gridDim.pageUp(); - } else { - gridDim.lineUp(); - } - } - window.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - glyphGrid.removeAllShapes(drawable.getGL().getGL2ES2(), scene.getRenderer()); - addGlyphs(reqGLP, font, glyphGrid, gridDim, showUnderline[0], showLabel[0], fontStatus, fontInfo, glyphMouseListener); - if( VERBOSE_UI ) { - System.err.println("Scroll.X: "+gridDim); - } - return true; - } - }); - } - }); window.addKeyListener(new KeyAdapter() { @Override public void keyReleased(final KeyEvent e) { final short keySym = e.getKeySymbol(); - boolean gridScroll = false; - if( keySym == KeyEvent.VK_DOWN ) { - if( VERBOSE_UI ) { - System.err.println("Scroll.0: "+gridDim); - } - gridScroll = true; - gridDim.lineDown(); - } else if( keySym == KeyEvent.VK_PAGE_DOWN ) { - if( VERBOSE_UI ) { - System.err.println("Scroll.0: "+gridDim); - } - gridScroll = true; - gridDim.pageDown(); - } else if( keySym == KeyEvent.VK_UP ) { - if( VERBOSE_UI ) { - System.err.println("Scroll.0: "+gridDim); - } - gridScroll = true; - gridDim.lineUp(); - } else if( keySym == KeyEvent.VK_PAGE_UP ) { - if( VERBOSE_UI ) { - System.err.println("Scroll.0: "+gridDim); - } - gridScroll = true; - gridDim.pageUp(); - } else if( keySym == KeyEvent.VK_F4 || keySym == KeyEvent.VK_ESCAPE || keySym == KeyEvent.VK_Q ) { + if( keySym == KeyEvent.VK_F4 || keySym == KeyEvent.VK_ESCAPE || keySym == KeyEvent.VK_Q ) { MiscUtils.destroyWindow(window); } - if( gridScroll ) { - window.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - glyphGrid.removeAllShapes(drawable.getGL().getGL2ES2(), scene.getRenderer()); - addGlyphs(reqGLP, font, glyphGrid, gridDim, showUnderline[0], showLabel[0], fontStatus, fontInfo, glyphMouseListener); - if( VERBOSE_UI ) { - System.err.println("Scroll.X: "+gridDim); - } - return true; - } - }); - } } }); @@ -447,33 +419,13 @@ public class FontView01 { return contourChars.size(); } - void pageDown() { - if( nextPage < contourChars.size() - elemCount ) { - start = nextPage; - } else { - start = 0; - } - } - void pageUp() { - if( start >= elemCount ) { - start -= elemCount; - } else { - start = contourChars.size() - 1 - elemCount; - } - } - void lineDown() { - if( nextLine < contourChars.size() - columns ) { - start = nextLine; - } else { - start = 0; - } - } - void lineUp() { - if( start >= columns ) { - start -= columns; - } else { - start = contourChars.size() - 1 - columns; + void setStartRow(final int row) { + final int old = start; + final int np = row * columns; + if( np < contourChars.size() - columns ) { + start = np; } + // System.err.println("XXX "+columns+"x"+rows+" @ "+old+"/"+contourChars.size()+": r "+row+" -> s "+start); } @Override diff --git a/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java b/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java index 3fc48306a..799a9e8ad 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java +++ b/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java @@ -128,10 +128,14 @@ public class MediaPlayer extends Widget { mButton.setName("mp.mButton").setInteractive(false); mButton.setPerp().setPressedColorMod(1f, 1f, 1f, 0.85f); - final RangeSlider ctrlSlider = new RangeSlider(renderModes, aratio, ctrlSliderHeight, 4.0f, 0, 100, 0); + final RangeSlider ctrlSlider; { - final float dy = ( ctrlSlider.getKnobSize() - ctrlSliderHeight ) * 0.5f; - ctrlSlider.setPaddding(new Padding(0, 0, ctrlCellHeight-dy, 0)); + final float knobScale = 3f; + final float knobHeight = ctrlSliderHeight * knobScale; + ctrlSlider = new RangeSlider(renderModes, new Vec2f(aratio - knobHeight, ctrlSliderHeight), knobScale, new Vec2f(0, 100), 0); + final float dx = knobHeight / 2f; + final float dy = ( knobHeight - ctrlSliderHeight ) * 0.5f; + ctrlSlider.setPaddding(new Padding(0, dx, ctrlCellHeight-dy, dx)); } ctrlSlider.setName("mp.slider"); @@ -153,7 +157,7 @@ public class MediaPlayer extends Widget { // System.err.println("MediaButton State: "+mp); if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Init) ) { System.err.println(mp.toString()); - ctrlSlider.setMinMax(0, mp.getDuration(), 0); + ctrlSlider.setMinMax(new Vec2f(0, mp.getDuration()), 0); } else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Play) ) { playButton.setToggle(true); } else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Pause) ) { 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 495d81149..f6498974e 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java +++ b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangeSlider.java @@ -32,20 +32,26 @@ import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.ui.Group; import com.jogamp.graph.ui.Shape; +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.Vec2f; import com.jogamp.math.Vec3f; import com.jogamp.math.Vec4f; +import com.jogamp.newt.event.KeyAdapter; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseEvent; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.util.texture.TextureSequence; /** - * RangeSlider {@link Widget} - * @see #RangeSlider(int, float, float, float, float, float, float) + * RangeSlider {@link Widget} either utilizing a simple positional round knob + * or a rectangular page-sized knob. + * @see #RangeSlider(int, Vec2f, float, Vec2f, float) + * @see #RangeSlider(int, Vec2f, Vec2f, float, float) */ public final class RangeSlider extends Widget { /** @@ -59,7 +65,7 @@ public final class RangeSlider extends Widget { /** Slider released down by user. */ void released(RangeSlider w, final MouseEvent e); /** - * Slide dragged by user + * Slide dragged by user (including clicked position) * @param w the {@link RangeSlider} widget owning the slider * @param old_val previous absolute value position of the slider * @param val the absolute value position of the slider @@ -69,60 +75,115 @@ public final class RangeSlider extends Widget { void dragged(RangeSlider w, float old_val, float val, float old_val_pct, float val_pct); } + 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; - private final float knobSz; - private final float width, height; + /** Width of knob orthogonal to sliding direction */ + private float knobDiameter; + /** Half length of knob in sliding direction */ + private float knobHalfLen; + private final Vec2f size; private final Group barAndKnob; private final Rectangle bar; - private final BaseButton knob; - private final Vec4f colBar = new Vec4f(0, 0, 1, 1); - private final Vec4f colKnob = new Vec4f(1, 0, 0, 1); + private final Shape knob; private SliderListener sliderListener = null; - private float min, max; - private float val, val_pct; + private final Vec2f minMax = new Vec2f(0, 100); + private float pageSize; + private float val=0, val_pct=0; + private boolean inverted=false; /** * Constructs a {@link RangeSlider}, i.e. its shapes and controls. + * <p> + * This slider comprises a background bar and a positional round knob. + * </p> * @param renderModes Graph's {@link Region} render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}. - * @param width width of this slider box. A horizontal slider has width >= height. - * @param height height of this slider box. A vertical slider has width < height. - * @param knobScale multiple of slider-bar height for {@link #getKnobSize()} - * @param min minimum value of slider - * @param max maximum value of slider + * @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 minMax minimum- and maximum-value of slider * @param value current value of slider */ - public RangeSlider(final int renderModes, final float width, final float height, final float knobScale, - final float min, final float max, final float value) { - this.horizontal = width >= height; - if( horizontal ) { - knobSz = height*knobScale; - this.width = width - knobSz; // half knobSz left and right - this.height = height; + public RangeSlider(final int renderModes, final Vec2f size, final float knobScale, + final Vec2f minMax, final float value) { + this(renderModes, size, knobScale, minMax, Float.NaN, value); + } + /** + * Constructs a {@link RangeSlider}, i.e. its shapes and controls. + * <p> + * This slider comprises a framing bar and a rectangular page-sized knob. + * </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 minMax minimum- and maximum-value of slider + * @param pageSize size of one virtual-page, triggers rendering mode from knob to rectangle + * @param value current value of slider + */ + public RangeSlider(final int renderModes, final Vec2f size, + final Vec2f minMax, final float pageSize, final float value) { + this(renderModes, size, 0, minMax, pageSize, value); + } + private RangeSlider(final int renderModes_, final Vec2f size, final float knobScale, + final Vec2f minMax, final float pageSz, final float value) { + // final int renderModes = ( renderModes_ & ~Region.AA_RENDERING_MASK ) | Region.COLORCHANNEL_RENDERING_BIT; + final int renderModes = renderModes_ & ~(Region.AA_RENDERING_MASK | Region.COLORCHANNEL_RENDERING_BIT); + this.pageSize = pageSz; + this.horizontal = size.x() >= size.y(); + barAndKnob = new Group(); + barAndKnob.setInteractive(true).setToggleable(false).setDraggable(false).setResizable(false); + this.size = new Vec2f(size); + if( Float.isNaN(pageSize) ) { + if( horizontal ) { + knobDiameter = size.y()*knobScale; + setPaddding(new Padding(knobHalfLen, 0, knobHalfLen, 0)); + } else { + knobDiameter = size.x()*knobScale; + setPaddding(new Padding(0, knobHalfLen, 0, knobHalfLen)); + } + knobHalfLen = knobDiameter * 0.5f; + bar = new Rectangle(renderModes, this.size.x(), this.size.y(), 0); + knob = new BaseButton(renderModes , knobDiameter*1.01f, knobDiameter); + setBackgroundBarColor(0.60f, 0.60f, 0.60f, 0.5f); } else { - knobSz = width*knobScale; - this.width = width; - this.height = height - knobSz; // half knobSz bottom and top + final float pageSizePct = Math.max(pageKnobSizePctMin, pageSize / getRange(minMax)); + final float barLineWidth; + final float knobWidth, knobHeight; + if( horizontal ) { + knobHeight = size.y() * pageKnobScale; + knobWidth = pageSizePct * this.size.x(); + barLineWidth = ( size.y() - knobHeight ) * pageBarLineScale; + knobHalfLen = knobWidth * 0.5f; + knobDiameter = knobHeight; + setPaddding(new Padding(size.y(), 0, size.y(), 0)); + } else { + knobWidth = size.x() * pageKnobScale; + knobHeight = pageSizePct * this.size.y(); + barLineWidth = ( size.x() - knobWidth ) * pageBarLineScale; + knobHalfLen = knobHeight * 0.5f; + knobDiameter = knobWidth; + setPaddding(new Padding(0, size.x(), 0, size.x())); + // System.err.println("ZZZ minMax "+minMax+", pageSize "+pageSize+" "+(pageSizePct*100f)+"% -> "+knobHeight+"/"+this.size.y()); + } + bar = new Rectangle(renderModes, this.size.x(), this.size.y(), barLineWidth); + knob = new Rectangle(renderModes, knobWidth, knobHeight, 0); } - barAndKnob = new Group(); - barAndKnob.setInteractive(true).setDraggable(false).setToggleable(false); - bar = new Rectangle(renderModes & ~Region.AA_RENDERING_MASK, this.width, this.height, 0); - bar.setToggleable(false); - bar.setColor(colBar); - knob = new BaseButton(renderModes & ~Region.AA_RENDERING_MASK, knobSz*1.01f, knobSz); - knob.setToggleable(false); - knob.setColor(colKnob); + setColor(0.30f, 0.30f, 0.30f, 1.0f); + + bar.setToggleable(false).setInteractive(false); + bar.setDraggable(false).setResizable(false); + + knob.setToggleable(false).setResizable(false); setName(getName()); barAndKnob.addShape( bar ); barAndKnob.addShape( knob ); addShape(barAndKnob); - setMinMax(min, max, value); + setMinMax(minMax, value); knob.onMove((final Shape s, final Vec3f origin, final Vec3f dest) -> { final float old_val = val; final float old_val_pct = val_pct; - setValuePct( getKnobValuePct( dest.x(), dest.y(), knobSz*0.5f ) ); - // System.err.println("KnobMove "+getName()+": "+origin+" -> "+dest+": "+old_val+" -> "+val+", "+(old_val_pct*100f)+"% -> "+(val_pct*100f)+"%"); + setValuePct( getKnobValuePct( dest.x(), dest.y(), knobHalfLen ) ); if( null != sliderListener ) { sliderListener.dragged(this, old_val, val, old_val_pct, val_pct); } @@ -130,19 +191,61 @@ public final class RangeSlider extends Widget { barAndKnob.addMouseListener(new Shape.MouseGestureAdapter() { @Override public void mouseClicked(final MouseEvent e) { + final float old_val = val; + final float old_val_pct = val_pct; final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment(); setValuePct( getKnobValuePct( shapeEvent.objPos.x(), shapeEvent.objPos.y(), 0 ) ); if( null != sliderListener ) { + sliderListener.dragged(RangeSlider.this, old_val, val, old_val_pct, val_pct); sliderListener.clicked(RangeSlider.this, e); } } + @Override + public void mouseWheelMoved(final MouseEvent e) { + final float old_val = val; + final float old_val_pct = val_pct; + float v = old_val; + if( !e.isControlDown() ) { + if( e.getRotation()[1] < 0f ) { + if( inverted ) { + v++; + } else { + v--; + } + } else { + if( inverted ) { + v--; + } else { + v++; + } + } + } else if( !Float.isNaN(pageSize) ){ + if( e.getRotation()[1] < 0f ) { + if( inverted ) { + v+=pageSize; + } else { + v-=pageSize; + } + } else { + if( inverted ) { + v-=pageSize; + } else { + v+=pageSize; + } + } + } + setValue( v ); + if( null != sliderListener ) { + sliderListener.dragged(RangeSlider.this, old_val, val, old_val_pct, val_pct); + } + } }); knob.addMouseListener(new Shape.MouseGestureAdapter() { @Override public void mouseClicked(final MouseEvent e) { - if( null != sliderListener ) { - sliderListener.clicked(RangeSlider.this, e); - } + // if( null != sliderListener ) { + // sliderListener.clicked(RangeSlider.this, e); + // } } @Override public void mousePressed(final MouseEvent e) { @@ -156,11 +259,75 @@ public final class RangeSlider extends Widget { sliderListener.released(RangeSlider.this, e); } } + }); + final KeyListener keyListener = new KeyAdapter() { @Override - public void mouseWheelMoved(final MouseEvent e) { - // Support ? + public void keyReleased(final KeyEvent e) { + final float old_val = val; + final float old_val_pct = val_pct; + float v = old_val; + final short keySym = e.getKeySymbol(); + boolean action = false; + if( horizontal ) { + if( keySym == KeyEvent.VK_RIGHT ) { + action = true; + if( inverted ) { + v--; + } else { + v++; + } + } else if( keySym == KeyEvent.VK_LEFT ) { + action = true; + if( inverted ) { + v++; + } else { + v--; + } + } + } else { + if( keySym == KeyEvent.VK_DOWN ) { + action = true; + if( inverted ) { + v++; + } else { + v--; + } + } else if( keySym == KeyEvent.VK_UP ) { + action = true; + if( inverted ) { + v--; + } else { + v++; + } + } + } + if( !action && !Float.isNaN(pageSize) ) { + if( keySym == KeyEvent.VK_PAGE_DOWN ) { + action = true; + if( inverted ) { + v+=pageSize; + } else { + v-=pageSize; + } + } else if( keySym == KeyEvent.VK_PAGE_UP ) { + action = true; + if( inverted ) { + v-=pageSize; + } else { + v+=pageSize; + } + } + } + if( action ) { + setValue( v ); + if( null != sliderListener ) { + sliderListener.dragged(RangeSlider.this, old_val, val, old_val_pct, val_pct); + } + } } - }); + }; + barAndKnob.addKeyListener(keyListener); + knob.addKeyListener(keyListener); } @Override @@ -180,44 +347,64 @@ public final class RangeSlider extends Widget { } public Rectangle getBar() { return bar; } - public BaseButton getKnob() { return knob; } + public Shape getKnob() { return knob; } - public final float getWidth() { return width; } - public final float getHeight() { return height; } - public final float getKnobSize() { return knobSz; } + public final Vec2f getSize() { return size; } + /** Height of knob orthogonal to sliding direction */ + public final float getKnobHeight() { return knobDiameter; } + /** Length of knob in sliding direction */ + public final float getKnobLength() { return knobHalfLen*2f; } - public float getMin() { return min; } - public float getMax() { return max; } - public float getRange() { return max - min; } + public Vec2f getMinMax() { return minMax; } + public float getRange() { return minMax.y() - minMax.x(); } + private float getRange(final Vec2f minMax) { return minMax.y() - minMax.x(); } public float getValue() { return val; } 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 knobWidth, knobHeight; + if( horizontal ) { + knobHeight = size.y() * pageKnobScale; + knobWidth = pageSizePct * this.size.x(); + knobHalfLen = knobWidth * 0.5f; + knobDiameter = knobHeight; + } else { + knobWidth = size.x() * pageKnobScale; + knobHeight = pageSizePct * this.size.y(); + knobHalfLen = knobHeight * 0.5f; + knobDiameter = knobWidth; + } + ((Rectangle)knob).setDimension(knobWidth, knobHeight, 0); + return this; + } + public float getPageSize() { return this.pageSize; } + + public RangeSlider setInverted(final boolean v) { inverted = v; return setValue(val); } + /** * Sets slider value range and current value - * @param min minimum value of slider - * @param max maximum value of slider + * @param minMax minimum- and maximum-value of slider * @param value current value of slider * @return this instance of chaining */ - public RangeSlider setMinMax(final float min, final float max, final float value) { - this.min = min; - this.max = max; - this.val = Math.max(min, Math.min(max, value)); - this.val_pct = ( value - min ) / getRange(); - setKnob(); - return this; + public RangeSlider setMinMax(final Vec2f minMax, final float value) { + this.minMax.set(minMax); + return setValue( value ); } public RangeSlider setValuePct(final float v) { - val_pct = v; - val = min + ( val_pct * getRange() ); - setKnob(); - return this; + final float pct = Math.max(0.0f, Math.min(1.0f, v)); + return setValue( minMax.x() + ( pct * getRange() ) ); } public RangeSlider setValue(final float v) { - val = v; - val_pct = ( val - min ) / getRange(); + val = Math.max(minMax.x(), Math.min(minMax.y(), v)); + val_pct = ( val - minMax.x() ) / getRange(); setKnob(); return this; } @@ -226,27 +413,118 @@ public final class RangeSlider extends Widget { * Knob position reflects value on its center and ranges from zero to max. */ private Vec2f getKnobPos(final Vec2f posRes, final float val_pct) { + final float v = inverted ? 1f - val_pct : val_pct; if( horizontal ) { - posRes.setX( val_pct*width - knobSz*0.5f ); - posRes.setY( -( knobSz - height ) * 0.5f ); + posRes.setX( Math.max(0, Math.min(size.x(), v*size.x() - knobHalfLen)) ); + posRes.setY( -( knobDiameter - size.y() ) * 0.5f ); } else { - posRes.setX( -( knobSz - width ) * 0.5f ); - posRes.setY( val_pct*height - knobSz*0.5f ); + posRes.setX( -( knobDiameter - size.x() ) * 0.5f ); + posRes.setY( Math.max(0, Math.min(size.y() - 2*knobHalfLen, v*size.y() - knobHalfLen)) ); } return posRes; } private float getKnobValuePct(final float pos_x, final float pos_y, final float adjustment) { final float v; if( horizontal ) { - v = ( pos_x + adjustment ) / width; + v = ( pos_x + adjustment ) / size.x(); } else { - v = ( pos_y + adjustment ) / height; + v = ( pos_y + adjustment ) / size.y(); } - return Math.max(0.0f, Math.min(1.0f, v)); + return Math.max(0.0f, Math.min(1.0f, inverted ? 1f - v : v)); } private void setKnob() { final Vec2f pos = getKnobPos(new Vec2f(), val_pct); knob.moveTo(pos.x(), pos.y(), Button.DEFAULT_LABEL_ZOFFSET); } + + /** + * Sets the slider knob color. + * <p> + * If this slider comprises a rectangular page-sized knob, + * its rectangular frame also shares the same color. + * </p> + * <p> + * Base color w/o color channel, will be modulated w/ pressed- and toggle color + * </p> + * <p> + * Default RGBA value is 0.30f, 0.30f, 0.30f, 1.0f + * </p> + */ + @Override + 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) ) { + bar.setColor(r, g, b, a); + } + return this; + } + + /** + * Sets the slider knob color. + * <p> + * If this slider comprises a rectangular page-sized knob, + * its rectangular frame also shares the same color. + * </p> + * <p> + * Base color w/o color channel, will be modulated w/ pressed- and toggle color + * </p> + * <p> + * Default RGBA value is 0.30f, 0.30f, 0.30f, 1.0f + * </p> + */ + @Override + public Shape setColor(final Vec4f c) { + this.rgbaColor.set(c); + knob.setColor(c); + if( !Float.isNaN(pageSize) ) { + bar.setColor(c); + } + return this; + } + + /** + * Sets the slider background bar color, if this slider comprises only a positional round knob. + * <p> + * Default RGBA value is 0.60f, 0.60f, 0.60f, 0.5f + * </p> + */ + public Shape setBackgroundBarColor(final float r, final float g, final float b, final float a) { + if( Float.isNaN(pageSize) ) { + bar.setColor(r, g, b, a); + } + return this; + } + /** + * Sets the slider background bar color, if this slider comprises only a positional round knob. + * <p> + * Default RGBA value is 0.60f, 0.60f, 0.60f, 0.5f + * </p> + */ + public Shape setBackgroundBarColor(final Vec4f c) { + if( Float.isNaN(pageSize) ) { + bar.setColor(c); + } + return this; + } + + /** + * {@inheritDoc} + * <p> + * Sets the slider bar and knob pressed color modulation. + * </p> + */ + @Override + public final Shape setPressedColorMod(final float r, final float g, final float b, final float a) { + super.setPressedColorMod(r, g, b, a); + bar.setPressedColorMod(r, g, b, a); + knob.setPressedColorMod(r, g, b, a); + return this; + } + + @Override + public String getSubString() { + return super.getSubString()+", range["+minMax+"] @ "+val+", "+(100f*val_pct)+"%"; + } } |