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.

285 lines
9.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. //==============================================================================
  21. class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSource,
  22. private Value::Listener
  23. {
  24. public:
  25. RemapperValueSource (const Value& source, const Array<var>& map)
  26. : sourceValue (source),
  27. mappings (map)
  28. {
  29. sourceValue.addListener (this);
  30. }
  31. var getValue() const override
  32. {
  33. auto targetValue = sourceValue.getValue();
  34. for (auto& map : mappings)
  35. if (map.equalsWithSameType (targetValue))
  36. return mappings.indexOf (map) + 1;
  37. return mappings.indexOf (targetValue) + 1;
  38. }
  39. void setValue (const var& newValue) override
  40. {
  41. auto remappedVal = mappings [static_cast<int> (newValue) - 1];
  42. if (! remappedVal.equalsWithSameType (sourceValue))
  43. sourceValue = remappedVal;
  44. }
  45. protected:
  46. Value sourceValue;
  47. Array<var> mappings;
  48. void valueChanged (Value&) override { sendChangeMessage (true); }
  49. //==============================================================================
  50. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSource)
  51. };
  52. //==============================================================================
  53. class ChoicePropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource,
  54. private Value::Listener
  55. {
  56. public:
  57. RemapperValueSourceWithDefault (ValueWithDefault* vwd, const Array<var>& map)
  58. : valueWithDefault (vwd),
  59. sourceValue (valueWithDefault->getPropertyAsValue()),
  60. mappings (map)
  61. {
  62. sourceValue.addListener (this);
  63. }
  64. var getValue() const override
  65. {
  66. if (valueWithDefault != nullptr && ! valueWithDefault->isUsingDefault())
  67. {
  68. const auto target = sourceValue.getValue();
  69. const auto equalsWithSameType = [&target] (const var& map) { return map.equalsWithSameType (target); };
  70. auto iter = std::find_if (mappings.begin(), mappings.end(), equalsWithSameType);
  71. if (iter == mappings.end())
  72. iter = std::find (mappings.begin(), mappings.end(), target);
  73. if (iter != mappings.end())
  74. return 1 + (int) std::distance (mappings.begin(), iter);
  75. }
  76. return -1;
  77. }
  78. void setValue (const var& newValue) override
  79. {
  80. if (valueWithDefault == nullptr)
  81. return;
  82. auto newValueInt = static_cast<int> (newValue);
  83. if (newValueInt == -1)
  84. {
  85. valueWithDefault->resetToDefault();
  86. }
  87. else
  88. {
  89. auto remappedVal = mappings [newValueInt - 1];
  90. if (! remappedVal.equalsWithSameType (sourceValue))
  91. *valueWithDefault = remappedVal;
  92. }
  93. }
  94. private:
  95. void valueChanged (Value&) override { sendChangeMessage (true); }
  96. WeakReference<ValueWithDefault> valueWithDefault;
  97. Value sourceValue;
  98. Array<var> mappings;
  99. //==============================================================================
  100. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault)
  101. };
  102. //==============================================================================
  103. ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
  104. : PropertyComponent (name),
  105. isCustomClass (true)
  106. {
  107. }
  108. ChoicePropertyComponent::ChoicePropertyComponent (const String& name,
  109. const StringArray& choiceList,
  110. const Array<var>& correspondingValues)
  111. : PropertyComponent (name),
  112. choices (choiceList)
  113. {
  114. // The array of corresponding values must contain one value for each of the items in
  115. // the choices array!
  116. jassertquiet (correspondingValues.size() == choices.size());
  117. }
  118. ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
  119. const String& name,
  120. const StringArray& choiceList,
  121. const Array<var>& correspondingValues)
  122. : ChoicePropertyComponent (name, choiceList, correspondingValues)
  123. {
  124. refreshChoices();
  125. initialiseComboBox (Value (new RemapperValueSource (valueToControl, correspondingValues)));
  126. }
  127. ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl,
  128. const String& name,
  129. const StringArray& choiceList,
  130. const Array<var>& correspondingValues)
  131. : ChoicePropertyComponent (name, choiceList, correspondingValues)
  132. {
  133. valueWithDefault = &valueToControl;
  134. auto getDefaultString = [this, correspondingValues] { return choices [correspondingValues.indexOf (valueWithDefault->getDefault())]; };
  135. refreshChoices (getDefaultString());
  136. initialiseComboBox (Value (new RemapperValueSourceWithDefault (valueWithDefault, correspondingValues)));
  137. valueWithDefault->onDefaultChange = [this, getDefaultString]
  138. {
  139. auto selectedId = comboBox.getSelectedId();
  140. refreshChoices (getDefaultString());
  141. comboBox.setSelectedId (selectedId);
  142. };
  143. }
  144. ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault& valueToControl,
  145. const String& name)
  146. : PropertyComponent (name),
  147. choices ({ "Enabled", "Disabled" })
  148. {
  149. valueWithDefault = &valueToControl;
  150. auto getDefaultString = [this] { return valueWithDefault->getDefault() ? "Enabled" : "Disabled"; };
  151. refreshChoices (getDefaultString());
  152. initialiseComboBox (Value (new RemapperValueSourceWithDefault (valueWithDefault, { true, false })));
  153. valueWithDefault->onDefaultChange = [this, getDefaultString]
  154. {
  155. auto selectedId = comboBox.getSelectedId();
  156. refreshChoices (getDefaultString());
  157. comboBox.setSelectedId (selectedId);
  158. };
  159. }
  160. ChoicePropertyComponent::~ChoicePropertyComponent()
  161. {
  162. if (valueWithDefault != nullptr)
  163. valueWithDefault->onDefaultChange = nullptr;
  164. }
  165. //==============================================================================
  166. void ChoicePropertyComponent::initialiseComboBox (const Value& v)
  167. {
  168. if (v != Value())
  169. comboBox.setSelectedId (v.getValue(), dontSendNotification);
  170. comboBox.getSelectedIdAsValue().referTo (v);
  171. comboBox.setEditableText (false);
  172. addAndMakeVisible (comboBox);
  173. }
  174. void ChoicePropertyComponent::refreshChoices()
  175. {
  176. comboBox.clear();
  177. for (int i = 0; i < choices.size(); ++i)
  178. {
  179. const auto& choice = choices[i];
  180. if (choice.isNotEmpty())
  181. comboBox.addItem (choice, i + 1);
  182. else
  183. comboBox.addSeparator();
  184. }
  185. }
  186. void ChoicePropertyComponent::refreshChoices (const String& defaultString)
  187. {
  188. refreshChoices();
  189. comboBox.addItem ("Default" + (defaultString.isNotEmpty() ? " (" + defaultString + ")" : ""), -1);
  190. }
  191. //==============================================================================
  192. void ChoicePropertyComponent::setIndex (const int /*newIndex*/)
  193. {
  194. jassertfalse; // you need to override this method in your subclass!
  195. }
  196. int ChoicePropertyComponent::getIndex() const
  197. {
  198. jassertfalse; // you need to override this method in your subclass!
  199. return -1;
  200. }
  201. const StringArray& ChoicePropertyComponent::getChoices() const
  202. {
  203. return choices;
  204. }
  205. //==============================================================================
  206. void ChoicePropertyComponent::refresh()
  207. {
  208. if (isCustomClass)
  209. {
  210. if (! comboBox.isVisible())
  211. {
  212. refreshChoices();
  213. initialiseComboBox ({});
  214. comboBox.onChange = [this] { changeIndex(); };
  215. }
  216. comboBox.setSelectedId (getIndex() + 1, dontSendNotification);
  217. }
  218. }
  219. void ChoicePropertyComponent::changeIndex()
  220. {
  221. if (isCustomClass)
  222. {
  223. auto newIndex = comboBox.getSelectedId() - 1;
  224. if (newIndex != getIndex())
  225. setIndex (newIndex);
  226. }
  227. }
  228. } // namespace juce