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.

346 lines
13KB

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