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}