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.

335 lines
12KB

  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. #pragma once
  19. #include "../../Utility/UI/PropertyComponents/jucer_ColourPropertyComponent.h"
  20. //==============================================================================
  21. class EditorColourSchemeWindowComponent : public Component
  22. {
  23. public:
  24. EditorColourSchemeWindowComponent()
  25. {
  26. if (getAppSettings().monospacedFontNames.size() == 0)
  27. changeContent (new AppearanceEditor::FontScanPanel());
  28. else
  29. changeContent (new AppearanceEditor::EditorPanel());
  30. }
  31. void paint (Graphics& g) override
  32. {
  33. g.fillAll (findColour (backgroundColourId));
  34. }
  35. void resized() override
  36. {
  37. content->setBounds (getLocalBounds());
  38. }
  39. void changeContent (Component* newContent)
  40. {
  41. content.reset (newContent);
  42. addAndMakeVisible (newContent);
  43. content->setBounds (getLocalBounds().reduced (10));
  44. }
  45. private:
  46. std::unique_ptr<Component> content;
  47. //==============================================================================
  48. struct AppearanceEditor
  49. {
  50. struct FontScanPanel : public Component,
  51. private Timer
  52. {
  53. FontScanPanel()
  54. {
  55. fontsToScan = Font::findAllTypefaceNames();
  56. startTimer (1);
  57. }
  58. void paint (Graphics& g) override
  59. {
  60. g.fillAll (findColour (backgroundColourId));
  61. g.setFont (14.0f);
  62. g.setColour (findColour (defaultTextColourId));
  63. g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
  64. const auto size = 30;
  65. getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
  66. }
  67. void timerCallback() override
  68. {
  69. repaint();
  70. if (fontsToScan.size() == 0)
  71. {
  72. getAppSettings().monospacedFontNames = fontsFound;
  73. if (auto* owner = findParentComponentOfClass<EditorColourSchemeWindowComponent>())
  74. owner->changeContent (new EditorPanel());
  75. }
  76. else
  77. {
  78. if (isMonospacedTypeface (fontsToScan[0]))
  79. fontsFound.add (fontsToScan[0]);
  80. fontsToScan.remove (0);
  81. }
  82. }
  83. // A rather hacky trick to select only the fixed-pitch fonts..
  84. // This is unfortunately a bit slow, but will work on all platforms.
  85. static bool isMonospacedTypeface (const String& name)
  86. {
  87. const Font font (name, 20.0f, Font::plain);
  88. const auto width = font.getStringWidth ("....");
  89. return width == font.getStringWidth ("WWWW")
  90. && width == font.getStringWidth ("0000")
  91. && width == font.getStringWidth ("1111")
  92. && width == font.getStringWidth ("iiii");
  93. }
  94. StringArray fontsToScan, fontsFound;
  95. };
  96. //==============================================================================
  97. struct EditorPanel : public Component
  98. {
  99. EditorPanel()
  100. : loadButton ("Load Scheme..."),
  101. saveButton ("Save Scheme...")
  102. {
  103. rebuildProperties();
  104. addAndMakeVisible (panel);
  105. addAndMakeVisible (loadButton);
  106. addAndMakeVisible (saveButton);
  107. loadButton.onClick = [this] { loadScheme(); };
  108. saveButton.onClick = [this] { saveScheme (false); };
  109. lookAndFeelChanged();
  110. saveSchemeState();
  111. }
  112. ~EditorPanel() override
  113. {
  114. if (hasSchemeBeenModifiedSinceSave())
  115. saveScheme (true);
  116. }
  117. void rebuildProperties()
  118. {
  119. auto& scheme = getAppSettings().appearance;
  120. Array<PropertyComponent*> props;
  121. auto fontValue = scheme.getCodeFontValue();
  122. props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
  123. props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
  124. const auto colourNames = scheme.getColourNames();
  125. for (int i = 0; i < colourNames.size(); ++i)
  126. props.add (new ColourPropertyComponent (nullptr, colourNames[i],
  127. scheme.getColourValue (colourNames[i]),
  128. Colours::white, false));
  129. panel.clear();
  130. panel.addProperties (props);
  131. }
  132. void resized() override
  133. {
  134. auto r = getLocalBounds();
  135. panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (10, 2));
  136. loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 1));
  137. saveButton.setBounds (r.reduced (10, 1));
  138. }
  139. private:
  140. PropertyPanel panel;
  141. TextButton loadButton, saveButton;
  142. Font codeFont;
  143. Array<var> colourValues;
  144. void saveScheme (bool isExit)
  145. {
  146. FileChooser fc ("Select a file in which to save this colour-scheme...",
  147. getAppSettings().appearance.getSchemesFolder()
  148. .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
  149. AppearanceSettings::getSchemeFileWildCard());
  150. if (fc.browseForFileToSave (true))
  151. {
  152. File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
  153. getAppSettings().appearance.writeToFile (file);
  154. getAppSettings().appearance.refreshPresetSchemeList();
  155. saveSchemeState();
  156. ProjucerApplication::getApp().selectEditorColourSchemeWithName (file.getFileNameWithoutExtension());
  157. }
  158. else if (isExit)
  159. {
  160. restorePreviousScheme();
  161. }
  162. }
  163. void loadScheme()
  164. {
  165. FileChooser fc ("Please select a colour-scheme file to load...",
  166. getAppSettings().appearance.getSchemesFolder(),
  167. AppearanceSettings::getSchemeFileWildCard());
  168. if (fc.browseForFileToOpen())
  169. {
  170. if (getAppSettings().appearance.readFromFile (fc.getResult()))
  171. {
  172. rebuildProperties();
  173. saveSchemeState();
  174. }
  175. }
  176. }
  177. void lookAndFeelChanged() override
  178. {
  179. loadButton.setColour (TextButton::buttonColourId,
  180. findColour (secondaryButtonBackgroundColourId));
  181. }
  182. void saveSchemeState()
  183. {
  184. auto& appearance = getAppSettings().appearance;
  185. const auto colourNames = appearance.getColourNames();
  186. codeFont = appearance.getCodeFont();
  187. colourValues.clear();
  188. for (int i = 0; i < colourNames.size(); ++i)
  189. colourValues.add (appearance.getColourValue (colourNames[i]).getValue());
  190. }
  191. bool hasSchemeBeenModifiedSinceSave()
  192. {
  193. auto& appearance = getAppSettings().appearance;
  194. const auto colourNames = appearance.getColourNames();
  195. if (codeFont != appearance.getCodeFont())
  196. return true;
  197. for (int i = 0; i < colourNames.size(); ++i)
  198. if (colourValues[i] != appearance.getColourValue (colourNames[i]).getValue())
  199. return true;
  200. return false;
  201. }
  202. void restorePreviousScheme()
  203. {
  204. auto& appearance = getAppSettings().appearance;
  205. const auto colourNames = appearance.getColourNames();
  206. appearance.getCodeFontValue().setValue (codeFont.toString());
  207. for (int i = 0; i < colourNames.size(); ++i)
  208. appearance.getColourValue (colourNames[i]).setValue (colourValues[i]);
  209. }
  210. JUCE_DECLARE_NON_COPYABLE (EditorPanel)
  211. };
  212. //==============================================================================
  213. struct FontNameValueSource : public ValueSourceFilter
  214. {
  215. FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
  216. var getValue() const override
  217. {
  218. return Font::fromString (sourceValue.toString()).getTypefaceName();
  219. }
  220. void setValue (const var& newValue) override
  221. {
  222. auto font = Font::fromString (sourceValue.toString());
  223. font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
  224. : newValue.toString());
  225. sourceValue = font.toString();
  226. }
  227. static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
  228. {
  229. auto fontNames = getAppSettings().monospacedFontNames;
  230. Array<var> values;
  231. values.add (Font::getDefaultMonospacedFontName());
  232. values.add (var());
  233. for (int i = 0; i < fontNames.size(); ++i)
  234. values.add (fontNames[i]);
  235. StringArray names;
  236. names.add ("<Default Monospaced>");
  237. names.add (String());
  238. names.addArray (getAppSettings().monospacedFontNames);
  239. return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
  240. title, names, values);
  241. }
  242. };
  243. //==============================================================================
  244. struct FontSizeValueSource : public ValueSourceFilter
  245. {
  246. FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
  247. var getValue() const override
  248. {
  249. return Font::fromString (sourceValue.toString()).getHeight();
  250. }
  251. void setValue (const var& newValue) override
  252. {
  253. sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
  254. }
  255. static PropertyComponent* createProperty (const String& title, const Value& value)
  256. {
  257. return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
  258. title, 5.0, 40.0, 0.1, 0.5);
  259. }
  260. };
  261. };
  262. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorColourSchemeWindowComponent)
  263. };