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.

348 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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. chooser = std::make_unique<FileChooser> ("Select a file in which to save this colour-scheme...",
  147. getAppSettings().appearance.getSchemesFolder()
  148. .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
  149. AppearanceSettings::getSchemeFileWildCard());
  150. auto chooserFlags = FileBrowserComponent::saveMode
  151. | FileBrowserComponent::canSelectFiles
  152. | FileBrowserComponent::warnAboutOverwriting;
  153. chooser->launchAsync (chooserFlags, [this, isExit] (const FileChooser& fc)
  154. {
  155. if (fc.getResult() == File{})
  156. {
  157. if (isExit)
  158. restorePreviousScheme();
  159. return;
  160. }
  161. File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
  162. getAppSettings().appearance.writeToFile (file);
  163. getAppSettings().appearance.refreshPresetSchemeList();
  164. saveSchemeState();
  165. ProjucerApplication::getApp().selectEditorColourSchemeWithName (file.getFileNameWithoutExtension());
  166. });
  167. }
  168. void loadScheme()
  169. {
  170. chooser = std::make_unique<FileChooser> ("Please select a colour-scheme file to load...",
  171. getAppSettings().appearance.getSchemesFolder(),
  172. AppearanceSettings::getSchemeFileWildCard());
  173. auto chooserFlags = FileBrowserComponent::openMode
  174. | FileBrowserComponent::canSelectFiles;
  175. chooser->launchAsync (chooserFlags, [this] (const FileChooser& fc)
  176. {
  177. if (fc.getResult() == File{})
  178. return;
  179. if (getAppSettings().appearance.readFromFile (fc.getResult()))
  180. {
  181. rebuildProperties();
  182. saveSchemeState();
  183. }
  184. });
  185. }
  186. void lookAndFeelChanged() override
  187. {
  188. loadButton.setColour (TextButton::buttonColourId,
  189. findColour (secondaryButtonBackgroundColourId));
  190. }
  191. void saveSchemeState()
  192. {
  193. auto& appearance = getAppSettings().appearance;
  194. const auto colourNames = appearance.getColourNames();
  195. codeFont = appearance.getCodeFont();
  196. colourValues.clear();
  197. for (int i = 0; i < colourNames.size(); ++i)
  198. colourValues.add (appearance.getColourValue (colourNames[i]).getValue());
  199. }
  200. bool hasSchemeBeenModifiedSinceSave()
  201. {
  202. auto& appearance = getAppSettings().appearance;
  203. const auto colourNames = appearance.getColourNames();
  204. if (codeFont != appearance.getCodeFont())
  205. return true;
  206. for (int i = 0; i < colourNames.size(); ++i)
  207. if (colourValues[i] != appearance.getColourValue (colourNames[i]).getValue())
  208. return true;
  209. return false;
  210. }
  211. void restorePreviousScheme()
  212. {
  213. auto& appearance = getAppSettings().appearance;
  214. const auto colourNames = appearance.getColourNames();
  215. appearance.getCodeFontValue().setValue (codeFont.toString());
  216. for (int i = 0; i < colourNames.size(); ++i)
  217. appearance.getColourValue (colourNames[i]).setValue (colourValues[i]);
  218. }
  219. std::unique_ptr<FileChooser> chooser;
  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. };