Vuo  2.0.0
VuoInputEditorRange.cc
Go to the documentation of this file.
1 
10 #include "VuoInputEditorRange.hh"
11 
16 {
17  return new VuoInputEditorRange();
18 }
19 
23 QLabel* makeLabel(const char* text)
24 {
25  QLabel* label = new QLabel(text);
26  label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
27  return label;
28 }
29 
33 VuoDoubleSpinBox* VuoInputEditorRange::initSpinBox(QDialog& dialog, double initialValue, double min, double max, double step)
34 {
35  VuoDoubleSpinBox* spin = new VuoDoubleSpinBox(&dialog, 11);
36  const int decimalPrecision = DBL_MAX_10_EXP + DBL_DIG;
37  spin->setDecimals(decimalPrecision);
38  spin->setButtonMinimum(min);
39  spin->setButtonMaximum(max);
40  spin->setSingleStep(step);
41  spin->setValue(initialValue);
42  spin->setFixedWidth(100);
43  return spin;
44 }
45 
49 QSlider* VuoInputEditorRange::initSlider(QSlider* slider, double initialValue, double min, double max, double step)
50 {
51  double range = max - min;
52  slider->setOrientation(Qt::Horizontal);
53  slider->setFocusPolicy(Qt::NoFocus);
54  slider->setMinimum(0);
55  slider->setMaximum(slider->width());
56  slider->setSingleStep( fmax(1, step * (slider->maximum() - slider->minimum()) / range ) );
57  slider->setValue( VuoDoubleSpinBox::doubleToSlider(slider->minimum(), slider->maximum(), min, max, initialValue) );
58  return slider;
59 }
60 
65 void VuoInputEditorRange::setUpDialog(QDialog &dialog, json_object *originalValue, json_object *details)
66 {
67  current = VuoRange_makeFromJson(originalValue);
68  lastValue = current;
69 
70  const double max_dbl = std::numeric_limits<double>::max();
71 
72  suggestedMin = VuoRange_make(-max_dbl, -max_dbl);
73  suggestedMax = VuoRange_make(max_dbl, max_dbl);
74  suggestedStep = VuoRange_make(0.1, 0.1);
75 
76  bool hasMinMax = true;
77 
78  requireMin = false;
79  requireMax = false;
80 
81  // Parse supported port annotations from the port's "details" JSON object:
82  if (details)
83  {
84  // "suggestedMin"
85  json_object *suggestedMinValue = NULL;
86  if (json_object_object_get_ex(details, "suggestedMin", &suggestedMinValue))
87  suggestedMin = VuoRange_makeFromJson(suggestedMinValue);
88  else
89  hasMinMax = false;
90 
91  // "suggestedMax"
92  json_object *suggestedMaxValue = NULL;
93  if (json_object_object_get_ex(details, "suggestedMax", &suggestedMaxValue))
94  suggestedMax = VuoRange_makeFromJson(suggestedMaxValue);
95  else
96  hasMinMax = false;
97 
98  // "suggestedStep"
99  json_object *suggestedStepValue = NULL;
100  if (json_object_object_get_ex(details, "suggestedStep", &suggestedStepValue))
101  suggestedStep = VuoRange_makeFromJson(suggestedStepValue);
102 
103  // requireMin
104  json_object *requireMinValue = NULL;
105  if (json_object_object_get_ex(details, "requireMin", &requireMinValue))
106  requireMin = VuoBoolean_makeFromJson(requireMinValue);
107 
108  // requireMax
109  json_object *requireMaxValue = NULL;
110  if (json_object_object_get_ex(details, "requireMax", &requireMaxValue))
111  requireMax = VuoBoolean_makeFromJson(requireMaxValue);
112  }
113 
114  spinbox_min = initSpinBox(dialog, current.minimum, suggestedMin.minimum, suggestedMax.minimum, suggestedStep.minimum);
115  {
116  spinbox_min->setMinimum(VuoRange_NoMinimum);
117 // spinbox_min->setMaximum(current.maximum);
118  spinbox_min->setMaximum(VuoRange_NoMaximum);
119  spinbox_min->setSpecialValueText("-∞");
120  connect(spinbox_min, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorRange::onSpinboxUpdate);
121  }
122 
123  spinbox_max = initSpinBox(dialog, current.maximum, suggestedMin.maximum, suggestedMax.maximum, suggestedStep.maximum);
124  {
125  // QDoubleSpinbox only can show the special string value if value == minimum
126  // So if the max value is VuoRange_NoMaximum temporarily make the min/max both
127  // VuoRange_NoMaximum (DBL_MAX)
128 // if(current.maximum == VuoRange_NoMaximum)
129 // {
130 // spinbox_max->setMinimum(VuoRange_NoMaximum);
131 // spinbox_max->setMaximum(VuoRange_NoMaximum);
132 // }
133 // else
134 // {
135 // spinbox_max->setMinimum(current.minimum);
136 // spinbox_max->setMaximum(VuoRange_NoMaximum);
137 // }
138 
139  connect(spinbox_max, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorRange::onSpinboxUpdate);
140  }
141 
142  if(hasMinMax)
143  {
144  slider_min = new QSlider(&dialog);
145  slider_max = new QSlider(&dialog);
146 
147  slider_min = initSlider(slider_min, current.minimum, suggestedMin.minimum, suggestedMax.minimum, suggestedStep.minimum);
148  slider_max = initSlider(slider_max, current.maximum, suggestedMin.maximum, suggestedMax.maximum, suggestedStep.maximum);
149 
150  connect(slider_min, &QSlider::valueChanged, this, &VuoInputEditorRange::onSliderUpdate);
151  connect(slider_max, &QSlider::valueChanged, this, &VuoInputEditorRange::onSliderUpdate);
152  }
153 
154  // layout dialog
155  QGridLayout* layout = new QGridLayout;
156  dialog.setLayout(layout);
157 
158  // left, top, right, bottom
159  // when showing sliders, add a little extra margin on the bottom since QSlider takes up the
160  // entire vertical spacing
161  layout->setContentsMargins(4, 4, 12, 4);
162  layout->setSpacing(4);
163  layout->setHorizontalSpacing(8);
164 
165  int row = 0;
166 
169  if(!requireMin)
170  {
171  toggle_hasMin = new QCheckBox("Minimum");
172  toggle_hasMin->setChecked( !VuoReal_areEqual(current.minimum, VuoRange_NoMinimum) );
173  toggle_hasMin->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
174  connect(toggle_hasMin, &QCheckBox::stateChanged, this, &VuoInputEditorRange::setHasMinBound);
175  layout->addWidget(toggle_hasMin, row, 0);
176  setHasMinBound(toggle_hasMin->isChecked() ? Qt::Checked : Qt::Unchecked);
177  }
178  else
179  {
180  layout->addWidget(makeLabel("Minimum"), row, 0);
181  }
182 
183  spinbox_min->setEnabled(toggle_hasMin == NULL || toggle_hasMin->isChecked());
184  layout->addWidget(spinbox_min, row++, 1);
185 
186  if(hasMinMax)
187  {
188  slider_min->setEnabled(toggle_hasMin == NULL || toggle_hasMin->isChecked());
189  layout->addWidget(slider_min, row++, 1);
190  }
192 
195  if(!requireMax)
196  {
197  toggle_hasMax = new QCheckBox("Maximum");
198  toggle_hasMax->setChecked( !VuoReal_areEqual(current.maximum, VuoRange_NoMaximum) );
199  toggle_hasMax->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
200  connect(toggle_hasMax, &QCheckBox::stateChanged, this, &VuoInputEditorRange::setHasMaxBound);
201  layout->addWidget(toggle_hasMax, row, 0);
202  setHasMaxBound(toggle_hasMax->isChecked() ? Qt::Checked : Qt::Unchecked);
203  }
204  else
205  {
206  layout->addWidget(makeLabel("Maximum"), row, 0);
207  }
208 
209  spinbox_max->setEnabled(toggle_hasMax == NULL || toggle_hasMax->isChecked());
210  layout->addWidget(spinbox_max, row++, 1);
211 
212  if(hasMinMax)
213  {
214  slider_max->setEnabled(toggle_hasMax == NULL || toggle_hasMax->isChecked());
215  layout->addWidget(slider_max, row, 1);
216  }
218 
219  dialog.setMaximumWidth(1);
220  dialog.setMaximumHeight(1);
221  dialog.adjustSize();
222 
223  setFirstWidgetInTabOrder(spinbox_min);
224  setLastWidgetInTabOrder(spinbox_max);
225 
226  spinbox_min->setFocus();
227 }
228 
229 void VuoInputEditorRange::setHasMinBound(int state)
230 {
231  if(state == Qt::Unchecked)
232  {
233  // had min bound, now we don't
234  lastValue.minimum = current.minimum;
235  current.minimum = VuoRange_NoMinimum;
236  }
237  else
238  {
239  // was unbound, now has bound
240  if( VuoReal_areEqual(VuoRange_NoMinimum, lastValue.minimum) )
241  current.minimum = fmin(fmax(suggestedMin.minimum, 0.), current.maximum);
242  else
243  current.minimum = lastValue.minimum;
244  }
245 
246  spinbox_min->setValue(current.minimum);
247 
248  spinbox_min->setEnabled(state != Qt::Unchecked);
249  if(slider_min) slider_min->setEnabled(state != Qt::Unchecked);
250 }
251 
252 void VuoInputEditorRange::setHasMaxBound(int state)
253 {
254  if(state == Qt::Unchecked)
255  {
256  lastValue.maximum = current.maximum;
257  current.maximum = VuoRange_NoMaximum;
258  spinbox_max->setSpecialValueText("∞");
259  spinbox_max->setMinimum(VuoRange_NoMaximum);
260  spinbox_max->setValue(current.maximum);
261  spinbox_max->setEnabled(false);
262  if(slider_max) slider_max->setEnabled(false);
263  }
264  else
265  {
266  if(lastValue.maximum == VuoRange_NoMaximum)
267  current.maximum = fmax(fmin(suggestedMax.maximum, 1), current.minimum);
268  else
269  current.maximum = lastValue.maximum;
270 
271  spinbox_max->setSpecialValueText("");
272  spinbox_max->setMinimum(current.minimum);
273  spinbox_max->setValue(current.maximum);
274  spinbox_max->setEnabled(true);
275  if(slider_max) slider_max->setEnabled(true);
276  }
277 }
278 
279 void VuoInputEditorRange::setMinimumValue(double value)
280 {
281  current.minimum = value;
282 // spinbox_max->setMinimum(current.minimum);
283 }
284 
285 void VuoInputEditorRange::setMaximumValue(double value)
286 {
287  current.maximum = value;
288 // spinbox_min->setMaximum(current.maximum);
289 }
290 
294 void VuoInputEditorRange::onSliderUpdate(int sliderValue)
295 {
296  if(QObject::sender() == NULL)
297  return;
298 
299  bool isMin = (QObject::sender() == spinbox_min || QObject::sender() == slider_min);
300 
301  QSlider* slider = isMin ? slider_min : slider_max;
302 
303  double val = VuoDoubleSpinBox::sliderToDouble( slider->minimum(),
304  slider->maximum(),
305  isMin ? suggestedMin.minimum : suggestedMin.maximum,
306  isMin ? suggestedMax.minimum : suggestedMax.maximum,
307  sliderValue);
308  if(isMin)
309  setMinimumValue(val);
310  else
311  setMaximumValue(val);
312 
313  VuoDoubleSpinBox* spinbox = isMin ? spinbox_min : spinbox_max;
314 
315  // disconnect before setting spinbox value otherwise onSpinboxUpdate is called and the whole thing just cycles
316  disconnect(spinbox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorRange::onSpinboxUpdate);
317  spinbox->setValue( isMin ? current.minimum : current.maximum );
318  connect(spinbox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorRange::onSpinboxUpdate);
319 
320  spinbox->setFocus();
321  spinbox->selectAll();
322 
323  emit valueChanged( getAcceptedValue() );
324 }
325 
329 void VuoInputEditorRange::onSpinboxUpdate(double value)
330 {
331  if(QObject::sender() == NULL)
332  return;
333 
334  bool isMin = (QObject::sender() == spinbox_min || QObject::sender() == slider_min);
335 
336  if(isMin)
337  setMinimumValue(value);
338  else
339  setMaximumValue(value);
340 
341  QSlider* slider = isMin ? slider_min : slider_max;
342 
343  if(slider != NULL)
344  {
345  int sliderValue = VuoDoubleSpinBox::doubleToSlider(
346  slider->minimum(),
347  slider->maximum(),
348  isMin ? suggestedMin.minimum : suggestedMin.maximum,
349  isMin ? suggestedMax.minimum : suggestedMax.maximum,
350  value);
351 
352  disconnect(slider, &QSlider::valueChanged, this, &VuoInputEditorRange::onSliderUpdate);
353  slider->setValue( sliderValue );
354  connect(slider, &QSlider::valueChanged, this, &VuoInputEditorRange::onSliderUpdate);
355  }
356 
357  emit valueChanged( getAcceptedValue() );
358 }
359 
364 {
365  return VuoRange_getJson(current);
366 }