sclass JTargetAndActualSlider extends MetaWithChangeListeners is Swingable {
  new TargetAndActual<Double> data;
  settableWithVar bool showSlider = true;
  settableWithVar bool sliderEnabled = true;
  settableWithVar double min;
  settableWithVar double max = 100;
  settableWithVar int decimalsToShow = 1;
  settableWithVar int widthOfNumbers = 50;
  settableWithVar S unit;
  settableWithVar S description;
  settableWithVar int preferredBarWidth = 50;
  
  // set to false to use ints
  settable bool scaling = true;

  transient JColorBar bar;
  transient JSlider slider;
  transient JLabel lblActual, lblTarget;
  
  *() {}
  *(TargetAndActual<Double> *data) {}
  
  double value() { ret unnull(data.value()); }
  double target() { ret unnull(data.target()); }

  cachedVisualize {
    bar = swing(-> new JColorBar().max(max));
    jPreferWidth(bar, preferredBarWidth);
    if (showSlider) {
      slider = scaling
        ? jLiveValueSlider_double_bothWays(min, max, data.varTarget())
        : jLiveValueSlider_double_noScaling(iround(min), iround(max), data.varTarget());
      jPreferWidth(slider, preferredBarWidth);
    }
    lblActual = jlabel();
    lblTarget = jlabel();
    data.varActual().onChangeAndNow(-> {
      bar.setValue(value());
      setText(lblActual, renderValue(value()));
    });
    data.varTarget().onChangeAndNow(-> {
      setText(lblTarget, renderValue(target()));
    });
      
    varSliderEnabled().onChangeAndNow(-> setEnabled(slider, sliderEnabled));
    
    ret vstack/*WithSpacing*/(
      slider == null ?: westCenterAndEastWithMargin(
        jlabel(spaceCombine("Target", description)), slider,
        jMinWidth(widthOfNumbers, lblTarget)),
      westCenterAndEastWithMargin(
        jlabel(spaceCombine("Actual", description)), bar,
        jMinWidth(widthOfNumbers, lblActual)));
  }
  
  swappable S renderValue(double value) {
    ret spaceCombine(!scaling ? n2(iround(value)) : formatDoubleX(value, decimalsToShow), unit);
  }
  
  JSlider slider() {
    visualize();
    ret slider;
  }
  
  selfType target(double target) {
    data.target(target);
    this;
  }
  
  selfType value(double value) {
    data.value(value);
    this;
  }
}