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.

773 lines
28KB

  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. #include "jucer_Application.h"
  19. #include "jucer_AppearanceSettings.h"
  20. namespace AppearanceColours
  21. {
  22. struct ColourInfo
  23. {
  24. const char* name;
  25. uint32 colourID;
  26. bool mustBeOpaque;
  27. bool applyToEditorOnly;
  28. };
  29. static const ColourInfo colours[] =
  30. {
  31. { "Main Window Bkgd", mainBackgroundColourId, true, false },
  32. { "Treeview Highlight", treeviewHighlightColourId, false, false },
  33. { "Code Background", CodeEditorComponent::backgroundColourId, true, false },
  34. { "Line Number Bkgd", CodeEditorComponent::lineNumberBackgroundId, false, false },
  35. { "Line Numbers", CodeEditorComponent::lineNumberTextId, false, false },
  36. { "Plain Text", CodeEditorComponent::defaultTextColourId, false, false },
  37. { "Selected Text Bkgd", CodeEditorComponent::highlightColourId, false, false },
  38. { "Caret", CaretComponent::caretColourId, false, true }
  39. };
  40. enum
  41. {
  42. numColours = sizeof (AppearanceColours::colours) / sizeof (AppearanceColours::colours[0])
  43. };
  44. }
  45. //==============================================================================
  46. AppearanceSettings::AppearanceSettings (bool updateAppWhenChanged)
  47. : settings ("COLOUR_SCHEME")
  48. {
  49. IntrojucerLookAndFeel lf;
  50. for (int i = 0; i < AppearanceColours::numColours; ++i)
  51. getColourValue (AppearanceColours::colours[i].name) = lf.findColour (AppearanceColours::colours[i].colourID).toString();
  52. CodeDocument doc;
  53. CPlusPlusCodeTokeniser tokeniser;
  54. CodeEditorComponent editor (doc, &tokeniser);
  55. const CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
  56. for (int i = cs.types.size(); --i >= 0;)
  57. {
  58. CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
  59. getColourValue (t.name) = t.colour.toString();
  60. }
  61. getCodeFontValue() = getDefaultCodeFont().toString();
  62. if (updateAppWhenChanged)
  63. settings.addListener (this);
  64. }
  65. File AppearanceSettings::getSchemesFolder()
  66. {
  67. File f (getGlobalProperties().getFile().getSiblingFile ("Schemes"));
  68. f.createDirectory();
  69. return f;
  70. }
  71. void AppearanceSettings::writeDefaultSchemeFile (const String& xmlString, const String& name)
  72. {
  73. const File file (getSchemesFolder().getChildFile (name).withFileExtension (getSchemeFileSuffix()));
  74. AppearanceSettings settings (false);
  75. ScopedPointer<XmlElement> xml (XmlDocument::parse (xmlString));
  76. if (xml != nullptr)
  77. settings.readFromXML (*xml);
  78. settings.writeToFile (file);
  79. }
  80. void AppearanceSettings::refreshPresetSchemeList()
  81. {
  82. writeDefaultSchemeFile (BinaryData::colourscheme_dark_xml, "Default (Dark)");
  83. writeDefaultSchemeFile (BinaryData::colourscheme_light_xml, "Default (Light)");
  84. Array<File> newSchemes;
  85. getSchemesFolder().findChildFiles (newSchemes, File::findFiles, false, String ("*") + getSchemeFileSuffix());
  86. if (newSchemes != presetSchemeFiles)
  87. {
  88. presetSchemeFiles.swapWithArray (newSchemes);
  89. commandManager->commandStatusChanged();
  90. }
  91. }
  92. StringArray AppearanceSettings::getPresetSchemes()
  93. {
  94. StringArray s;
  95. for (int i = 0; i < presetSchemeFiles.size(); ++i)
  96. s.add (presetSchemeFiles.getReference(i).getFileNameWithoutExtension());
  97. return s;
  98. }
  99. void AppearanceSettings::selectPresetScheme (int index)
  100. {
  101. readFromFile (presetSchemeFiles [index]);
  102. }
  103. bool AppearanceSettings::readFromXML (const XmlElement& xml)
  104. {
  105. if (xml.hasTagName (settings.getType().toString()))
  106. {
  107. const ValueTree newSettings (ValueTree::fromXml (xml));
  108. // we'll manually copy across the new properties to the existing tree so that
  109. // any open editors will be kept up to date..
  110. settings.copyPropertiesFrom (newSettings, nullptr);
  111. for (int i = settings.getNumChildren(); --i >= 0;)
  112. {
  113. ValueTree c (settings.getChild (i));
  114. const ValueTree newValue (newSettings.getChildWithProperty (Ids::name, c.getProperty (Ids::name)));
  115. if (newValue.isValid())
  116. c.copyPropertiesFrom (newValue, nullptr);
  117. }
  118. return true;
  119. }
  120. return false;
  121. }
  122. bool AppearanceSettings::readFromFile (const File& file)
  123. {
  124. const ScopedPointer<XmlElement> xml (XmlDocument::parse (file));
  125. return xml != nullptr && readFromXML (*xml);
  126. }
  127. bool AppearanceSettings::writeToFile (const File& file) const
  128. {
  129. const ScopedPointer<XmlElement> xml (settings.createXml());
  130. return xml != nullptr && xml->writeToFile (file, String::empty);
  131. }
  132. Font AppearanceSettings::getDefaultCodeFont()
  133. {
  134. return Font (Font::getDefaultMonospacedFontName(), Font::getDefaultStyle(), 13.0f);
  135. }
  136. StringArray AppearanceSettings::getColourNames() const
  137. {
  138. StringArray s;
  139. for (int i = 0; i < settings.getNumChildren(); ++i)
  140. {
  141. const ValueTree c (settings.getChild(i));
  142. if (c.hasType ("COLOUR"))
  143. s.add (c [Ids::name]);
  144. }
  145. return s;
  146. }
  147. void AppearanceSettings::updateColourScheme()
  148. {
  149. applyToLookAndFeel (LookAndFeel::getDefaultLookAndFeel());
  150. IntrojucerApp::getApp().mainWindowList.sendLookAndFeelChange();
  151. }
  152. void AppearanceSettings::applyToLookAndFeel (LookAndFeel& lf) const
  153. {
  154. for (int i = 0; i < AppearanceColours::numColours; ++i)
  155. {
  156. Colour col;
  157. if (getColour (AppearanceColours::colours[i].name, col))
  158. {
  159. if (AppearanceColours::colours[i].mustBeOpaque)
  160. col = Colours::white.overlaidWith (col);
  161. if (! AppearanceColours::colours[i].applyToEditorOnly)
  162. lf.setColour (AppearanceColours::colours[i].colourID, col);
  163. }
  164. }
  165. lf.setColour (ScrollBar::thumbColourId,
  166. IntrojucerLookAndFeel::getScrollbarColourForBackground (lf.findColour (mainBackgroundColourId)));
  167. }
  168. void AppearanceSettings::applyToCodeEditor (CodeEditorComponent& editor) const
  169. {
  170. CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
  171. for (int i = cs.types.size(); --i >= 0;)
  172. {
  173. CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
  174. getColour (t.name, t.colour);
  175. }
  176. editor.setColourScheme (cs);
  177. editor.setFont (getCodeFont());
  178. for (int i = 0; i < AppearanceColours::numColours; ++i)
  179. {
  180. if (AppearanceColours::colours[i].applyToEditorOnly)
  181. {
  182. Colour col;
  183. if (getColour (AppearanceColours::colours[i].name, col))
  184. editor.setColour (AppearanceColours::colours[i].colourID, col);
  185. }
  186. }
  187. editor.setColour (ScrollBar::thumbColourId,
  188. IntrojucerLookAndFeel::getScrollbarColourForBackground (editor.findColour (CodeEditorComponent::backgroundColourId)));
  189. }
  190. Font AppearanceSettings::getCodeFont() const
  191. {
  192. const String fontString (settings [Ids::font].toString());
  193. if (fontString.isEmpty())
  194. return getDefaultCodeFont();
  195. return Font::fromString (fontString);
  196. }
  197. Value AppearanceSettings::getCodeFontValue()
  198. {
  199. return settings.getPropertyAsValue (Ids::font, nullptr);
  200. }
  201. Value AppearanceSettings::getColourValue (const String& colourName)
  202. {
  203. ValueTree c (settings.getChildWithProperty (Ids::name, colourName));
  204. if (! c.isValid())
  205. {
  206. c = ValueTree ("COLOUR");
  207. c.setProperty (Ids::name, colourName, nullptr);
  208. settings.addChild (c, -1, nullptr);
  209. }
  210. return c.getPropertyAsValue (Ids::colour, nullptr);
  211. }
  212. bool AppearanceSettings::getColour (const String& name, Colour& result) const
  213. {
  214. const ValueTree colour (settings.getChildWithProperty (Ids::name, name));
  215. if (colour.isValid())
  216. {
  217. result = Colour::fromString (colour [Ids::colour].toString());
  218. return true;
  219. }
  220. return false;
  221. }
  222. //==============================================================================
  223. struct AppearanceEditor
  224. {
  225. class FontScanPanel : public Component,
  226. private Timer
  227. {
  228. public:
  229. FontScanPanel()
  230. {
  231. fontsToScan = Font::findAllTypefaceNames();
  232. startTimer (1);
  233. }
  234. void paint (Graphics& g)
  235. {
  236. g.fillAll (Colours::darkgrey);
  237. g.setFont (14.0f);
  238. g.setColour (Colours::white);
  239. g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
  240. const int size = 30;
  241. getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
  242. }
  243. void timerCallback()
  244. {
  245. repaint();
  246. if (fontsToScan.size() == 0)
  247. {
  248. getAppSettings().monospacedFontNames = fontsFound;
  249. if (DialogWindow* w = findParentComponentOfClass<DialogWindow>())
  250. w->setContentOwned (new EditorPanel(), false);
  251. }
  252. else
  253. {
  254. if (isMonospacedTypeface (fontsToScan[0]))
  255. fontsFound.add (fontsToScan[0]);
  256. fontsToScan.remove (0);
  257. }
  258. }
  259. // A rather hacky trick to select only the fixed-pitch fonts..
  260. // This is unfortunately a bit slow, but will work on all platforms.
  261. static bool isMonospacedTypeface (const String& name)
  262. {
  263. const Font font (name, 20.0f, Font::plain);
  264. const int width = font.getStringWidth ("....");
  265. return width == font.getStringWidth ("WWWW")
  266. && width == font.getStringWidth ("0000")
  267. && width == font.getStringWidth ("1111")
  268. && width == font.getStringWidth ("iiii");
  269. }
  270. StringArray fontsToScan, fontsFound;
  271. };
  272. //==============================================================================
  273. class EditorPanel : public Component,
  274. private ButtonListener
  275. {
  276. public:
  277. EditorPanel()
  278. : loadButton ("Load Scheme..."),
  279. saveButton ("Save Scheme...")
  280. {
  281. rebuildProperties();
  282. addAndMakeVisible (&panel);
  283. loadButton.setColour (TextButton::buttonColourId, Colours::lightgrey.withAlpha (0.5f));
  284. saveButton.setColour (TextButton::buttonColourId, Colours::lightgrey.withAlpha (0.5f));
  285. loadButton.setColour (TextButton::textColourOffId, Colours::white);
  286. saveButton.setColour (TextButton::textColourOffId, Colours::white);
  287. addAndMakeVisible (&loadButton);
  288. addAndMakeVisible (&saveButton);
  289. loadButton.addListener (this);
  290. saveButton.addListener (this);
  291. }
  292. void rebuildProperties()
  293. {
  294. AppearanceSettings& scheme = getAppSettings().appearance;
  295. Array <PropertyComponent*> props;
  296. Value fontValue (scheme.getCodeFontValue());
  297. props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
  298. props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
  299. const StringArray colourNames (scheme.getColourNames());
  300. for (int i = 0; i < colourNames.size(); ++i)
  301. props.add (new ColourPropertyComponent (nullptr, colourNames[i],
  302. scheme.getColourValue (colourNames[i]),
  303. Colours::white, false));
  304. panel.clear();
  305. panel.addProperties (props);
  306. }
  307. void resized()
  308. {
  309. Rectangle<int> r (getLocalBounds());
  310. panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (4, 2));
  311. loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 4));
  312. saveButton.setBounds (r.reduced (10, 3));
  313. }
  314. private:
  315. PropertyPanel panel;
  316. TextButton loadButton, saveButton;
  317. void buttonClicked (Button* b)
  318. {
  319. if (b == &loadButton)
  320. loadScheme();
  321. else
  322. saveScheme();
  323. }
  324. void saveScheme()
  325. {
  326. FileChooser fc ("Select a file in which to save this colour-scheme...",
  327. getAppSettings().appearance.getSchemesFolder()
  328. .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
  329. AppearanceSettings::getSchemeFileWildCard());
  330. if (fc.browseForFileToSave (true))
  331. {
  332. File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
  333. getAppSettings().appearance.writeToFile (file);
  334. getAppSettings().appearance.refreshPresetSchemeList();
  335. }
  336. }
  337. void loadScheme()
  338. {
  339. FileChooser fc ("Please select a colour-scheme file to load...",
  340. getAppSettings().appearance.getSchemesFolder(),
  341. AppearanceSettings::getSchemeFileWildCard());
  342. if (fc.browseForFileToOpen())
  343. {
  344. if (getAppSettings().appearance.readFromFile (fc.getResult()))
  345. rebuildProperties();
  346. }
  347. }
  348. JUCE_DECLARE_NON_COPYABLE (EditorPanel)
  349. };
  350. //==============================================================================
  351. class FontNameValueSource : public ValueSourceFilter
  352. {
  353. public:
  354. FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
  355. var getValue() const
  356. {
  357. return Font::fromString (sourceValue.toString()).getTypefaceName();
  358. }
  359. void setValue (const var& newValue)
  360. {
  361. Font font (Font::fromString (sourceValue.toString()));
  362. font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
  363. : newValue.toString());
  364. sourceValue = font.toString();
  365. }
  366. static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
  367. {
  368. StringArray fontNames = getAppSettings().monospacedFontNames;
  369. Array<var> values;
  370. values.add (Font::getDefaultMonospacedFontName());
  371. values.add (var());
  372. for (int i = 0; i < fontNames.size(); ++i)
  373. values.add (fontNames[i]);
  374. StringArray names;
  375. names.add ("<Default Monospaced>");
  376. names.add (String::empty);
  377. names.addArray (getAppSettings().monospacedFontNames);
  378. return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
  379. title, names, values);
  380. }
  381. };
  382. //==============================================================================
  383. class FontSizeValueSource : public ValueSourceFilter
  384. {
  385. public:
  386. FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
  387. var getValue() const
  388. {
  389. return Font::fromString (sourceValue.toString()).getHeight();
  390. }
  391. void setValue (const var& newValue)
  392. {
  393. sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
  394. }
  395. static PropertyComponent* createProperty (const String& title, const Value& value)
  396. {
  397. return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
  398. title, 5.0, 40.0, 0.1, 0.5);
  399. }
  400. };
  401. };
  402. void AppearanceSettings::showEditorWindow (ScopedPointer<Component>& ownerPointer)
  403. {
  404. if (ownerPointer != nullptr)
  405. {
  406. ownerPointer->toFront (true);
  407. }
  408. else
  409. {
  410. Component* content;
  411. if (getAppSettings().monospacedFontNames.size() == 0)
  412. content = new AppearanceEditor::FontScanPanel();
  413. else
  414. content = new AppearanceEditor::EditorPanel();
  415. const int width = 350;
  416. new FloatingToolWindow ("Appearance Settings",
  417. "colourSchemeEditorPos",
  418. content, ownerPointer,
  419. width, 500,
  420. width, 200, width, 1000);
  421. }
  422. }
  423. //==============================================================================
  424. IntrojucerLookAndFeel::IntrojucerLookAndFeel()
  425. {
  426. setColour (mainBackgroundColourId, Colour::greyLevel (0.8f));
  427. setColour (treeviewHighlightColourId, Colour (0x401111ee));
  428. setColour (TextButton::buttonColourId, Colour (0xffeeeeff));
  429. setColour (ScrollBar::thumbColourId,
  430. getScrollbarColourForBackground (findColour (mainBackgroundColourId)));
  431. }
  432. Colour IntrojucerLookAndFeel::getScrollbarColourForBackground (const Colour& background)
  433. {
  434. return background.contrasting().withAlpha (0.13f);
  435. }
  436. Rectangle<int> IntrojucerLookAndFeel::getPropertyComponentContentPosition (PropertyComponent& component)
  437. {
  438. if (component.findParentComponentOfClass<AppearanceEditor::EditorPanel>() != nullptr)
  439. return component.getLocalBounds().reduced (1).removeFromRight (component.getWidth() / 2);
  440. return LookAndFeel::getPropertyComponentContentPosition (component);
  441. }
  442. int IntrojucerLookAndFeel::getTabButtonOverlap (int /*tabDepth*/) { return -1; }
  443. int IntrojucerLookAndFeel::getTabButtonSpaceAroundImage() { return 1; }
  444. int IntrojucerLookAndFeel::getTabButtonBestWidth (TabBarButton&, int /*tabDepth*/) { return 120; }
  445. void IntrojucerLookAndFeel::createTabTextLayout (const TabBarButton& button, const Rectangle<int>& textArea, GlyphArrangement& textLayout)
  446. {
  447. Font font (textArea.getHeight() * 0.5f);
  448. font.setUnderline (button.hasKeyboardFocus (false));
  449. textLayout.addFittedText (font, button.getButtonText().trim(),
  450. (float) textArea.getX(), (float) textArea.getY(), (float) textArea.getWidth(), (float) textArea.getHeight(),
  451. Justification::centred, 1);
  452. }
  453. Colour IntrojucerLookAndFeel::getTabBackgroundColour (TabBarButton& button)
  454. {
  455. const Colour normalBkg (button.findColour (mainBackgroundColourId));
  456. Colour bkg (normalBkg.contrasting (0.15f));
  457. if (button.isFrontTab())
  458. bkg = bkg.overlaidWith (Colours::yellow.withAlpha (0.5f));
  459. return bkg;
  460. }
  461. void IntrojucerLookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
  462. {
  463. const Rectangle<int> activeArea (button.getActiveArea());
  464. const Colour bkg (getTabBackgroundColour (button));
  465. g.setGradientFill (ColourGradient (bkg.brighter (0.1f), 0, (float) activeArea.getY(),
  466. bkg.darker (0.1f), 0, (float) activeArea.getBottom(), false));
  467. g.fillRect (activeArea);
  468. g.setColour (button.findColour (mainBackgroundColourId).darker (0.3f));
  469. g.drawRect (activeArea);
  470. GlyphArrangement textLayout;
  471. createTabTextLayout (button, button.getTextArea(), textLayout);
  472. const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f;
  473. g.setColour (bkg.contrasting().withMultipliedAlpha (alpha));
  474. textLayout.draw (g);
  475. }
  476. Rectangle<int> IntrojucerLookAndFeel::getTabButtonExtraComponentBounds (const TabBarButton& button, Rectangle<int>& textArea, Component& comp)
  477. {
  478. GlyphArrangement textLayout;
  479. createTabTextLayout (button, textArea, textLayout);
  480. const int textWidth = (int) textLayout.getBoundingBox (0, -1, false).getWidth();
  481. const int extraSpace = jmax (0, textArea.getWidth() - (textWidth + comp.getWidth())) / 2;
  482. textArea.removeFromRight (extraSpace);
  483. textArea.removeFromLeft (extraSpace);
  484. return textArea.removeFromRight (comp.getWidth());
  485. }
  486. void IntrojucerLookAndFeel::drawStretchableLayoutResizerBar (Graphics& g, int /*w*/, int /*h*/, bool /*isVerticalBar*/, bool isMouseOver, bool isMouseDragging)
  487. {
  488. if (isMouseOver || isMouseDragging)
  489. g.fillAll (Colours::yellow.withAlpha (0.4f));
  490. }
  491. void IntrojucerLookAndFeel::drawScrollbar (Graphics& g, ScrollBar& scrollbar, int x, int y, int width, int height,
  492. bool isScrollbarVertical, int thumbStartPosition, int thumbSize,
  493. bool isMouseOver, bool isMouseDown)
  494. {
  495. Path thumbPath;
  496. if (thumbSize > 0)
  497. {
  498. const float thumbIndent = (isScrollbarVertical ? width : height) * 0.25f;
  499. const float thumbIndentx2 = thumbIndent * 2.0f;
  500. if (isScrollbarVertical)
  501. thumbPath.addRoundedRectangle (x + thumbIndent, thumbStartPosition + thumbIndent,
  502. width - thumbIndentx2, thumbSize - thumbIndentx2, (width - thumbIndentx2) * 0.5f);
  503. else
  504. thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, y + thumbIndent,
  505. thumbSize - thumbIndentx2, height - thumbIndentx2, (height - thumbIndentx2) * 0.5f);
  506. }
  507. Colour thumbCol (scrollbar.findColour (ScrollBar::thumbColourId, true));
  508. if (isMouseOver || isMouseDown)
  509. thumbCol = thumbCol.withMultipliedAlpha (2.0f);
  510. g.setColour (thumbCol);
  511. g.fillPath (thumbPath);
  512. g.setColour (thumbCol.contrasting ((isMouseOver || isMouseDown) ? 0.2f : 0.1f));
  513. g.strokePath (thumbPath, PathStrokeType (1.0f));
  514. }
  515. static Range<float> getBrightnessRange (const Image& im)
  516. {
  517. float minB = 1.0f, maxB = 0;
  518. const int w = im.getWidth();
  519. const int h = im.getHeight();
  520. for (int y = 0; y < h; ++y)
  521. {
  522. for (int x = 0; x < w; ++x)
  523. {
  524. const float b = im.getPixelAt (x, y).getBrightness();
  525. minB = jmin (minB, b);
  526. maxB = jmax (maxB, b);
  527. }
  528. }
  529. return Range<float> (minB, maxB);
  530. }
  531. void IntrojucerLookAndFeel::fillWithBackgroundTexture (Graphics& g)
  532. {
  533. const Colour bkg (findColour (mainBackgroundColourId));
  534. if (backgroundTextureBaseColour != bkg)
  535. {
  536. backgroundTextureBaseColour = bkg;
  537. const Image original (ImageCache::getFromMemory (BinaryData::background_tile_png,
  538. BinaryData::background_tile_pngSize));
  539. const int w = original.getWidth();
  540. const int h = original.getHeight();
  541. backgroundTexture = Image (Image::RGB, w, h, false);
  542. const Range<float> brightnessRange (getBrightnessRange (original));
  543. const float brightnessOffset = (brightnessRange.getStart() + brightnessRange.getEnd()) / 2.0f;
  544. const float brightnessScale = 0.025f / brightnessRange.getLength();
  545. const float bkgB = bkg.getBrightness();
  546. for (int y = 0; y < h; ++y)
  547. {
  548. for (int x = 0; x < w; ++x)
  549. {
  550. const float b = (original.getPixelAt (x, y).getBrightness() - brightnessOffset) * brightnessScale;
  551. backgroundTexture.setPixelAt (x, y, bkg.withBrightness (jlimit (0.0f, 1.0f, bkgB + b)));
  552. }
  553. }
  554. }
  555. g.setTiledImageFill (backgroundTexture, 0, 0, 1.0f);
  556. g.fillAll();
  557. }
  558. void IntrojucerLookAndFeel::fillWithBackgroundTexture (Component& c, Graphics& g)
  559. {
  560. dynamic_cast<IntrojucerLookAndFeel&> (c.getLookAndFeel()).fillWithBackgroundTexture (g);
  561. }
  562. void IntrojucerLookAndFeel::drawConcertinaPanelHeader (Graphics& g, const Rectangle<int>& area,
  563. bool isMouseOver, bool /*isMouseDown*/,
  564. ConcertinaPanel&, Component& panel)
  565. {
  566. const Colour bkg (findColour (mainBackgroundColourId));
  567. g.setGradientFill (ColourGradient (Colours::white.withAlpha (isMouseOver ? 0.4f : 0.2f), 0, (float) area.getY(),
  568. Colours::darkgrey.withAlpha (0.2f), 0, (float) area.getBottom(), false));
  569. g.fillAll();
  570. g.setColour (bkg.contrasting().withAlpha (0.04f));
  571. g.fillRect (area.withHeight (1));
  572. g.fillRect (area.withTop (area.getBottom() - 1));
  573. g.setColour (bkg.contrasting());
  574. g.setFont (Font (area.getHeight() * 0.6f).boldened());
  575. g.drawFittedText (panel.getName(), 4, 0, area.getWidth() - 6, area.getHeight(), Justification::centredLeft, 1);
  576. }
  577. void IntrojucerLookAndFeel::drawButtonBackground (Graphics& g,
  578. Button& button,
  579. const Colour& backgroundColour,
  580. bool isMouseOverButton,
  581. bool isButtonDown)
  582. {
  583. const bool flatOnLeft = button.isConnectedOnLeft();
  584. const bool flatOnRight = button.isConnectedOnRight();
  585. const bool flatOnTop = button.isConnectedOnTop();
  586. const bool flatOnBottom = button.isConnectedOnBottom();
  587. const float width = (float) button.getWidth();
  588. const float height = (float) button.getHeight();
  589. const float x = 0.5f;
  590. const float y = 0.5f;
  591. const float w = width - 1.0f;
  592. const float h = height - 1.0f;
  593. const float cornerSize = 4.0f;
  594. Colour baseColour (backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true)
  595. ? 1.3f : 0.9f)
  596. .withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f));
  597. if (isButtonDown) baseColour = baseColour.contrasting (0.2f);
  598. else if (isMouseOverButton) baseColour = baseColour.contrasting (0.1f);
  599. const float mainBrightness = baseColour.getBrightness();
  600. const float mainAlpha = baseColour.getFloatAlpha();
  601. Path outline;
  602. outline.addRoundedRectangle (x, y, w, h, cornerSize, cornerSize,
  603. ! (flatOnLeft || flatOnTop),
  604. ! (flatOnRight || flatOnTop),
  605. ! (flatOnLeft || flatOnBottom),
  606. ! (flatOnRight || flatOnBottom));
  607. g.setGradientFill (ColourGradient (baseColour.brighter (0.2f), 0.0f, 0.0f,
  608. baseColour.darker (0.25f), 0.0f, height, false));
  609. g.fillPath (outline);
  610. g.setColour (Colours::white.withAlpha (0.4f * mainAlpha * mainBrightness * mainBrightness));
  611. g.strokePath (outline, PathStrokeType (1.0f), AffineTransform::translation (0.0f, 1.0f)
  612. .scaled (1.0f, (h - 1.6f) / h));
  613. g.setColour (Colours::black.withAlpha (0.4f * mainAlpha));
  614. g.strokePath (outline, PathStrokeType (1.0f));
  615. }