001package polaris.sdk.ui.controls;
002import polaris.sdk.ui.*;
003
004/** Drag either endpoint, or drag the selected interval as a whole without changing its span. */
005public class RangeSlider extends Slider {
006 private double upper=75,grabLow,grabHigh;private boolean upperActive,wholeRange;
007 private float grabX,shownLow=.25f,shownHigh=.75f;private boolean painted;
008 public RangeSlider(){value=25;}
009 public RangeSlider values(double low,double high){if(!Double.isFinite(low)||!Double.isFinite(high)||low>high)throw new IllegalArgumentException("range values");value(low);upper=Math.max(value,Math.min(max,Math.max(min,high)));return this;}
010 @Override public RangeSlider range(double a,double b){super.range(a,b);upper=Math.max(value,Math.min(max,upper));return this;}
011 public double low(){return value;}public double high(){return upper;}
012 @Override protected void setFromPointer(float px){
013  double oldLow=value,oldHigh=upper;
014  if(wholeRange){double delta=(px-grabX)/trackLength()*(max-min);if(snapToStep)delta=Math.round(delta/step)*step;
015   delta=Math.max(min-grabLow,Math.min(max-grabHigh,delta));value=grabLow+delta;upper=grabHigh+delta;
016  }else{double v=pointerValue(px);if(upperActive)upper=Math.max(value,v);else value=Math.min(upper,v);}
017  if(oldLow!=value||oldHigh!=upper)changed();
018 }
019 @Override public void event(UiEvent e){
020  if(e.type==UiEvent.Type.DOWN&&e.button==1){
021   downX=e.x;pointerMoved=false;
022   float x1=trackStart()+trackLength()*(painted?shownLow:fraction()),x2=trackStart()+trackLength()*(painted?shownHigh:(float)((upper-min)/(max-min)));
023   float reach=thumbSize()/2+3;boolean nearLow=Math.abs(e.x-x1)<=reach,nearHigh=Math.abs(e.x-x2)<=reach;
024   wholeRange=!nearLow&&!nearHigh&&e.x>x1&&e.x<x2;
025   upperActive=Math.abs(e.x-x2)<Math.abs(e.x-x1);pressed=true;
026   if(wholeRange){grabX=e.x;grabLow=value;grabHigh=upper;dragOffset=0;}
027   else{float selected=upperActive?x2:x1;boolean grabbed=Math.abs(e.x-selected)<=reach;dragOffset=grabbed?e.x-selected:0;if(!grabbed)setFromPointer(e.x);}
028   e.handled=true;return;
029  }
030  if(e.type==UiEvent.Type.KEY){
031   if(e.key==32){upperActive=!upperActive;e.handled=true;return;}
032   double oldLow=value,oldHigh=upper,delta=(e.key==37||e.key==40)?-step:(e.key==39||e.key==38)?step:0;
033   if(delta!=0){if(upperActive)upper=Math.max(value,Math.min(max,upper+delta));else value=Math.max(min,Math.min(upper,value+delta));}
034   else if(e.key==36){if(upperActive)upper=value;else value=min;}
035   else if(e.key==35){if(upperActive)upper=max;else value=upper;}else return;
036   if(oldLow!=value||oldHigh!=upper)changed();e.handled=true;return;
037  }
038  super.event(e);if(e.type==UiEvent.Type.UP||e.type==UiEvent.Type.BLUR)wholeRange=false;
039 }
040 @Override protected void paint(Painter p){
041  shownLow=MotionValue.clamp(follow("rangeLow",fraction(),70));shownHigh=MotionValue.clamp(follow("rangeHigh",(float)((upper-min)/(max-min)),70));painted=true;
042  drawTrack(p,shownLow,shownHigh);float pressure=pressAmount();
043  drawThumb(p,shownLow,wholeRange||!upperActive?pressure:0);drawThumb(p,shownHigh,wholeRange||upperActive?pressure:0);
044 }
045}