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.

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