The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1452 lines
46KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. class Slider::PopupDisplayComponent : public BubbleComponent,
  21. public Timer
  22. {
  23. public:
  24. PopupDisplayComponent (Slider& owner_)
  25. : owner (owner_),
  26. font (15.0f, Font::bold)
  27. {
  28. setAlwaysOnTop (true);
  29. }
  30. void paintContent (Graphics& g, int w, int h)
  31. {
  32. g.setFont (font);
  33. g.setColour (findColour (TooltipWindow::textColourId, true));
  34. g.drawFittedText (text, 0, 0, w, h, Justification::centred, 1);
  35. }
  36. void getContentSize (int& w, int& h)
  37. {
  38. w = font.getStringWidth (text) + 18;
  39. h = (int) (font.getHeight() * 1.6f);
  40. }
  41. void updatePosition (const String& newText)
  42. {
  43. text = newText;
  44. BubbleComponent::setPosition (&owner);
  45. repaint();
  46. }
  47. void timerCallback()
  48. {
  49. owner.popupDisplay = nullptr;
  50. }
  51. private:
  52. Slider& owner;
  53. Font font;
  54. String text;
  55. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PopupDisplayComponent);
  56. };
  57. //==============================================================================
  58. Slider::Slider (const String& name)
  59. : Component (name),
  60. lastCurrentValue (0),
  61. lastValueMin (0),
  62. lastValueMax (0),
  63. minimum (0),
  64. maximum (10),
  65. interval (0),
  66. skewFactor (1.0),
  67. velocityModeSensitivity (1.0),
  68. velocityModeOffset (0.0),
  69. velocityModeThreshold (1),
  70. rotaryStart (float_Pi * 1.2f),
  71. rotaryEnd (float_Pi * 2.8f),
  72. numDecimalPlaces (7),
  73. sliderRegionStart (0),
  74. sliderRegionSize (1),
  75. sliderBeingDragged (-1),
  76. pixelsForFullDragExtent (250),
  77. style (LinearHorizontal),
  78. textBoxPos (TextBoxLeft),
  79. textBoxWidth (80),
  80. textBoxHeight (20),
  81. incDecButtonMode (incDecButtonsNotDraggable),
  82. editableText (true),
  83. doubleClickToValue (false),
  84. isVelocityBased (false),
  85. userKeyOverridesVelocity (true),
  86. rotaryStop (true),
  87. incDecButtonsSideBySide (false),
  88. sendChangeOnlyOnRelease (false),
  89. popupDisplayEnabled (false),
  90. menuEnabled (false),
  91. menuShown (false),
  92. scrollWheelEnabled (true),
  93. snapsToMousePos (true),
  94. parentForPopupDisplay (nullptr)
  95. {
  96. setWantsKeyboardFocus (false);
  97. setRepaintsOnMouseActivity (true);
  98. Slider::lookAndFeelChanged();
  99. updateText();
  100. currentValue.addListener (this);
  101. valueMin.addListener (this);
  102. valueMax.addListener (this);
  103. }
  104. Slider::~Slider()
  105. {
  106. currentValue.removeListener (this);
  107. valueMin.removeListener (this);
  108. valueMax.removeListener (this);
  109. popupDisplay = nullptr;
  110. }
  111. //==============================================================================
  112. void Slider::handleAsyncUpdate()
  113. {
  114. cancelPendingUpdate();
  115. Component::BailOutChecker checker (this);
  116. listeners.callChecked (checker, &SliderListener::sliderValueChanged, this); // (can't use Slider::Listener due to idiotic VC2005 bug)
  117. }
  118. void Slider::sendDragStart()
  119. {
  120. startedDragging();
  121. Component::BailOutChecker checker (this);
  122. listeners.callChecked (checker, &SliderListener::sliderDragStarted, this);
  123. }
  124. void Slider::sendDragEnd()
  125. {
  126. stoppedDragging();
  127. sliderBeingDragged = -1;
  128. Component::BailOutChecker checker (this);
  129. listeners.callChecked (checker, &SliderListener::sliderDragEnded, this);
  130. }
  131. void Slider::addListener (SliderListener* const listener)
  132. {
  133. listeners.add (listener);
  134. }
  135. void Slider::removeListener (SliderListener* const listener)
  136. {
  137. listeners.remove (listener);
  138. }
  139. //==============================================================================
  140. void Slider::setSliderStyle (const SliderStyle newStyle)
  141. {
  142. if (style != newStyle)
  143. {
  144. style = newStyle;
  145. repaint();
  146. lookAndFeelChanged();
  147. }
  148. }
  149. void Slider::setRotaryParameters (const float startAngleRadians,
  150. const float endAngleRadians,
  151. const bool stopAtEnd)
  152. {
  153. // make sure the values are sensible..
  154. jassert (rotaryStart >= 0 && rotaryEnd >= 0);
  155. jassert (rotaryStart < float_Pi * 4.0f && rotaryEnd < float_Pi * 4.0f);
  156. jassert (rotaryStart < rotaryEnd);
  157. rotaryStart = startAngleRadians;
  158. rotaryEnd = endAngleRadians;
  159. rotaryStop = stopAtEnd;
  160. }
  161. void Slider::setVelocityBasedMode (const bool velBased)
  162. {
  163. isVelocityBased = velBased;
  164. }
  165. void Slider::setVelocityModeParameters (const double sensitivity,
  166. const int threshold,
  167. const double offset,
  168. const bool userCanPressKeyToSwapMode)
  169. {
  170. jassert (threshold >= 0);
  171. jassert (sensitivity > 0);
  172. jassert (offset >= 0);
  173. velocityModeSensitivity = sensitivity;
  174. velocityModeOffset = offset;
  175. velocityModeThreshold = threshold;
  176. userKeyOverridesVelocity = userCanPressKeyToSwapMode;
  177. }
  178. void Slider::setSkewFactor (const double factor)
  179. {
  180. skewFactor = factor;
  181. }
  182. void Slider::setSkewFactorFromMidPoint (const double sliderValueToShowAtMidPoint)
  183. {
  184. if (maximum > minimum)
  185. skewFactor = log (0.5) / log ((sliderValueToShowAtMidPoint - minimum)
  186. / (maximum - minimum));
  187. }
  188. void Slider::setMouseDragSensitivity (const int distanceForFullScaleDrag)
  189. {
  190. jassert (distanceForFullScaleDrag > 0);
  191. pixelsForFullDragExtent = distanceForFullScaleDrag;
  192. }
  193. void Slider::setIncDecButtonsMode (const IncDecButtonMode mode)
  194. {
  195. if (incDecButtonMode != mode)
  196. {
  197. incDecButtonMode = mode;
  198. lookAndFeelChanged();
  199. }
  200. }
  201. void Slider::setTextBoxStyle (const TextEntryBoxPosition newPosition,
  202. const bool isReadOnly,
  203. const int textEntryBoxWidth,
  204. const int textEntryBoxHeight)
  205. {
  206. if (textBoxPos != newPosition
  207. || editableText != (! isReadOnly)
  208. || textBoxWidth != textEntryBoxWidth
  209. || textBoxHeight != textEntryBoxHeight)
  210. {
  211. textBoxPos = newPosition;
  212. editableText = ! isReadOnly;
  213. textBoxWidth = textEntryBoxWidth;
  214. textBoxHeight = textEntryBoxHeight;
  215. repaint();
  216. lookAndFeelChanged();
  217. }
  218. }
  219. void Slider::setTextBoxIsEditable (const bool shouldBeEditable)
  220. {
  221. editableText = shouldBeEditable;
  222. if (valueBox != nullptr)
  223. valueBox->setEditable (shouldBeEditable && isEnabled());
  224. }
  225. void Slider::showTextBox()
  226. {
  227. jassert (editableText); // this should probably be avoided in read-only sliders.
  228. if (valueBox != nullptr)
  229. valueBox->showEditor();
  230. }
  231. void Slider::hideTextBox (const bool discardCurrentEditorContents)
  232. {
  233. if (valueBox != nullptr)
  234. {
  235. valueBox->hideEditor (discardCurrentEditorContents);
  236. if (discardCurrentEditorContents)
  237. updateText();
  238. }
  239. }
  240. void Slider::setChangeNotificationOnlyOnRelease (const bool onlyNotifyOnRelease)
  241. {
  242. sendChangeOnlyOnRelease = onlyNotifyOnRelease;
  243. }
  244. void Slider::setSliderSnapsToMousePosition (const bool shouldSnapToMouse)
  245. {
  246. snapsToMousePos = shouldSnapToMouse;
  247. }
  248. void Slider::setPopupDisplayEnabled (const bool enabled, Component* const parentComponentToUse)
  249. {
  250. popupDisplayEnabled = enabled;
  251. parentForPopupDisplay = parentComponentToUse;
  252. }
  253. Component* Slider::getCurrentPopupDisplay() const noexcept
  254. {
  255. return popupDisplay.get();
  256. }
  257. //==============================================================================
  258. void Slider::colourChanged()
  259. {
  260. lookAndFeelChanged();
  261. }
  262. void Slider::lookAndFeelChanged()
  263. {
  264. LookAndFeel& lf = getLookAndFeel();
  265. if (textBoxPos != NoTextBox)
  266. {
  267. const String previousTextBoxContent (valueBox != nullptr ? valueBox->getText()
  268. : getTextFromValue (currentValue.getValue()));
  269. valueBox = nullptr;
  270. addAndMakeVisible (valueBox = getLookAndFeel().createSliderTextBox (*this));
  271. valueBox->setWantsKeyboardFocus (false);
  272. valueBox->setText (previousTextBoxContent, false);
  273. if (valueBox->isEditable() != editableText) // (avoid overriding the single/double click flags unless we have to)
  274. valueBox->setEditable (editableText && isEnabled());
  275. valueBox->addListener (this);
  276. if (style == LinearBar)
  277. valueBox->addMouseListener (this, false);
  278. else
  279. valueBox->setTooltip (getTooltip());
  280. }
  281. else
  282. {
  283. valueBox = nullptr;
  284. }
  285. if (style == IncDecButtons)
  286. {
  287. addAndMakeVisible (incButton = lf.createSliderButton (true));
  288. incButton->addListener (this);
  289. addAndMakeVisible (decButton = lf.createSliderButton (false));
  290. decButton->addListener (this);
  291. if (incDecButtonMode != incDecButtonsNotDraggable)
  292. {
  293. incButton->addMouseListener (this, false);
  294. decButton->addMouseListener (this, false);
  295. }
  296. else
  297. {
  298. incButton->setRepeatSpeed (300, 100, 20);
  299. incButton->addMouseListener (decButton, false);
  300. decButton->setRepeatSpeed (300, 100, 20);
  301. decButton->addMouseListener (incButton, false);
  302. }
  303. incButton->setTooltip (getTooltip());
  304. decButton->setTooltip (getTooltip());
  305. }
  306. else
  307. {
  308. incButton = nullptr;
  309. decButton = nullptr;
  310. }
  311. setComponentEffect (lf.getSliderEffect());
  312. resized();
  313. repaint();
  314. }
  315. //==============================================================================
  316. void Slider::setRange (const double newMin,
  317. const double newMax,
  318. const double newInt)
  319. {
  320. if (minimum != newMin
  321. || maximum != newMax
  322. || interval != newInt)
  323. {
  324. minimum = newMin;
  325. maximum = newMax;
  326. interval = newInt;
  327. // figure out the number of DPs needed to display all values at this
  328. // interval setting.
  329. numDecimalPlaces = 7;
  330. if (newInt != 0)
  331. {
  332. int v = abs ((int) (newInt * 10000000));
  333. while ((v % 10) == 0)
  334. {
  335. --numDecimalPlaces;
  336. v /= 10;
  337. }
  338. }
  339. // keep the current values inside the new range..
  340. if (style != TwoValueHorizontal && style != TwoValueVertical)
  341. {
  342. setValue (getValue(), false, false);
  343. }
  344. else
  345. {
  346. setMinValue (getMinValue(), false, false);
  347. setMaxValue (getMaxValue(), false, false);
  348. }
  349. updateText();
  350. }
  351. }
  352. void Slider::triggerChangeMessage (const bool synchronous)
  353. {
  354. if (synchronous)
  355. handleAsyncUpdate();
  356. else
  357. triggerAsyncUpdate();
  358. valueChanged();
  359. }
  360. void Slider::valueChanged (Value& value)
  361. {
  362. if (value.refersToSameSourceAs (currentValue))
  363. {
  364. if (style != TwoValueHorizontal && style != TwoValueVertical)
  365. setValue (currentValue.getValue(), false, false);
  366. }
  367. else if (value.refersToSameSourceAs (valueMin))
  368. setMinValue (valueMin.getValue(), false, false, true);
  369. else if (value.refersToSameSourceAs (valueMax))
  370. setMaxValue (valueMax.getValue(), false, false, true);
  371. }
  372. double Slider::getValue() const
  373. {
  374. // for a two-value style slider, you should use the getMinValue() and getMaxValue()
  375. // methods to get the two values.
  376. jassert (style != TwoValueHorizontal && style != TwoValueVertical);
  377. return currentValue.getValue();
  378. }
  379. void Slider::setValue (double newValue,
  380. const bool sendUpdateMessage,
  381. const bool sendMessageSynchronously)
  382. {
  383. // for a two-value style slider, you should use the setMinValue() and setMaxValue()
  384. // methods to set the two values.
  385. jassert (style != TwoValueHorizontal && style != TwoValueVertical);
  386. newValue = constrainedValue (newValue);
  387. if (style == ThreeValueHorizontal || style == ThreeValueVertical)
  388. {
  389. jassert ((double) valueMin.getValue() <= (double) valueMax.getValue());
  390. newValue = jlimit ((double) valueMin.getValue(),
  391. (double) valueMax.getValue(),
  392. newValue);
  393. }
  394. if (newValue != lastCurrentValue)
  395. {
  396. if (valueBox != nullptr)
  397. valueBox->hideEditor (true);
  398. lastCurrentValue = newValue;
  399. // (need to do this comparison because the Value will use equalsWithSameType to compare
  400. // the new and old values, so will generate unwanted change events if the type changes)
  401. if (currentValue != newValue)
  402. currentValue = newValue;
  403. updateText();
  404. repaint();
  405. if (popupDisplay != nullptr)
  406. popupDisplay->updatePosition (getTextFromValue (newValue));
  407. if (sendUpdateMessage)
  408. triggerChangeMessage (sendMessageSynchronously);
  409. }
  410. }
  411. double Slider::getMinValue() const
  412. {
  413. // The minimum value only applies to sliders that are in two- or three-value mode.
  414. jassert (style == TwoValueHorizontal || style == TwoValueVertical
  415. || style == ThreeValueHorizontal || style == ThreeValueVertical);
  416. return valueMin.getValue();
  417. }
  418. double Slider::getMaxValue() const
  419. {
  420. // The maximum value only applies to sliders that are in two- or three-value mode.
  421. jassert (style == TwoValueHorizontal || style == TwoValueVertical
  422. || style == ThreeValueHorizontal || style == ThreeValueVertical);
  423. return valueMax.getValue();
  424. }
  425. void Slider::setMinValue (double newValue, const bool sendUpdateMessage, const bool sendMessageSynchronously, const bool allowNudgingOfOtherValues)
  426. {
  427. // The minimum value only applies to sliders that are in two- or three-value mode.
  428. jassert (style == TwoValueHorizontal || style == TwoValueVertical
  429. || style == ThreeValueHorizontal || style == ThreeValueVertical);
  430. newValue = constrainedValue (newValue);
  431. if (style == TwoValueHorizontal || style == TwoValueVertical)
  432. {
  433. if (allowNudgingOfOtherValues && newValue > (double) valueMax.getValue())
  434. setMaxValue (newValue, sendUpdateMessage, sendMessageSynchronously);
  435. newValue = jmin ((double) valueMax.getValue(), newValue);
  436. }
  437. else
  438. {
  439. if (allowNudgingOfOtherValues && newValue > lastCurrentValue)
  440. setValue (newValue, sendUpdateMessage, sendMessageSynchronously);
  441. newValue = jmin (lastCurrentValue, newValue);
  442. }
  443. if (lastValueMin != newValue)
  444. {
  445. lastValueMin = newValue;
  446. valueMin = newValue;
  447. repaint();
  448. if (popupDisplay != nullptr)
  449. popupDisplay->updatePosition (getTextFromValue (newValue));
  450. if (sendUpdateMessage)
  451. triggerChangeMessage (sendMessageSynchronously);
  452. }
  453. }
  454. void Slider::setMaxValue (double newValue, const bool sendUpdateMessage, const bool sendMessageSynchronously, const bool allowNudgingOfOtherValues)
  455. {
  456. // The maximum value only applies to sliders that are in two- or three-value mode.
  457. jassert (style == TwoValueHorizontal || style == TwoValueVertical
  458. || style == ThreeValueHorizontal || style == ThreeValueVertical);
  459. newValue = constrainedValue (newValue);
  460. if (style == TwoValueHorizontal || style == TwoValueVertical)
  461. {
  462. if (allowNudgingOfOtherValues && newValue < (double) valueMin.getValue())
  463. setMinValue (newValue, sendUpdateMessage, sendMessageSynchronously);
  464. newValue = jmax ((double) valueMin.getValue(), newValue);
  465. }
  466. else
  467. {
  468. if (allowNudgingOfOtherValues && newValue < lastCurrentValue)
  469. setValue (newValue, sendUpdateMessage, sendMessageSynchronously);
  470. newValue = jmax (lastCurrentValue, newValue);
  471. }
  472. if (lastValueMax != newValue)
  473. {
  474. lastValueMax = newValue;
  475. valueMax = newValue;
  476. repaint();
  477. if (popupDisplay != nullptr)
  478. popupDisplay->updatePosition (getTextFromValue (valueMax.getValue()));
  479. if (sendUpdateMessage)
  480. triggerChangeMessage (sendMessageSynchronously);
  481. }
  482. }
  483. void Slider::setMinAndMaxValues (double newMinValue, double newMaxValue, bool sendUpdateMessage, bool sendMessageSynchronously)
  484. {
  485. // The maximum value only applies to sliders that are in two- or three-value mode.
  486. jassert (style == TwoValueHorizontal || style == TwoValueVertical
  487. || style == ThreeValueHorizontal || style == ThreeValueVertical);
  488. if (newMaxValue < newMinValue)
  489. std::swap (newMaxValue, newMinValue);
  490. newMinValue = constrainedValue (newMinValue);
  491. newMaxValue = constrainedValue (newMaxValue);
  492. if (lastValueMax != newMaxValue || lastValueMin != newMinValue)
  493. {
  494. lastValueMax = newMaxValue;
  495. lastValueMin = newMinValue;
  496. valueMin = newMinValue;
  497. valueMax = newMaxValue;
  498. repaint();
  499. if (sendUpdateMessage)
  500. triggerChangeMessage (sendMessageSynchronously);
  501. }
  502. }
  503. void Slider::setDoubleClickReturnValue (const bool isDoubleClickEnabled,
  504. const double valueToSetOnDoubleClick)
  505. {
  506. doubleClickToValue = isDoubleClickEnabled;
  507. doubleClickReturnValue = valueToSetOnDoubleClick;
  508. }
  509. double Slider::getDoubleClickReturnValue (bool& isEnabled_) const
  510. {
  511. isEnabled_ = doubleClickToValue;
  512. return doubleClickReturnValue;
  513. }
  514. void Slider::updateText()
  515. {
  516. if (valueBox != nullptr)
  517. valueBox->setText (getTextFromValue (currentValue.getValue()), false);
  518. }
  519. void Slider::setTextValueSuffix (const String& suffix)
  520. {
  521. if (textSuffix != suffix)
  522. {
  523. textSuffix = suffix;
  524. updateText();
  525. }
  526. }
  527. String Slider::getTextValueSuffix() const
  528. {
  529. return textSuffix;
  530. }
  531. String Slider::getTextFromValue (double v)
  532. {
  533. if (getNumDecimalPlacesToDisplay() > 0)
  534. return String (v, getNumDecimalPlacesToDisplay()) + getTextValueSuffix();
  535. else
  536. return String (roundToInt (v)) + getTextValueSuffix();
  537. }
  538. double Slider::getValueFromText (const String& text)
  539. {
  540. String t (text.trimStart());
  541. if (t.endsWith (textSuffix))
  542. t = t.substring (0, t.length() - textSuffix.length());
  543. while (t.startsWithChar ('+'))
  544. t = t.substring (1).trimStart();
  545. return t.initialSectionContainingOnly ("0123456789.,-")
  546. .getDoubleValue();
  547. }
  548. double Slider::proportionOfLengthToValue (double proportion)
  549. {
  550. if (skewFactor != 1.0 && proportion > 0.0)
  551. proportion = exp (log (proportion) / skewFactor);
  552. return minimum + (maximum - minimum) * proportion;
  553. }
  554. double Slider::valueToProportionOfLength (double value)
  555. {
  556. const double n = (value - minimum) / (maximum - minimum);
  557. return skewFactor == 1.0 ? n : pow (n, skewFactor);
  558. }
  559. double Slider::snapValue (double attemptedValue, const bool)
  560. {
  561. return attemptedValue;
  562. }
  563. //==============================================================================
  564. void Slider::startedDragging()
  565. {
  566. }
  567. void Slider::stoppedDragging()
  568. {
  569. }
  570. void Slider::valueChanged()
  571. {
  572. }
  573. //==============================================================================
  574. void Slider::enablementChanged()
  575. {
  576. repaint();
  577. }
  578. void Slider::setPopupMenuEnabled (const bool menuEnabled_)
  579. {
  580. menuEnabled = menuEnabled_;
  581. }
  582. void Slider::setScrollWheelEnabled (const bool enabled)
  583. {
  584. scrollWheelEnabled = enabled;
  585. }
  586. //==============================================================================
  587. void Slider::labelTextChanged (Label* label)
  588. {
  589. const double newValue = snapValue (getValueFromText (label->getText()), false);
  590. if (newValue != (double) currentValue.getValue())
  591. {
  592. sendDragStart();
  593. setValue (newValue, true, true);
  594. sendDragEnd();
  595. }
  596. updateText(); // force a clean-up of the text, needed in case setValue() hasn't done this.
  597. }
  598. void Slider::buttonClicked (Button* button)
  599. {
  600. if (style == IncDecButtons)
  601. {
  602. sendDragStart();
  603. if (button == incButton)
  604. setValue (snapValue (getValue() + interval, false), true, true);
  605. else if (button == decButton)
  606. setValue (snapValue (getValue() - interval, false), true, true);
  607. sendDragEnd();
  608. }
  609. }
  610. //==============================================================================
  611. double Slider::constrainedValue (double value) const
  612. {
  613. if (interval > 0)
  614. value = minimum + interval * std::floor ((value - minimum) / interval + 0.5);
  615. if (value <= minimum || maximum <= minimum)
  616. value = minimum;
  617. else if (value >= maximum)
  618. value = maximum;
  619. return value;
  620. }
  621. float Slider::getLinearSliderPos (const double value)
  622. {
  623. double sliderPosProportional;
  624. if (maximum > minimum)
  625. {
  626. if (value < minimum)
  627. {
  628. sliderPosProportional = 0.0;
  629. }
  630. else if (value > maximum)
  631. {
  632. sliderPosProportional = 1.0;
  633. }
  634. else
  635. {
  636. sliderPosProportional = valueToProportionOfLength (value);
  637. jassert (sliderPosProportional >= 0 && sliderPosProportional <= 1.0);
  638. }
  639. }
  640. else
  641. {
  642. sliderPosProportional = 0.5;
  643. }
  644. if (isVertical() || style == IncDecButtons)
  645. sliderPosProportional = 1.0 - sliderPosProportional;
  646. return (float) (sliderRegionStart + sliderPosProportional * sliderRegionSize);
  647. }
  648. bool Slider::isHorizontal() const
  649. {
  650. return style == LinearHorizontal
  651. || style == LinearBar
  652. || style == TwoValueHorizontal
  653. || style == ThreeValueHorizontal;
  654. }
  655. bool Slider::isVertical() const
  656. {
  657. return style == LinearVertical
  658. || style == TwoValueVertical
  659. || style == ThreeValueVertical;
  660. }
  661. bool Slider::incDecDragDirectionIsHorizontal() const
  662. {
  663. return incDecButtonMode == incDecButtonsDraggable_Horizontal
  664. || (incDecButtonMode == incDecButtonsDraggable_AutoDirection && incDecButtonsSideBySide);
  665. }
  666. float Slider::getPositionOfValue (const double value)
  667. {
  668. if (isHorizontal() || isVertical())
  669. {
  670. return getLinearSliderPos (value);
  671. }
  672. else
  673. {
  674. jassertfalse; // not a valid call on a slider that doesn't work linearly!
  675. return 0.0f;
  676. }
  677. }
  678. //==============================================================================
  679. void Slider::paint (Graphics& g)
  680. {
  681. if (style != IncDecButtons)
  682. {
  683. if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag)
  684. {
  685. const float sliderPos = (float) valueToProportionOfLength (lastCurrentValue);
  686. jassert (sliderPos >= 0 && sliderPos <= 1.0f);
  687. getLookAndFeel().drawRotarySlider (g,
  688. sliderRect.getX(),
  689. sliderRect.getY(),
  690. sliderRect.getWidth(),
  691. sliderRect.getHeight(),
  692. sliderPos,
  693. rotaryStart, rotaryEnd,
  694. *this);
  695. }
  696. else
  697. {
  698. getLookAndFeel().drawLinearSlider (g,
  699. sliderRect.getX(),
  700. sliderRect.getY(),
  701. sliderRect.getWidth(),
  702. sliderRect.getHeight(),
  703. getLinearSliderPos (lastCurrentValue),
  704. getLinearSliderPos (lastValueMin),
  705. getLinearSliderPos (lastValueMax),
  706. style,
  707. *this);
  708. }
  709. if (style == LinearBar && valueBox == nullptr)
  710. {
  711. g.setColour (findColour (Slider::textBoxOutlineColourId));
  712. g.drawRect (0, 0, getWidth(), getHeight(), 1);
  713. }
  714. }
  715. }
  716. void Slider::resized()
  717. {
  718. int minXSpace = 0;
  719. int minYSpace = 0;
  720. if (textBoxPos == TextBoxLeft || textBoxPos == TextBoxRight)
  721. minXSpace = 30;
  722. else
  723. minYSpace = 15;
  724. const int tbw = jmax (0, jmin (textBoxWidth, getWidth() - minXSpace));
  725. const int tbh = jmax (0, jmin (textBoxHeight, getHeight() - minYSpace));
  726. if (style == LinearBar)
  727. {
  728. if (valueBox != nullptr)
  729. valueBox->setBounds (getLocalBounds());
  730. }
  731. else
  732. {
  733. if (textBoxPos == NoTextBox)
  734. {
  735. sliderRect = getLocalBounds();
  736. }
  737. else if (textBoxPos == TextBoxLeft)
  738. {
  739. valueBox->setBounds (0, (getHeight() - tbh) / 2, tbw, tbh);
  740. sliderRect.setBounds (tbw, 0, getWidth() - tbw, getHeight());
  741. }
  742. else if (textBoxPos == TextBoxRight)
  743. {
  744. valueBox->setBounds (getWidth() - tbw, (getHeight() - tbh) / 2, tbw, tbh);
  745. sliderRect.setBounds (0, 0, getWidth() - tbw, getHeight());
  746. }
  747. else if (textBoxPos == TextBoxAbove)
  748. {
  749. valueBox->setBounds ((getWidth() - tbw) / 2, 0, tbw, tbh);
  750. sliderRect.setBounds (0, tbh, getWidth(), getHeight() - tbh);
  751. }
  752. else if (textBoxPos == TextBoxBelow)
  753. {
  754. valueBox->setBounds ((getWidth() - tbw) / 2, getHeight() - tbh, tbw, tbh);
  755. sliderRect.setBounds (0, 0, getWidth(), getHeight() - tbh);
  756. }
  757. }
  758. const int indent = getLookAndFeel().getSliderThumbRadius (*this);
  759. if (style == LinearBar)
  760. {
  761. const int barIndent = 1;
  762. sliderRegionStart = barIndent;
  763. sliderRegionSize = getWidth() - barIndent * 2;
  764. sliderRect.setBounds (sliderRegionStart, barIndent,
  765. sliderRegionSize, getHeight() - barIndent * 2);
  766. }
  767. else if (isHorizontal())
  768. {
  769. sliderRegionStart = sliderRect.getX() + indent;
  770. sliderRegionSize = jmax (1, sliderRect.getWidth() - indent * 2);
  771. sliderRect.setBounds (sliderRegionStart, sliderRect.getY(),
  772. sliderRegionSize, sliderRect.getHeight());
  773. }
  774. else if (isVertical())
  775. {
  776. sliderRegionStart = sliderRect.getY() + indent;
  777. sliderRegionSize = jmax (1, sliderRect.getHeight() - indent * 2);
  778. sliderRect.setBounds (sliderRect.getX(), sliderRegionStart,
  779. sliderRect.getWidth(), sliderRegionSize);
  780. }
  781. else
  782. {
  783. sliderRegionStart = 0;
  784. sliderRegionSize = 100;
  785. }
  786. if (style == IncDecButtons)
  787. {
  788. Rectangle<int> buttonRect (sliderRect);
  789. if (textBoxPos == TextBoxLeft || textBoxPos == TextBoxRight)
  790. buttonRect.expand (-2, 0);
  791. else
  792. buttonRect.expand (0, -2);
  793. incDecButtonsSideBySide = buttonRect.getWidth() > buttonRect.getHeight();
  794. if (incDecButtonsSideBySide)
  795. {
  796. decButton->setBounds (buttonRect.removeFromLeft (buttonRect.getWidth() / 2));
  797. decButton->setConnectedEdges (Button::ConnectedOnRight);
  798. incButton->setConnectedEdges (Button::ConnectedOnLeft);
  799. }
  800. else
  801. {
  802. decButton->setBounds (buttonRect.removeFromBottom (buttonRect.getHeight() / 2));
  803. decButton->setConnectedEdges (Button::ConnectedOnTop);
  804. incButton->setConnectedEdges (Button::ConnectedOnBottom);
  805. }
  806. incButton->setBounds (buttonRect);
  807. }
  808. }
  809. void Slider::focusOfChildComponentChanged (FocusChangeType)
  810. {
  811. repaint();
  812. }
  813. namespace SliderHelpers
  814. {
  815. double smallestAngleBetween (double a1, double a2) noexcept
  816. {
  817. return jmin (std::abs (a1 - a2),
  818. std::abs (a1 + double_Pi * 2.0 - a2),
  819. std::abs (a2 + double_Pi * 2.0 - a1));
  820. }
  821. void sliderMenuCallback (int result, Slider* slider)
  822. {
  823. if (slider != nullptr)
  824. {
  825. switch (result)
  826. {
  827. case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break;
  828. case 2: slider->setSliderStyle (Slider::Rotary); break;
  829. case 3: slider->setSliderStyle (Slider::RotaryHorizontalDrag); break;
  830. case 4: slider->setSliderStyle (Slider::RotaryVerticalDrag); break;
  831. default: break;
  832. }
  833. }
  834. }
  835. }
  836. void Slider::showPopupMenu()
  837. {
  838. menuShown = true;
  839. PopupMenu m;
  840. m.setLookAndFeel (&getLookAndFeel());
  841. m.addItem (1, TRANS ("velocity-sensitive mode"), true, isVelocityBased);
  842. m.addSeparator();
  843. if (style == Rotary || style == RotaryHorizontalDrag || style == RotaryVerticalDrag)
  844. {
  845. PopupMenu rotaryMenu;
  846. rotaryMenu.addItem (2, TRANS ("use circular dragging"), true, style == Rotary);
  847. rotaryMenu.addItem (3, TRANS ("use left-right dragging"), true, style == RotaryHorizontalDrag);
  848. rotaryMenu.addItem (4, TRANS ("use up-down dragging"), true, style == RotaryVerticalDrag);
  849. m.addSubMenu (TRANS ("rotary mode"), rotaryMenu);
  850. }
  851. m.showMenuAsync (PopupMenu::Options(),
  852. ModalCallbackFunction::forComponent (SliderHelpers::sliderMenuCallback, this));
  853. }
  854. int Slider::getThumbIndexAt (const MouseEvent& e)
  855. {
  856. const bool isTwoValue = (style == TwoValueHorizontal || style == TwoValueVertical);
  857. const bool isThreeValue = (style == ThreeValueHorizontal || style == ThreeValueVertical);
  858. if (isTwoValue || isThreeValue)
  859. {
  860. const float mousePos = (float) (isVertical() ? e.y : e.x);
  861. const float normalPosDistance = std::abs (getLinearSliderPos (currentValue.getValue()) - mousePos);
  862. const float minPosDistance = std::abs (getLinearSliderPos (valueMin.getValue()) - 0.1f - mousePos);
  863. const float maxPosDistance = std::abs (getLinearSliderPos (valueMax.getValue()) + 0.1f - mousePos);
  864. if (isTwoValue)
  865. return maxPosDistance <= minPosDistance ? 2 : 1;
  866. if (normalPosDistance >= minPosDistance && maxPosDistance >= minPosDistance)
  867. return 1;
  868. else if (normalPosDistance >= maxPosDistance)
  869. return 2;
  870. }
  871. return 0;
  872. }
  873. void Slider::mouseDown (const MouseEvent& e)
  874. {
  875. mouseWasHidden = false;
  876. incDecDragged = false;
  877. mouseDragStartPos = mousePosWhenLastDragged = e.getPosition();
  878. if (isEnabled())
  879. {
  880. if (e.mods.isPopupMenu() && menuEnabled)
  881. {
  882. showPopupMenu();
  883. }
  884. else if (maximum > minimum)
  885. {
  886. menuShown = false;
  887. if (valueBox != nullptr)
  888. valueBox->hideEditor (true);
  889. sliderBeingDragged = getThumbIndexAt (e);
  890. minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
  891. lastAngle = rotaryStart + (rotaryEnd - rotaryStart)
  892. * valueToProportionOfLength (currentValue.getValue());
  893. valueWhenLastDragged = (sliderBeingDragged == 2 ? valueMax
  894. : (sliderBeingDragged == 1 ? valueMin
  895. : currentValue)).getValue();
  896. valueOnMouseDown = valueWhenLastDragged;
  897. if (popupDisplayEnabled)
  898. {
  899. PopupDisplayComponent* const popup = new PopupDisplayComponent (*this);
  900. popupDisplay = popup;
  901. if (parentForPopupDisplay != nullptr)
  902. parentForPopupDisplay->addChildComponent (popup);
  903. else
  904. popup->addToDesktop (0);
  905. popup->setVisible (true);
  906. }
  907. sendDragStart();
  908. mouseDrag (e);
  909. }
  910. }
  911. }
  912. void Slider::mouseUp (const MouseEvent&)
  913. {
  914. if (isEnabled()
  915. && (! menuShown)
  916. && (maximum > minimum)
  917. && (style != IncDecButtons || incDecDragged))
  918. {
  919. restoreMouseIfHidden();
  920. if (sendChangeOnlyOnRelease && valueOnMouseDown != (double) currentValue.getValue())
  921. triggerChangeMessage (false);
  922. sendDragEnd();
  923. popupDisplay = nullptr;
  924. if (style == IncDecButtons)
  925. {
  926. incButton->setState (Button::buttonNormal);
  927. decButton->setState (Button::buttonNormal);
  928. }
  929. }
  930. else if (popupDisplay != nullptr)
  931. {
  932. popupDisplay->startTimer (2000);
  933. }
  934. }
  935. void Slider::restoreMouseIfHidden()
  936. {
  937. if (mouseWasHidden)
  938. {
  939. mouseWasHidden = false;
  940. for (int i = Desktop::getInstance().getNumMouseSources(); --i >= 0;)
  941. Desktop::getInstance().getMouseSource(i)->enableUnboundedMouseMovement (false);
  942. const double pos = sliderBeingDragged == 2 ? getMaxValue()
  943. : (sliderBeingDragged == 1 ? getMinValue()
  944. : (double) currentValue.getValue());
  945. Point<int> mousePos;
  946. if (style == RotaryHorizontalDrag || style == RotaryVerticalDrag)
  947. {
  948. mousePos = Desktop::getLastMouseDownPosition();
  949. if (style == RotaryHorizontalDrag)
  950. {
  951. const double posDiff = valueToProportionOfLength (pos) - valueToProportionOfLength (valueOnMouseDown);
  952. mousePos += Point<int> (roundToInt (pixelsForFullDragExtent * posDiff), 0);
  953. }
  954. else
  955. {
  956. const double posDiff = valueToProportionOfLength (valueOnMouseDown) - valueToProportionOfLength (pos);
  957. mousePos += Point<int> (0, roundToInt (pixelsForFullDragExtent * posDiff));
  958. }
  959. }
  960. else
  961. {
  962. const int pixelPos = (int) getLinearSliderPos (pos);
  963. mousePos = localPointToGlobal (Point<int> (isHorizontal() ? pixelPos : (getWidth() / 2),
  964. isVertical() ? pixelPos : (getHeight() / 2)));
  965. }
  966. Desktop::setMousePosition (mousePos);
  967. }
  968. }
  969. void Slider::modifierKeysChanged (const ModifierKeys& modifiers)
  970. {
  971. if (isEnabled()
  972. && style != IncDecButtons
  973. && style != Rotary
  974. && isVelocityBased == modifiers.isAnyModifierKeyDown())
  975. {
  976. restoreMouseIfHidden();
  977. }
  978. }
  979. void Slider::handleRotaryDrag (const MouseEvent& e)
  980. {
  981. const int dx = e.x - sliderRect.getCentreX();
  982. const int dy = e.y - sliderRect.getCentreY();
  983. if (dx * dx + dy * dy > 25)
  984. {
  985. double angle = std::atan2 ((double) dx, (double) -dy);
  986. while (angle < 0.0)
  987. angle += double_Pi * 2.0;
  988. if (rotaryStop && ! e.mouseWasClicked())
  989. {
  990. if (std::abs (angle - lastAngle) > double_Pi)
  991. {
  992. if (angle >= lastAngle)
  993. angle -= double_Pi * 2.0;
  994. else
  995. angle += double_Pi * 2.0;
  996. }
  997. if (angle >= lastAngle)
  998. angle = jmin (angle, (double) jmax (rotaryStart, rotaryEnd));
  999. else
  1000. angle = jmax (angle, (double) jmin (rotaryStart, rotaryEnd));
  1001. }
  1002. else
  1003. {
  1004. while (angle < rotaryStart)
  1005. angle += double_Pi * 2.0;
  1006. if (angle > rotaryEnd)
  1007. {
  1008. if (SliderHelpers::smallestAngleBetween (angle, rotaryStart)
  1009. <= SliderHelpers::smallestAngleBetween (angle, rotaryEnd))
  1010. angle = rotaryStart;
  1011. else
  1012. angle = rotaryEnd;
  1013. }
  1014. }
  1015. const double proportion = (angle - rotaryStart) / (rotaryEnd - rotaryStart);
  1016. valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, proportion));
  1017. lastAngle = angle;
  1018. }
  1019. }
  1020. void Slider::handleAbsoluteDrag (const MouseEvent& e)
  1021. {
  1022. const int mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.x : e.y;
  1023. double scaledMousePos = (mousePos - sliderRegionStart) / (double) sliderRegionSize;
  1024. if (style == RotaryHorizontalDrag
  1025. || style == RotaryVerticalDrag
  1026. || style == IncDecButtons
  1027. || ((style == LinearHorizontal || style == LinearVertical || style == LinearBar)
  1028. && ! snapsToMousePos))
  1029. {
  1030. const int mouseDiff = (style == RotaryHorizontalDrag
  1031. || style == LinearHorizontal
  1032. || style == LinearBar
  1033. || (style == IncDecButtons && incDecDragDirectionIsHorizontal()))
  1034. ? e.x - mouseDragStartPos.x
  1035. : mouseDragStartPos.y - e.y;
  1036. double newPos = valueToProportionOfLength (valueOnMouseDown)
  1037. + mouseDiff * (1.0 / pixelsForFullDragExtent);
  1038. valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, newPos));
  1039. if (style == IncDecButtons)
  1040. {
  1041. incButton->setState (mouseDiff < 0 ? Button::buttonNormal : Button::buttonDown);
  1042. decButton->setState (mouseDiff > 0 ? Button::buttonNormal : Button::buttonDown);
  1043. }
  1044. }
  1045. else
  1046. {
  1047. if (isVertical())
  1048. scaledMousePos = 1.0 - scaledMousePos;
  1049. valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, scaledMousePos));
  1050. }
  1051. }
  1052. void Slider::handleVelocityDrag (const MouseEvent& e)
  1053. {
  1054. const int mouseDiff = (isHorizontal() || style == RotaryHorizontalDrag
  1055. || (style == IncDecButtons && incDecDragDirectionIsHorizontal()))
  1056. ? e.x - mousePosWhenLastDragged.x
  1057. : e.y - mousePosWhenLastDragged.y;
  1058. const double maxSpeed = jmax (200, sliderRegionSize);
  1059. double speed = jlimit (0.0, maxSpeed, (double) abs (mouseDiff));
  1060. if (speed != 0)
  1061. {
  1062. speed = 0.2 * velocityModeSensitivity
  1063. * (1.0 + std::sin (double_Pi * (1.5 + jmin (0.5, velocityModeOffset
  1064. + jmax (0.0, (double) (speed - velocityModeThreshold))
  1065. / maxSpeed))));
  1066. if (mouseDiff < 0)
  1067. speed = -speed;
  1068. if (isVertical() || style == RotaryVerticalDrag
  1069. || (style == IncDecButtons && ! incDecDragDirectionIsHorizontal()))
  1070. speed = -speed;
  1071. const double currentPos = valueToProportionOfLength (valueWhenLastDragged);
  1072. valueWhenLastDragged = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + speed));
  1073. e.source.enableUnboundedMouseMovement (true, false);
  1074. mouseWasHidden = true;
  1075. }
  1076. }
  1077. void Slider::mouseDrag (const MouseEvent& e)
  1078. {
  1079. if (isEnabled()
  1080. && (! menuShown)
  1081. && (maximum > minimum)
  1082. && ! (style == LinearBar && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable()))
  1083. {
  1084. if (style == Rotary)
  1085. {
  1086. handleRotaryDrag (e);
  1087. }
  1088. else
  1089. {
  1090. if (style == IncDecButtons && ! incDecDragged)
  1091. {
  1092. if (e.getDistanceFromDragStart() < 10 || e.mouseWasClicked())
  1093. return;
  1094. incDecDragged = true;
  1095. mouseDragStartPos = e.getPosition();
  1096. }
  1097. if (isVelocityBased == (userKeyOverridesVelocity && e.mods.testFlags (ModifierKeys::ctrlModifier
  1098. | ModifierKeys::commandModifier
  1099. | ModifierKeys::altModifier))
  1100. || (maximum - minimum) / sliderRegionSize < interval)
  1101. handleAbsoluteDrag (e);
  1102. else
  1103. handleVelocityDrag (e);
  1104. }
  1105. valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged);
  1106. if (sliderBeingDragged == 0)
  1107. {
  1108. setValue (snapValue (valueWhenLastDragged, true),
  1109. ! sendChangeOnlyOnRelease, true);
  1110. }
  1111. else if (sliderBeingDragged == 1)
  1112. {
  1113. setMinValue (snapValue (valueWhenLastDragged, true),
  1114. ! sendChangeOnlyOnRelease, false, true);
  1115. if (e.mods.isShiftDown())
  1116. setMaxValue (getMinValue() + minMaxDiff, false, false, true);
  1117. else
  1118. minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
  1119. }
  1120. else if (sliderBeingDragged == 2)
  1121. {
  1122. setMaxValue (snapValue (valueWhenLastDragged, true),
  1123. ! sendChangeOnlyOnRelease, false, true);
  1124. if (e.mods.isShiftDown())
  1125. setMinValue (getMaxValue() - minMaxDiff, false, false, true);
  1126. else
  1127. minMaxDiff = (double) valueMax.getValue() - (double) valueMin.getValue();
  1128. }
  1129. mousePosWhenLastDragged = e.getPosition();
  1130. }
  1131. }
  1132. void Slider::mouseDoubleClick (const MouseEvent&)
  1133. {
  1134. if (doubleClickToValue
  1135. && isEnabled()
  1136. && style != IncDecButtons
  1137. && minimum <= doubleClickReturnValue
  1138. && maximum >= doubleClickReturnValue)
  1139. {
  1140. sendDragStart();
  1141. setValue (doubleClickReturnValue, true, true);
  1142. sendDragEnd();
  1143. }
  1144. }
  1145. void Slider::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY)
  1146. {
  1147. if (scrollWheelEnabled && isEnabled()
  1148. && style != TwoValueHorizontal
  1149. && style != TwoValueVertical)
  1150. {
  1151. if (maximum > minimum && ! e.mods.isAnyMouseButtonDown())
  1152. {
  1153. if (valueBox != nullptr)
  1154. valueBox->hideEditor (false);
  1155. const double value = (double) currentValue.getValue();
  1156. const double proportionDelta = (wheelIncrementX != 0 ? -wheelIncrementX : wheelIncrementY) * 0.15f;
  1157. const double currentPos = valueToProportionOfLength (value);
  1158. const double newValue = proportionOfLengthToValue (jlimit (0.0, 1.0, currentPos + proportionDelta));
  1159. double delta = (newValue != value)
  1160. ? jmax (std::abs (newValue - value), interval) : 0;
  1161. if (value > newValue)
  1162. delta = -delta;
  1163. sendDragStart();
  1164. setValue (snapValue (value + delta, false), true, true);
  1165. sendDragEnd();
  1166. }
  1167. }
  1168. else
  1169. {
  1170. Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY);
  1171. }
  1172. }
  1173. void SliderListener::sliderDragStarted (Slider*) // (can't write Slider::Listener due to idiotic VC2005 bug)
  1174. {
  1175. }
  1176. void SliderListener::sliderDragEnded (Slider*)
  1177. {
  1178. }
  1179. const Identifier Slider::Ids::tagType ("SLIDER");
  1180. const Identifier Slider::Ids::min ("min");
  1181. const Identifier Slider::Ids::max ("max");
  1182. const Identifier Slider::Ids::interval ("interval");
  1183. const Identifier Slider::Ids::type ("type");
  1184. const Identifier Slider::Ids::editable ("editable");
  1185. const Identifier Slider::Ids::textBoxPos ("textBoxPos");
  1186. const Identifier Slider::Ids::textBoxWidth ("textBoxWidth");
  1187. const Identifier Slider::Ids::textBoxHeight ("textBoxHeight");
  1188. const Identifier Slider::Ids::skew ("skew");
  1189. void Slider::refreshFromValueTree (const ValueTree& state, ComponentBuilder&)
  1190. {
  1191. ComponentBuilder::refreshBasicComponentProperties (*this, state);
  1192. setRange (static_cast <double> (state [Ids::min]),
  1193. static_cast <double> (state [Ids::max]),
  1194. static_cast <double> (state [Ids::interval]));
  1195. setSliderStyle ((SliderStyle) static_cast <int> (state [Ids::type]));
  1196. setTextBoxStyle ((TextEntryBoxPosition) static_cast <int> (state [Ids::textBoxPos]),
  1197. ! static_cast <bool> (state [Ids::editable]),
  1198. static_cast <int> (state [Ids::textBoxWidth]),
  1199. static_cast <int> (state [Ids::textBoxHeight]));
  1200. setSkewFactor (static_cast <double> (state [Ids::skew]));
  1201. }
  1202. END_JUCE_NAMESPACE