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.

3062 lines
123KB

  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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. namespace LookAndFeelHelpers
  21. {
  22. void createRoundedPath (Path& p,
  23. const float x, const float y,
  24. const float w, const float h,
  25. const float cs,
  26. const bool curveTopLeft, const bool curveTopRight,
  27. const bool curveBottomLeft, const bool curveBottomRight) noexcept
  28. {
  29. const float cs2 = 2.0f * cs;
  30. if (curveTopLeft)
  31. {
  32. p.startNewSubPath (x, y + cs);
  33. p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f);
  34. }
  35. else
  36. {
  37. p.startNewSubPath (x, y);
  38. }
  39. if (curveTopRight)
  40. {
  41. p.lineTo (x + w - cs, y);
  42. p.addArc (x + w - cs2, y, cs2, cs2, 0.0f, float_Pi * 0.5f);
  43. }
  44. else
  45. {
  46. p.lineTo (x + w, y);
  47. }
  48. if (curveBottomRight)
  49. {
  50. p.lineTo (x + w, y + h - cs);
  51. p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi);
  52. }
  53. else
  54. {
  55. p.lineTo (x + w, y + h);
  56. }
  57. if (curveBottomLeft)
  58. {
  59. p.lineTo (x + cs, y + h);
  60. p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f);
  61. }
  62. else
  63. {
  64. p.lineTo (x, y + h);
  65. }
  66. p.closeSubPath();
  67. }
  68. Colour createBaseColour (const Colour& buttonColour,
  69. const bool hasKeyboardFocus,
  70. const bool isMouseOverButton,
  71. const bool isButtonDown) noexcept
  72. {
  73. const float sat = hasKeyboardFocus ? 1.3f : 0.9f;
  74. const Colour baseColour (buttonColour.withMultipliedSaturation (sat));
  75. if (isButtonDown)
  76. return baseColour.contrasting (0.2f);
  77. else if (isMouseOverButton)
  78. return baseColour.contrasting (0.1f);
  79. return baseColour;
  80. }
  81. TextLayout layoutTooltipText (const String& text) noexcept
  82. {
  83. const float tooltipFontSize = 13.0f;
  84. const int maxToolTipWidth = 400;
  85. AttributedString s;
  86. s.setJustification (Justification::centred);
  87. s.append (text, Font (tooltipFontSize, Font::bold));
  88. TextLayout tl;
  89. tl.createLayoutWithBalancedLineLengths (s, (float) maxToolTipWidth);
  90. return tl;
  91. }
  92. }
  93. //==============================================================================
  94. typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&);
  95. extern GetTypefaceForFont juce_getTypefaceForFont;
  96. static Typeface::Ptr getTypefaceForFontFromLookAndFeel (const Font& font)
  97. {
  98. return LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font);
  99. }
  100. //==============================================================================
  101. LookAndFeel::LookAndFeel()
  102. : useNativeAlertWindows (false)
  103. {
  104. /* if this fails it means you're trying to create a LookAndFeel object before
  105. the static Colours have been initialised. That ain't gonna work. It probably
  106. means that you're using a static LookAndFeel object and that your compiler has
  107. decided to intialise it before the Colours class.
  108. */
  109. jassert (Colours::white == Colour (0xffffffff));
  110. // set up the standard set of colours..
  111. const int textButtonColour = 0xffbbbbff;
  112. const int textHighlightColour = 0x401111ee;
  113. const int standardOutlineColour = 0xb2808080;
  114. static const int standardColours[] =
  115. {
  116. TextButton::buttonColourId, textButtonColour,
  117. TextButton::buttonOnColourId, 0xff4444ff,
  118. TextButton::textColourOnId, 0xff000000,
  119. TextButton::textColourOffId, 0xff000000,
  120. ComboBox::buttonColourId, 0xffbbbbff,
  121. ComboBox::outlineColourId, standardOutlineColour,
  122. ToggleButton::textColourId, 0xff000000,
  123. TextEditor::backgroundColourId, 0xffffffff,
  124. TextEditor::textColourId, 0xff000000,
  125. TextEditor::highlightColourId, textHighlightColour,
  126. TextEditor::highlightedTextColourId, 0xff000000,
  127. TextEditor::outlineColourId, 0x00000000,
  128. TextEditor::focusedOutlineColourId, textButtonColour,
  129. TextEditor::shadowColourId, 0x38000000,
  130. CaretComponent::caretColourId, 0xff000000,
  131. Label::backgroundColourId, 0x00000000,
  132. Label::textColourId, 0xff000000,
  133. Label::outlineColourId, 0x00000000,
  134. ScrollBar::backgroundColourId, 0x00000000,
  135. ScrollBar::thumbColourId, 0xffffffff,
  136. TreeView::linesColourId, 0x4c000000,
  137. TreeView::backgroundColourId, 0x00000000,
  138. TreeView::dragAndDropIndicatorColourId, 0x80ff0000,
  139. PopupMenu::backgroundColourId, 0xffffffff,
  140. PopupMenu::textColourId, 0xff000000,
  141. PopupMenu::headerTextColourId, 0xff000000,
  142. PopupMenu::highlightedTextColourId, 0xffffffff,
  143. PopupMenu::highlightedBackgroundColourId, 0x991111aa,
  144. ComboBox::textColourId, 0xff000000,
  145. ComboBox::backgroundColourId, 0xffffffff,
  146. ComboBox::arrowColourId, 0x99000000,
  147. ListBox::backgroundColourId, 0xffffffff,
  148. ListBox::outlineColourId, standardOutlineColour,
  149. ListBox::textColourId, 0xff000000,
  150. Slider::backgroundColourId, 0x00000000,
  151. Slider::thumbColourId, textButtonColour,
  152. Slider::trackColourId, 0x7fffffff,
  153. Slider::rotarySliderFillColourId, 0x7f0000ff,
  154. Slider::rotarySliderOutlineColourId, 0x66000000,
  155. Slider::textBoxTextColourId, 0xff000000,
  156. Slider::textBoxBackgroundColourId, 0xffffffff,
  157. Slider::textBoxHighlightColourId, textHighlightColour,
  158. Slider::textBoxOutlineColourId, standardOutlineColour,
  159. ResizableWindow::backgroundColourId, 0xff777777,
  160. //DocumentWindow::textColourId, 0xff000000, // (this is deliberately not set)
  161. AlertWindow::backgroundColourId, 0xffededed,
  162. AlertWindow::textColourId, 0xff000000,
  163. AlertWindow::outlineColourId, 0xff666666,
  164. ProgressBar::backgroundColourId, 0xffeeeeee,
  165. ProgressBar::foregroundColourId, 0xffaaaaee,
  166. TooltipWindow::backgroundColourId, 0xffeeeebb,
  167. TooltipWindow::textColourId, 0xff000000,
  168. TooltipWindow::outlineColourId, 0x4c000000,
  169. TabbedComponent::backgroundColourId, 0x00000000,
  170. TabbedComponent::outlineColourId, 0xff777777,
  171. TabbedButtonBar::tabOutlineColourId, 0x80000000,
  172. TabbedButtonBar::frontOutlineColourId, 0x90000000,
  173. Toolbar::backgroundColourId, 0xfff6f8f9,
  174. Toolbar::separatorColourId, 0x4c000000,
  175. Toolbar::buttonMouseOverBackgroundColourId, 0x4c0000ff,
  176. Toolbar::buttonMouseDownBackgroundColourId, 0x800000ff,
  177. Toolbar::labelTextColourId, 0xff000000,
  178. Toolbar::editingModeOutlineColourId, 0xffff0000,
  179. HyperlinkButton::textColourId, 0xcc1111ee,
  180. GroupComponent::outlineColourId, 0x66000000,
  181. GroupComponent::textColourId, 0xff000000,
  182. DirectoryContentsDisplayComponent::highlightColourId, textHighlightColour,
  183. DirectoryContentsDisplayComponent::textColourId, 0xff000000,
  184. 0x1000440, /*LassoComponent::lassoFillColourId*/ 0x66dddddd,
  185. 0x1000441, /*LassoComponent::lassoOutlineColourId*/ 0x99111111,
  186. 0x1005000, /*MidiKeyboardComponent::whiteNoteColourId*/ 0xffffffff,
  187. 0x1005001, /*MidiKeyboardComponent::blackNoteColourId*/ 0xff000000,
  188. 0x1005002, /*MidiKeyboardComponent::keySeparatorLineColourId*/ 0x66000000,
  189. 0x1005003, /*MidiKeyboardComponent::mouseOverKeyOverlayColourId*/ 0x80ffff00,
  190. 0x1005004, /*MidiKeyboardComponent::keyDownOverlayColourId*/ 0xffb6b600,
  191. 0x1005005, /*MidiKeyboardComponent::textLabelColourId*/ 0xff000000,
  192. 0x1005006, /*MidiKeyboardComponent::upDownButtonBackgroundColourId*/ 0xffd3d3d3,
  193. 0x1005007, /*MidiKeyboardComponent::upDownButtonArrowColourId*/ 0xff000000,
  194. 0x1004500, /*CodeEditorComponent::backgroundColourId*/ 0xffffffff,
  195. 0x1004502, /*CodeEditorComponent::highlightColourId*/ textHighlightColour,
  196. 0x1004503, /*CodeEditorComponent::defaultTextColourId*/ 0xff000000,
  197. 0x1007000, /*ColourSelector::backgroundColourId*/ 0xffe5e5e5,
  198. 0x1007001, /*ColourSelector::labelTextColourId*/ 0xff000000,
  199. 0x100ad00, /*KeyMappingEditorComponent::backgroundColourId*/ 0x00000000,
  200. 0x100ad01, /*KeyMappingEditorComponent::textColourId*/ 0xff000000,
  201. FileSearchPathListComponent::backgroundColourId, 0xffffffff,
  202. FileChooserDialogBox::titleTextColourId, 0xff000000,
  203. DrawableButton::textColourId, 0xff000000,
  204. };
  205. for (int i = 0; i < numElementsInArray (standardColours); i += 2)
  206. setColour (standardColours [i], Colour ((uint32) standardColours [i + 1]));
  207. juce_getTypefaceForFont = getTypefaceForFontFromLookAndFeel;
  208. }
  209. LookAndFeel::~LookAndFeel()
  210. {
  211. masterReference.clear();
  212. }
  213. //==============================================================================
  214. const Colour LookAndFeel::findColour (const int colourId) const noexcept
  215. {
  216. const int index = colourIds.indexOf (colourId);
  217. if (index >= 0)
  218. return colours [index];
  219. jassertfalse;
  220. return Colours::black;
  221. }
  222. void LookAndFeel::setColour (const int colourId, const Colour& colour) noexcept
  223. {
  224. const int index = colourIds.indexOf (colourId);
  225. if (index >= 0)
  226. {
  227. colours.set (index, colour);
  228. }
  229. else
  230. {
  231. colourIds.add (colourId);
  232. colours.add (colour);
  233. }
  234. }
  235. bool LookAndFeel::isColourSpecified (const int colourId) const noexcept
  236. {
  237. return colourIds.contains (colourId);
  238. }
  239. //==============================================================================
  240. LookAndFeel& LookAndFeel::getDefaultLookAndFeel() noexcept
  241. {
  242. return Desktop::getInstance().getDefaultLookAndFeel();
  243. }
  244. void LookAndFeel::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel) noexcept
  245. {
  246. Desktop::getInstance().setDefaultLookAndFeel (newDefaultLookAndFeel);
  247. }
  248. //==============================================================================
  249. const Typeface::Ptr LookAndFeel::getTypefaceForFont (const Font& font)
  250. {
  251. if (defaultSans.isNotEmpty() && font.getTypefaceName() == Font::getDefaultSansSerifFontName())
  252. {
  253. Font f (font);
  254. f.setTypefaceName (defaultSans);
  255. return Typeface::createSystemTypefaceFor (f);
  256. }
  257. return Font::getDefaultTypefaceForFont (font);
  258. }
  259. void LookAndFeel::setDefaultSansSerifTypefaceName (const String& newName)
  260. {
  261. defaultSans = newName;
  262. }
  263. //==============================================================================
  264. MouseCursor LookAndFeel::getMouseCursorFor (Component& component)
  265. {
  266. return component.getMouseCursor();
  267. }
  268. LowLevelGraphicsContext* LookAndFeel::createGraphicsContext (const Image& imageToRenderOn, const Point<int>& origin, const RectangleList& initialClip)
  269. {
  270. return new LowLevelGraphicsSoftwareRenderer (imageToRenderOn, origin, initialClip);
  271. }
  272. //==============================================================================
  273. void LookAndFeel::drawButtonBackground (Graphics& g,
  274. Button& button,
  275. const Colour& backgroundColour,
  276. bool isMouseOverButton,
  277. bool isButtonDown)
  278. {
  279. const int width = button.getWidth();
  280. const int height = button.getHeight();
  281. const float outlineThickness = button.isEnabled() ? ((isButtonDown || isMouseOverButton) ? 1.2f : 0.7f) : 0.4f;
  282. const float halfThickness = outlineThickness * 0.5f;
  283. const float indentL = button.isConnectedOnLeft() ? 0.1f : halfThickness;
  284. const float indentR = button.isConnectedOnRight() ? 0.1f : halfThickness;
  285. const float indentT = button.isConnectedOnTop() ? 0.1f : halfThickness;
  286. const float indentB = button.isConnectedOnBottom() ? 0.1f : halfThickness;
  287. const Colour baseColour (LookAndFeelHelpers::createBaseColour (backgroundColour,
  288. button.hasKeyboardFocus (true),
  289. isMouseOverButton, isButtonDown)
  290. .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f));
  291. drawGlassLozenge (g,
  292. indentL,
  293. indentT,
  294. width - indentL - indentR,
  295. height - indentT - indentB,
  296. baseColour, outlineThickness, -1.0f,
  297. button.isConnectedOnLeft(),
  298. button.isConnectedOnRight(),
  299. button.isConnectedOnTop(),
  300. button.isConnectedOnBottom());
  301. }
  302. const Font LookAndFeel::getFontForTextButton (TextButton& button)
  303. {
  304. return button.getFont();
  305. }
  306. void LookAndFeel::drawButtonText (Graphics& g, TextButton& button,
  307. bool /*isMouseOverButton*/, bool /*isButtonDown*/)
  308. {
  309. Font font (getFontForTextButton (button));
  310. g.setFont (font);
  311. g.setColour (button.findColour (button.getToggleState() ? TextButton::textColourOnId
  312. : TextButton::textColourOffId)
  313. .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f));
  314. const int yIndent = jmin (4, button.proportionOfHeight (0.3f));
  315. const int cornerSize = jmin (button.getHeight(), button.getWidth()) / 2;
  316. const int fontHeight = roundToInt (font.getHeight() * 0.6f);
  317. const int leftIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnLeft() ? 4 : 2));
  318. const int rightIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnRight() ? 4 : 2));
  319. g.drawFittedText (button.getButtonText(),
  320. leftIndent,
  321. yIndent,
  322. button.getWidth() - leftIndent - rightIndent,
  323. button.getHeight() - yIndent * 2,
  324. Justification::centred, 2);
  325. }
  326. void LookAndFeel::drawTickBox (Graphics& g,
  327. Component& component,
  328. float x, float y, float w, float h,
  329. const bool ticked,
  330. const bool isEnabled,
  331. const bool isMouseOverButton,
  332. const bool isButtonDown)
  333. {
  334. const float boxSize = w * 0.7f;
  335. drawGlassSphere (g, x, y + (h - boxSize) * 0.5f, boxSize,
  336. LookAndFeelHelpers::createBaseColour (component.findColour (TextButton::buttonColourId)
  337. .withMultipliedAlpha (isEnabled ? 1.0f : 0.5f),
  338. true, isMouseOverButton, isButtonDown),
  339. isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f);
  340. if (ticked)
  341. {
  342. Path tick;
  343. tick.startNewSubPath (1.5f, 3.0f);
  344. tick.lineTo (3.0f, 6.0f);
  345. tick.lineTo (6.0f, 0.0f);
  346. g.setColour (isEnabled ? Colours::black : Colours::grey);
  347. const AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f)
  348. .translated (x, y));
  349. g.strokePath (tick, PathStrokeType (2.5f), trans);
  350. }
  351. }
  352. void LookAndFeel::drawToggleButton (Graphics& g,
  353. ToggleButton& button,
  354. bool isMouseOverButton,
  355. bool isButtonDown)
  356. {
  357. if (button.hasKeyboardFocus (true))
  358. {
  359. g.setColour (button.findColour (TextEditor::focusedOutlineColourId));
  360. g.drawRect (0, 0, button.getWidth(), button.getHeight());
  361. }
  362. float fontSize = jmin (15.0f, button.getHeight() * 0.75f);
  363. const float tickWidth = fontSize * 1.1f;
  364. drawTickBox (g, button, 4.0f, (button.getHeight() - tickWidth) * 0.5f,
  365. tickWidth, tickWidth,
  366. button.getToggleState(),
  367. button.isEnabled(),
  368. isMouseOverButton,
  369. isButtonDown);
  370. g.setColour (button.findColour (ToggleButton::textColourId));
  371. g.setFont (fontSize);
  372. if (! button.isEnabled())
  373. g.setOpacity (0.5f);
  374. const int textX = (int) tickWidth + 5;
  375. g.drawFittedText (button.getButtonText(),
  376. textX, 0,
  377. button.getWidth() - textX - 2, button.getHeight(),
  378. Justification::centredLeft, 10);
  379. }
  380. void LookAndFeel::changeToggleButtonWidthToFitText (ToggleButton& button)
  381. {
  382. Font font (jmin (15.0f, button.getHeight() * 0.6f));
  383. const int tickWidth = jmin (24, button.getHeight());
  384. button.setSize (font.getStringWidth (button.getButtonText()) + tickWidth + 8,
  385. button.getHeight());
  386. }
  387. //==============================================================================
  388. AlertWindow* LookAndFeel::createAlertWindow (const String& title,
  389. const String& message,
  390. const String& button1,
  391. const String& button2,
  392. const String& button3,
  393. AlertWindow::AlertIconType iconType,
  394. int numButtons,
  395. Component* associatedComponent)
  396. {
  397. AlertWindow* aw = new AlertWindow (title, message, iconType, associatedComponent);
  398. if (numButtons == 1)
  399. {
  400. aw->addButton (button1, 0,
  401. KeyPress (KeyPress::escapeKey, 0, 0),
  402. KeyPress (KeyPress::returnKey, 0, 0));
  403. }
  404. else
  405. {
  406. const KeyPress button1ShortCut ((int) CharacterFunctions::toLowerCase (button1[0]), 0, 0);
  407. KeyPress button2ShortCut ((int) CharacterFunctions::toLowerCase (button2[0]), 0, 0);
  408. if (button1ShortCut == button2ShortCut)
  409. button2ShortCut = KeyPress();
  410. if (numButtons == 2)
  411. {
  412. aw->addButton (button1, 1, KeyPress (KeyPress::returnKey, 0, 0), button1ShortCut);
  413. aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey, 0, 0), button2ShortCut);
  414. }
  415. else if (numButtons == 3)
  416. {
  417. aw->addButton (button1, 1, button1ShortCut);
  418. aw->addButton (button2, 2, button2ShortCut);
  419. aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey, 0, 0));
  420. }
  421. }
  422. return aw;
  423. }
  424. void LookAndFeel::drawAlertBox (Graphics& g,
  425. AlertWindow& alert,
  426. const Rectangle<int>& textArea,
  427. TextLayout& textLayout)
  428. {
  429. g.fillAll (alert.findColour (AlertWindow::backgroundColourId));
  430. int iconSpaceUsed = 0;
  431. const int iconWidth = 80;
  432. int iconSize = jmin (iconWidth + 50, alert.getHeight() + 20);
  433. if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2)
  434. iconSize = jmin (iconSize, textArea.getHeight() + 50);
  435. const Rectangle<int> iconRect (iconSize / -10, iconSize / -10,
  436. iconSize, iconSize);
  437. if (alert.getAlertType() != AlertWindow::NoIcon)
  438. {
  439. Path icon;
  440. uint32 colour;
  441. char character;
  442. if (alert.getAlertType() == AlertWindow::WarningIcon)
  443. {
  444. colour = 0x55ff5555;
  445. character = '!';
  446. icon.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, (float) iconRect.getY(),
  447. (float) iconRect.getRight(), (float) iconRect.getBottom(),
  448. (float) iconRect.getX(), (float) iconRect.getBottom());
  449. icon = icon.createPathWithRoundedCorners (5.0f);
  450. }
  451. else
  452. {
  453. colour = alert.getAlertType() == AlertWindow::InfoIcon ? (uint32) 0x605555ff : (uint32) 0x40b69900;
  454. character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?';
  455. icon.addEllipse ((float) iconRect.getX(), (float) iconRect.getY(),
  456. (float) iconRect.getWidth(), (float) iconRect.getHeight());
  457. }
  458. GlyphArrangement ga;
  459. ga.addFittedText (Font (iconRect.getHeight() * 0.9f, Font::bold),
  460. String::charToString ((juce_wchar) (uint8) character),
  461. (float) iconRect.getX(), (float) iconRect.getY(),
  462. (float) iconRect.getWidth(), (float) iconRect.getHeight(),
  463. Justification::centred, false);
  464. ga.createPath (icon);
  465. icon.setUsingNonZeroWinding (false);
  466. g.setColour (Colour (colour));
  467. g.fillPath (icon);
  468. iconSpaceUsed = iconWidth;
  469. }
  470. g.setColour (alert.findColour (AlertWindow::textColourId));
  471. textLayout.draw (g, Rectangle<int> (textArea.getX() + iconSpaceUsed,
  472. textArea.getY(),
  473. textArea.getWidth() - iconSpaceUsed,
  474. textArea.getHeight()).toFloat());
  475. g.setColour (alert.findColour (AlertWindow::outlineColourId));
  476. g.drawRect (0, 0, alert.getWidth(), alert.getHeight());
  477. }
  478. int LookAndFeel::getAlertBoxWindowFlags()
  479. {
  480. return ComponentPeer::windowAppearsOnTaskbar
  481. | ComponentPeer::windowHasDropShadow;
  482. }
  483. int LookAndFeel::getAlertWindowButtonHeight()
  484. {
  485. return 28;
  486. }
  487. const Font LookAndFeel::getAlertWindowMessageFont()
  488. {
  489. return Font (15.0f);
  490. }
  491. const Font LookAndFeel::getAlertWindowFont()
  492. {
  493. return Font (12.0f);
  494. }
  495. void LookAndFeel::setUsingNativeAlertWindows (bool shouldUseNativeAlerts)
  496. {
  497. useNativeAlertWindows = shouldUseNativeAlerts;
  498. }
  499. bool LookAndFeel::isUsingNativeAlertWindows()
  500. {
  501. #if JUCE_LINUX
  502. return false; // not available currently..
  503. #else
  504. return useNativeAlertWindows;
  505. #endif
  506. }
  507. //==============================================================================
  508. void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar,
  509. int width, int height,
  510. double progress, const String& textToShow)
  511. {
  512. const Colour background (progressBar.findColour (ProgressBar::backgroundColourId));
  513. const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId));
  514. g.fillAll (background);
  515. if (progress >= 0.0f && progress < 1.0f)
  516. {
  517. drawGlassLozenge (g, 1.0f, 1.0f,
  518. (float) jlimit (0.0, width - 2.0, progress * (width - 2.0)),
  519. (float) (height - 2),
  520. foreground,
  521. 0.5f, 0.0f,
  522. true, true, true, true);
  523. }
  524. else
  525. {
  526. // spinning bar..
  527. g.setColour (foreground);
  528. const int stripeWidth = height * 2;
  529. const int position = (int) (Time::getMillisecondCounter() / 15) % stripeWidth;
  530. Path p;
  531. for (float x = (float) (- position); x < width + stripeWidth; x += stripeWidth)
  532. p.addQuadrilateral (x, 0.0f,
  533. x + stripeWidth * 0.5f, 0.0f,
  534. x, (float) height,
  535. x - stripeWidth * 0.5f, (float) height);
  536. Image im (Image::ARGB, width, height, true);
  537. {
  538. Graphics g2 (im);
  539. drawGlassLozenge (g2, 1.0f, 1.0f,
  540. (float) (width - 2),
  541. (float) (height - 2),
  542. foreground,
  543. 0.5f, 0.0f,
  544. true, true, true, true);
  545. }
  546. g.setTiledImageFill (im, 0, 0, 0.85f);
  547. g.fillPath (p);
  548. }
  549. if (textToShow.isNotEmpty())
  550. {
  551. g.setColour (Colour::contrasting (background, foreground));
  552. g.setFont (height * 0.6f);
  553. g.drawText (textToShow, 0, 0, width, height, Justification::centred, false);
  554. }
  555. }
  556. void LookAndFeel::drawSpinningWaitAnimation (Graphics& g, const Colour& colour, int x, int y, int w, int h)
  557. {
  558. const float radius = jmin (w, h) * 0.4f;
  559. const float thickness = radius * 0.15f;
  560. Path p;
  561. p.addRoundedRectangle (radius * 0.4f, thickness * -0.5f,
  562. radius * 0.6f, thickness,
  563. thickness * 0.5f);
  564. const float cx = x + w * 0.5f;
  565. const float cy = y + h * 0.5f;
  566. const uint32 animationIndex = (Time::getMillisecondCounter() / (1000 / 10)) % 12;
  567. for (uint32 i = 0; i < 12; ++i)
  568. {
  569. const uint32 n = (i + 12 - animationIndex) % 12;
  570. g.setColour (colour.withMultipliedAlpha ((n + 1) / 12.0f));
  571. g.fillPath (p, AffineTransform::rotation (i * (float_Pi / 6.0f))
  572. .translated (cx, cy));
  573. }
  574. }
  575. void LookAndFeel::drawScrollbarButton (Graphics& g,
  576. ScrollBar& scrollbar,
  577. int width, int height,
  578. int buttonDirection,
  579. bool /*isScrollbarVertical*/,
  580. bool /*isMouseOverButton*/,
  581. bool isButtonDown)
  582. {
  583. Path p;
  584. if (buttonDirection == 0)
  585. p.addTriangle (width * 0.5f, height * 0.2f,
  586. width * 0.1f, height * 0.7f,
  587. width * 0.9f, height * 0.7f);
  588. else if (buttonDirection == 1)
  589. p.addTriangle (width * 0.8f, height * 0.5f,
  590. width * 0.3f, height * 0.1f,
  591. width * 0.3f, height * 0.9f);
  592. else if (buttonDirection == 2)
  593. p.addTriangle (width * 0.5f, height * 0.8f,
  594. width * 0.1f, height * 0.3f,
  595. width * 0.9f, height * 0.3f);
  596. else if (buttonDirection == 3)
  597. p.addTriangle (width * 0.2f, height * 0.5f,
  598. width * 0.7f, height * 0.1f,
  599. width * 0.7f, height * 0.9f);
  600. if (isButtonDown)
  601. g.setColour (scrollbar.findColour (ScrollBar::thumbColourId).contrasting (0.2f));
  602. else
  603. g.setColour (scrollbar.findColour (ScrollBar::thumbColourId));
  604. g.fillPath (p);
  605. g.setColour (Colour (0x80000000));
  606. g.strokePath (p, PathStrokeType (0.5f));
  607. }
  608. void LookAndFeel::drawScrollbar (Graphics& g,
  609. ScrollBar& scrollbar,
  610. int x, int y,
  611. int width, int height,
  612. bool isScrollbarVertical,
  613. int thumbStartPosition,
  614. int thumbSize,
  615. bool /*isMouseOver*/,
  616. bool /*isMouseDown*/)
  617. {
  618. g.fillAll (scrollbar.findColour (ScrollBar::backgroundColourId));
  619. Path slotPath, thumbPath;
  620. const float slotIndent = jmin (width, height) > 15 ? 1.0f : 0.0f;
  621. const float slotIndentx2 = slotIndent * 2.0f;
  622. const float thumbIndent = slotIndent + 1.0f;
  623. const float thumbIndentx2 = thumbIndent * 2.0f;
  624. float gx1 = 0.0f, gy1 = 0.0f, gx2 = 0.0f, gy2 = 0.0f;
  625. if (isScrollbarVertical)
  626. {
  627. slotPath.addRoundedRectangle (x + slotIndent,
  628. y + slotIndent,
  629. width - slotIndentx2,
  630. height - slotIndentx2,
  631. (width - slotIndentx2) * 0.5f);
  632. if (thumbSize > 0)
  633. thumbPath.addRoundedRectangle (x + thumbIndent,
  634. thumbStartPosition + thumbIndent,
  635. width - thumbIndentx2,
  636. thumbSize - thumbIndentx2,
  637. (width - thumbIndentx2) * 0.5f);
  638. gx1 = (float) x;
  639. gx2 = x + width * 0.7f;
  640. }
  641. else
  642. {
  643. slotPath.addRoundedRectangle (x + slotIndent,
  644. y + slotIndent,
  645. width - slotIndentx2,
  646. height - slotIndentx2,
  647. (height - slotIndentx2) * 0.5f);
  648. if (thumbSize > 0)
  649. thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent,
  650. y + thumbIndent,
  651. thumbSize - thumbIndentx2,
  652. height - thumbIndentx2,
  653. (height - thumbIndentx2) * 0.5f);
  654. gy1 = (float) y;
  655. gy2 = y + height * 0.7f;
  656. }
  657. const Colour thumbColour (scrollbar.findColour (ScrollBar::thumbColourId));
  658. Colour trackColour1, trackColour2;
  659. if (scrollbar.isColourSpecified (ScrollBar::trackColourId)
  660. || isColourSpecified (ScrollBar::trackColourId))
  661. {
  662. trackColour1 = trackColour2 = scrollbar.findColour (ScrollBar::trackColourId);
  663. }
  664. else
  665. {
  666. trackColour1 = thumbColour.overlaidWith (Colour (0x44000000));
  667. trackColour2 = thumbColour.overlaidWith (Colour (0x19000000));
  668. }
  669. g.setGradientFill (ColourGradient (trackColour1, gx1, gy1,
  670. trackColour2, gx2, gy2, false));
  671. g.fillPath (slotPath);
  672. if (isScrollbarVertical)
  673. {
  674. gx1 = x + width * 0.6f;
  675. gx2 = (float) x + width;
  676. }
  677. else
  678. {
  679. gy1 = y + height * 0.6f;
  680. gy2 = (float) y + height;
  681. }
  682. g.setGradientFill (ColourGradient (Colours::transparentBlack,gx1, gy1,
  683. Colour (0x19000000), gx2, gy2, false));
  684. g.fillPath (slotPath);
  685. g.setColour (thumbColour);
  686. g.fillPath (thumbPath);
  687. g.setGradientFill (ColourGradient (Colour (0x10000000), gx1, gy1,
  688. Colours::transparentBlack, gx2, gy2, false));
  689. g.saveState();
  690. if (isScrollbarVertical)
  691. g.reduceClipRegion (x + width / 2, y, width, height);
  692. else
  693. g.reduceClipRegion (x, y + height / 2, width, height);
  694. g.fillPath (thumbPath);
  695. g.restoreState();
  696. g.setColour (Colour (0x4c000000));
  697. g.strokePath (thumbPath, PathStrokeType (0.4f));
  698. }
  699. ImageEffectFilter* LookAndFeel::getScrollbarEffect()
  700. {
  701. return nullptr;
  702. }
  703. int LookAndFeel::getMinimumScrollbarThumbSize (ScrollBar& scrollbar)
  704. {
  705. return jmin (scrollbar.getWidth(), scrollbar.getHeight()) * 2;
  706. }
  707. int LookAndFeel::getDefaultScrollbarWidth()
  708. {
  709. return 18;
  710. }
  711. int LookAndFeel::getScrollbarButtonSize (ScrollBar& scrollbar)
  712. {
  713. return 2 + (scrollbar.isVertical() ? scrollbar.getWidth()
  714. : scrollbar.getHeight());
  715. }
  716. //==============================================================================
  717. const Path LookAndFeel::getTickShape (const float height)
  718. {
  719. static const unsigned char tickShapeData[] =
  720. {
  721. 109,0,224,168,68,0,0,119,67,108,0,224,172,68,0,128,146,67,113,0,192,148,68,0,0,219,67,0,96,110,68,0,224,56,68,113,0,64,51,68,0,32,130,68,0,64,20,68,0,224,
  722. 162,68,108,0,128,3,68,0,128,168,68,113,0,128,221,67,0,192,175,68,0,0,207,67,0,32,179,68,113,0,0,201,67,0,224,173,68,0,0,181,67,0,224,161,68,108,0,128,168,67,
  723. 0,128,154,68,113,0,128,141,67,0,192,138,68,0,128,108,67,0,64,131,68,113,0,0,62,67,0,128,119,68,0,0,5,67,0,128,114,68,113,0,0,102,67,0,192,88,68,0,128,155,
  724. 67,0,192,88,68,113,0,0,190,67,0,192,88,68,0,128,232,67,0,224,131,68,108,0,128,246,67,0,192,139,68,113,0,64,33,68,0,128,87,68,0,0,93,68,0,224,26,68,113,0,
  725. 96,140,68,0,128,188,67,0,224,168,68,0,0,119,67,99,101
  726. };
  727. Path p;
  728. p.loadPathFromData (tickShapeData, sizeof (tickShapeData));
  729. p.scaleToFit (0, 0, height * 2.0f, height, true);
  730. return p;
  731. }
  732. const Path LookAndFeel::getCrossShape (const float height)
  733. {
  734. static const unsigned char crossShapeData[] =
  735. {
  736. 109,0,0,17,68,0,96,145,68,108,0,192,13,68,0,192,147,68,113,0,0,213,67,0,64,174,68,0,0,168,67,0,64,174,68,113,0,0,104,67,0,64,174,68,0,0,5,67,0,64,
  737. 153,68,113,0,0,18,67,0,64,153,68,0,0,24,67,0,64,153,68,113,0,0,135,67,0,64,153,68,0,128,207,67,0,224,130,68,108,0,0,220,67,0,0,126,68,108,0,0,204,67,
  738. 0,128,117,68,113,0,0,138,67,0,64,82,68,0,0,138,67,0,192,57,68,113,0,0,138,67,0,192,37,68,0,128,210,67,0,64,10,68,113,0,128,220,67,0,64,45,68,0,0,8,
  739. 68,0,128,78,68,108,0,192,14,68,0,0,87,68,108,0,64,20,68,0,0,80,68,113,0,192,57,68,0,0,32,68,0,128,88,68,0,0,32,68,113,0,64,112,68,0,0,32,68,0,
  740. 128,124,68,0,64,68,68,113,0,0,121,68,0,192,67,68,0,128,119,68,0,192,67,68,113,0,192,108,68,0,192,67,68,0,32,89,68,0,96,82,68,113,0,128,69,68,0,0,97,68,
  741. 0,0,56,68,0,64,115,68,108,0,64,49,68,0,128,124,68,108,0,192,55,68,0,96,129,68,113,0,0,92,68,0,224,146,68,0,192,129,68,0,224,146,68,113,0,64,110,68,0,64,
  742. 168,68,0,64,87,68,0,64,168,68,113,0,128,66,68,0,64,168,68,0,64,27,68,0,32,150,68,99,101
  743. };
  744. Path p;
  745. p.loadPathFromData (crossShapeData, sizeof (crossShapeData));
  746. p.scaleToFit (0, 0, height * 2.0f, height, true);
  747. return p;
  748. }
  749. //==============================================================================
  750. void LookAndFeel::drawTreeviewPlusMinusBox (Graphics& g, int x, int y, int w, int h, bool isPlus, bool /*isMouseOver*/)
  751. {
  752. const int boxSize = ((jmin (16, w, h) << 1) / 3) | 1;
  753. x += (w - boxSize) >> 1;
  754. y += (h - boxSize) >> 1;
  755. w = boxSize;
  756. h = boxSize;
  757. g.setColour (Colour (0xe5ffffff));
  758. g.fillRect (x, y, w, h);
  759. g.setColour (Colour (0x80000000));
  760. g.drawRect (x, y, w, h);
  761. const float size = boxSize / 2 + 1.0f;
  762. const float centre = (float) (boxSize / 2);
  763. g.fillRect (x + (w - size) * 0.5f, y + centre, size, 1.0f);
  764. if (isPlus)
  765. g.fillRect (x + centre, y + (h - size) * 0.5f, 1.0f, size);
  766. }
  767. //==============================================================================
  768. void LookAndFeel::drawBubble (Graphics& g,
  769. float tipX, float tipY,
  770. float boxX, float boxY,
  771. float boxW, float boxH)
  772. {
  773. int side = 0;
  774. if (tipX < boxX)
  775. side = 1;
  776. else if (tipX > boxX + boxW)
  777. side = 3;
  778. else if (tipY > boxY + boxH)
  779. side = 2;
  780. const float indent = 2.0f;
  781. Path p;
  782. p.addBubble (boxX + indent,
  783. boxY + indent,
  784. boxW - indent * 2.0f,
  785. boxH - indent * 2.0f,
  786. 5.0f,
  787. tipX, tipY,
  788. side,
  789. 0.5f,
  790. jmin (15.0f, boxW * 0.3f, boxH * 0.3f));
  791. //xxx need to take comp as param for colour
  792. g.setColour (findColour (TooltipWindow::backgroundColourId).withAlpha (0.9f));
  793. g.fillPath (p);
  794. //xxx as above
  795. g.setColour (findColour (TooltipWindow::textColourId).withAlpha (0.4f));
  796. g.strokePath (p, PathStrokeType (1.33f));
  797. }
  798. //==============================================================================
  799. const Font LookAndFeel::getPopupMenuFont()
  800. {
  801. return Font (17.0f);
  802. }
  803. void LookAndFeel::getIdealPopupMenuItemSize (const String& text,
  804. const bool isSeparator,
  805. int standardMenuItemHeight,
  806. int& idealWidth,
  807. int& idealHeight)
  808. {
  809. if (isSeparator)
  810. {
  811. idealWidth = 50;
  812. idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight / 2 : 10;
  813. }
  814. else
  815. {
  816. Font font (getPopupMenuFont());
  817. if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f)
  818. font.setHeight (standardMenuItemHeight / 1.3f);
  819. idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundToInt (font.getHeight() * 1.3f);
  820. idealWidth = font.getStringWidth (text) + idealHeight * 2;
  821. }
  822. }
  823. void LookAndFeel::drawPopupMenuBackground (Graphics& g, int width, int height)
  824. {
  825. const Colour background (findColour (PopupMenu::backgroundColourId));
  826. g.fillAll (background);
  827. g.setColour (background.overlaidWith (Colour (0x2badd8e6)));
  828. for (int i = 0; i < height; i += 3)
  829. g.fillRect (0, i, width, 1);
  830. #if ! JUCE_MAC
  831. g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f));
  832. g.drawRect (0, 0, width, height);
  833. #endif
  834. }
  835. void LookAndFeel::drawPopupMenuUpDownArrow (Graphics& g,
  836. int width, int height,
  837. bool isScrollUpArrow)
  838. {
  839. const Colour background (findColour (PopupMenu::backgroundColourId));
  840. g.setGradientFill (ColourGradient (background, 0.0f, height * 0.5f,
  841. background.withAlpha (0.0f),
  842. 0.0f, isScrollUpArrow ? ((float) height) : 0.0f,
  843. false));
  844. g.fillRect (1, 1, width - 2, height - 2);
  845. const float hw = width * 0.5f;
  846. const float arrowW = height * 0.3f;
  847. const float y1 = height * (isScrollUpArrow ? 0.6f : 0.3f);
  848. const float y2 = height * (isScrollUpArrow ? 0.3f : 0.6f);
  849. Path p;
  850. p.addTriangle (hw - arrowW, y1,
  851. hw + arrowW, y1,
  852. hw, y2);
  853. g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.5f));
  854. g.fillPath (p);
  855. }
  856. void LookAndFeel::drawPopupMenuItem (Graphics& g,
  857. int width, int height,
  858. const bool isSeparator,
  859. const bool isActive,
  860. const bool isHighlighted,
  861. const bool isTicked,
  862. const bool hasSubMenu,
  863. const String& text,
  864. const String& shortcutKeyText,
  865. Image* image,
  866. const Colour* const textColourToUse)
  867. {
  868. const float halfH = height * 0.5f;
  869. if (isSeparator)
  870. {
  871. const float separatorIndent = 5.5f;
  872. g.setColour (Colour (0x33000000));
  873. g.drawLine (separatorIndent, halfH, width - separatorIndent, halfH);
  874. g.setColour (Colour (0x66ffffff));
  875. g.drawLine (separatorIndent, halfH + 1.0f, width - separatorIndent, halfH + 1.0f);
  876. }
  877. else
  878. {
  879. Colour textColour (findColour (PopupMenu::textColourId));
  880. if (textColourToUse != nullptr)
  881. textColour = *textColourToUse;
  882. if (isHighlighted)
  883. {
  884. g.setColour (findColour (PopupMenu::highlightedBackgroundColourId));
  885. g.fillRect (1, 1, width - 2, height - 2);
  886. g.setColour (findColour (PopupMenu::highlightedTextColourId));
  887. }
  888. else
  889. {
  890. g.setColour (textColour);
  891. }
  892. if (! isActive)
  893. g.setOpacity (0.3f);
  894. Font font (getPopupMenuFont());
  895. if (font.getHeight() > height / 1.3f)
  896. font.setHeight (height / 1.3f);
  897. g.setFont (font);
  898. const int leftBorder = (height * 5) / 4;
  899. const int rightBorder = 4;
  900. if (image != nullptr)
  901. {
  902. g.drawImageWithin (*image,
  903. 2, 1, leftBorder - 4, height - 2,
  904. RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false);
  905. }
  906. else if (isTicked)
  907. {
  908. const Path tick (getTickShape (1.0f));
  909. const float th = font.getAscent();
  910. const float ty = halfH - th * 0.5f;
  911. g.fillPath (tick, tick.getTransformToScaleToFit (2.0f, ty, (float) (leftBorder - 4),
  912. th, true));
  913. }
  914. g.drawFittedText (text,
  915. leftBorder, 0,
  916. width - (leftBorder + rightBorder), height,
  917. Justification::centredLeft, 1);
  918. if (shortcutKeyText.isNotEmpty())
  919. {
  920. Font f2 (font);
  921. f2.setHeight (f2.getHeight() * 0.75f);
  922. f2.setHorizontalScale (0.95f);
  923. g.setFont (f2);
  924. g.drawText (shortcutKeyText,
  925. leftBorder,
  926. 0,
  927. width - (leftBorder + rightBorder + 4),
  928. height,
  929. Justification::centredRight,
  930. true);
  931. }
  932. if (hasSubMenu)
  933. {
  934. const float arrowH = 0.6f * getPopupMenuFont().getAscent();
  935. const float x = width - height * 0.6f;
  936. Path p;
  937. p.addTriangle (x, halfH - arrowH * 0.5f,
  938. x, halfH + arrowH * 0.5f,
  939. x + arrowH * 0.6f, halfH);
  940. g.fillPath (p);
  941. }
  942. }
  943. }
  944. //==============================================================================
  945. int LookAndFeel::getMenuWindowFlags()
  946. {
  947. return ComponentPeer::windowHasDropShadow;
  948. }
  949. void LookAndFeel::drawMenuBarBackground (Graphics& g, int width, int height,
  950. bool, MenuBarComponent& menuBar)
  951. {
  952. const Colour baseColour (LookAndFeelHelpers::createBaseColour (menuBar.findColour (PopupMenu::backgroundColourId), false, false, false));
  953. if (menuBar.isEnabled())
  954. {
  955. drawShinyButtonShape (g,
  956. -4.0f, 0.0f,
  957. width + 8.0f, (float) height,
  958. 0.0f,
  959. baseColour,
  960. 0.4f,
  961. true, true, true, true);
  962. }
  963. else
  964. {
  965. g.fillAll (baseColour);
  966. }
  967. }
  968. const Font LookAndFeel::getMenuBarFont (MenuBarComponent& menuBar, int /*itemIndex*/, const String& /*itemText*/)
  969. {
  970. return Font (menuBar.getHeight() * 0.7f);
  971. }
  972. int LookAndFeel::getMenuBarItemWidth (MenuBarComponent& menuBar, int itemIndex, const String& itemText)
  973. {
  974. return getMenuBarFont (menuBar, itemIndex, itemText)
  975. .getStringWidth (itemText) + menuBar.getHeight();
  976. }
  977. void LookAndFeel::drawMenuBarItem (Graphics& g,
  978. int width, int height,
  979. int itemIndex,
  980. const String& itemText,
  981. bool isMouseOverItem,
  982. bool isMenuOpen,
  983. bool /*isMouseOverBar*/,
  984. MenuBarComponent& menuBar)
  985. {
  986. if (! menuBar.isEnabled())
  987. {
  988. g.setColour (menuBar.findColour (PopupMenu::textColourId)
  989. .withMultipliedAlpha (0.5f));
  990. }
  991. else if (isMenuOpen || isMouseOverItem)
  992. {
  993. g.fillAll (menuBar.findColour (PopupMenu::highlightedBackgroundColourId));
  994. g.setColour (menuBar.findColour (PopupMenu::highlightedTextColourId));
  995. }
  996. else
  997. {
  998. g.setColour (menuBar.findColour (PopupMenu::textColourId));
  999. }
  1000. g.setFont (getMenuBarFont (menuBar, itemIndex, itemText));
  1001. g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1);
  1002. }
  1003. //==============================================================================
  1004. void LookAndFeel::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/,
  1005. TextEditor& textEditor)
  1006. {
  1007. g.fillAll (textEditor.findColour (TextEditor::backgroundColourId));
  1008. }
  1009. void LookAndFeel::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor)
  1010. {
  1011. if (textEditor.isEnabled())
  1012. {
  1013. if (textEditor.hasKeyboardFocus (true) && ! textEditor.isReadOnly())
  1014. {
  1015. const int border = 2;
  1016. g.setColour (textEditor.findColour (TextEditor::focusedOutlineColourId));
  1017. g.drawRect (0, 0, width, height, border);
  1018. g.setOpacity (1.0f);
  1019. const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId).withMultipliedAlpha (0.75f));
  1020. g.drawBevel (0, 0, width, height + 2, border + 2, shadowColour, shadowColour);
  1021. }
  1022. else
  1023. {
  1024. g.setColour (textEditor.findColour (TextEditor::outlineColourId));
  1025. g.drawRect (0, 0, width, height);
  1026. g.setOpacity (1.0f);
  1027. const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId));
  1028. g.drawBevel (0, 0, width, height + 2, 3, shadowColour, shadowColour);
  1029. }
  1030. }
  1031. }
  1032. CaretComponent* LookAndFeel::createCaretComponent (Component* keyFocusOwner)
  1033. {
  1034. return new CaretComponent (keyFocusOwner);
  1035. }
  1036. //==============================================================================
  1037. void LookAndFeel::drawComboBox (Graphics& g, int width, int height,
  1038. const bool isButtonDown,
  1039. int buttonX, int buttonY,
  1040. int buttonW, int buttonH,
  1041. ComboBox& box)
  1042. {
  1043. g.fillAll (box.findColour (ComboBox::backgroundColourId));
  1044. if (box.isEnabled() && box.hasKeyboardFocus (false))
  1045. {
  1046. g.setColour (box.findColour (TextButton::buttonColourId));
  1047. g.drawRect (0, 0, width, height, 2);
  1048. }
  1049. else
  1050. {
  1051. g.setColour (box.findColour (ComboBox::outlineColourId));
  1052. g.drawRect (0, 0, width, height);
  1053. }
  1054. const float outlineThickness = box.isEnabled() ? (isButtonDown ? 1.2f : 0.5f) : 0.3f;
  1055. const Colour baseColour (LookAndFeelHelpers::createBaseColour (box.findColour (ComboBox::buttonColourId),
  1056. box.hasKeyboardFocus (true),
  1057. false, isButtonDown)
  1058. .withMultipliedAlpha (box.isEnabled() ? 1.0f : 0.5f));
  1059. drawGlassLozenge (g,
  1060. buttonX + outlineThickness, buttonY + outlineThickness,
  1061. buttonW - outlineThickness * 2.0f, buttonH - outlineThickness * 2.0f,
  1062. baseColour, outlineThickness, -1.0f,
  1063. true, true, true, true);
  1064. if (box.isEnabled())
  1065. {
  1066. const float arrowX = 0.3f;
  1067. const float arrowH = 0.2f;
  1068. Path p;
  1069. p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH),
  1070. buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f,
  1071. buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f);
  1072. p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH),
  1073. buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f,
  1074. buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f);
  1075. g.setColour (box.findColour (ComboBox::arrowColourId));
  1076. g.fillPath (p);
  1077. }
  1078. }
  1079. const Font LookAndFeel::getComboBoxFont (ComboBox& box)
  1080. {
  1081. return Font (jmin (15.0f, box.getHeight() * 0.85f));
  1082. }
  1083. Label* LookAndFeel::createComboBoxTextBox (ComboBox&)
  1084. {
  1085. return new Label (String::empty, String::empty);
  1086. }
  1087. void LookAndFeel::positionComboBoxText (ComboBox& box, Label& label)
  1088. {
  1089. label.setBounds (1, 1,
  1090. box.getWidth() + 3 - box.getHeight(),
  1091. box.getHeight() - 2);
  1092. label.setFont (getComboBoxFont (box));
  1093. }
  1094. //==============================================================================
  1095. void LookAndFeel::drawLabel (Graphics& g, Label& label)
  1096. {
  1097. g.fillAll (label.findColour (Label::backgroundColourId));
  1098. if (! label.isBeingEdited())
  1099. {
  1100. const float alpha = label.isEnabled() ? 1.0f : 0.5f;
  1101. g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha));
  1102. g.setFont (label.getFont());
  1103. g.drawFittedText (label.getText(),
  1104. label.getHorizontalBorderSize(),
  1105. label.getVerticalBorderSize(),
  1106. label.getWidth() - 2 * label.getHorizontalBorderSize(),
  1107. label.getHeight() - 2 * label.getVerticalBorderSize(),
  1108. label.getJustificationType(),
  1109. jmax (1, (int) (label.getHeight() / label.getFont().getHeight())),
  1110. label.getMinimumHorizontalScale());
  1111. g.setColour (label.findColour (Label::outlineColourId).withMultipliedAlpha (alpha));
  1112. g.drawRect (0, 0, label.getWidth(), label.getHeight());
  1113. }
  1114. else if (label.isEnabled())
  1115. {
  1116. g.setColour (label.findColour (Label::outlineColourId));
  1117. g.drawRect (0, 0, label.getWidth(), label.getHeight());
  1118. }
  1119. }
  1120. //==============================================================================
  1121. void LookAndFeel::drawLinearSliderBackground (Graphics& g,
  1122. int x, int y,
  1123. int width, int height,
  1124. float /*sliderPos*/,
  1125. float /*minSliderPos*/,
  1126. float /*maxSliderPos*/,
  1127. const Slider::SliderStyle /*style*/,
  1128. Slider& slider)
  1129. {
  1130. const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2);
  1131. const Colour trackColour (slider.findColour (Slider::trackColourId));
  1132. const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f)));
  1133. const Colour gradCol2 (trackColour.overlaidWith (Colour (0x14000000)));
  1134. Path indent;
  1135. if (slider.isHorizontal())
  1136. {
  1137. const float iy = y + height * 0.5f - sliderRadius * 0.5f;
  1138. const float ih = sliderRadius;
  1139. g.setGradientFill (ColourGradient (gradCol1, 0.0f, iy,
  1140. gradCol2, 0.0f, iy + ih, false));
  1141. indent.addRoundedRectangle (x - sliderRadius * 0.5f, iy,
  1142. width + sliderRadius, ih,
  1143. 5.0f);
  1144. g.fillPath (indent);
  1145. }
  1146. else
  1147. {
  1148. const float ix = x + width * 0.5f - sliderRadius * 0.5f;
  1149. const float iw = sliderRadius;
  1150. g.setGradientFill (ColourGradient (gradCol1, ix, 0.0f,
  1151. gradCol2, ix + iw, 0.0f, false));
  1152. indent.addRoundedRectangle (ix, y - sliderRadius * 0.5f,
  1153. iw, height + sliderRadius,
  1154. 5.0f);
  1155. g.fillPath (indent);
  1156. }
  1157. g.setColour (Colour (0x4c000000));
  1158. g.strokePath (indent, PathStrokeType (0.5f));
  1159. }
  1160. void LookAndFeel::drawLinearSliderThumb (Graphics& g,
  1161. int x, int y,
  1162. int width, int height,
  1163. float sliderPos,
  1164. float minSliderPos,
  1165. float maxSliderPos,
  1166. const Slider::SliderStyle style,
  1167. Slider& slider)
  1168. {
  1169. const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2);
  1170. Colour knobColour (LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId),
  1171. slider.hasKeyboardFocus (false) && slider.isEnabled(),
  1172. slider.isMouseOverOrDragging() && slider.isEnabled(),
  1173. slider.isMouseButtonDown() && slider.isEnabled()));
  1174. const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;
  1175. if (style == Slider::LinearHorizontal || style == Slider::LinearVertical)
  1176. {
  1177. float kx, ky;
  1178. if (style == Slider::LinearVertical)
  1179. {
  1180. kx = x + width * 0.5f;
  1181. ky = sliderPos;
  1182. }
  1183. else
  1184. {
  1185. kx = sliderPos;
  1186. ky = y + height * 0.5f;
  1187. }
  1188. drawGlassSphere (g,
  1189. kx - sliderRadius,
  1190. ky - sliderRadius,
  1191. sliderRadius * 2.0f,
  1192. knobColour, outlineThickness);
  1193. }
  1194. else
  1195. {
  1196. if (style == Slider::ThreeValueVertical)
  1197. {
  1198. drawGlassSphere (g, x + width * 0.5f - sliderRadius,
  1199. sliderPos - sliderRadius,
  1200. sliderRadius * 2.0f,
  1201. knobColour, outlineThickness);
  1202. }
  1203. else if (style == Slider::ThreeValueHorizontal)
  1204. {
  1205. drawGlassSphere (g,sliderPos - sliderRadius,
  1206. y + height * 0.5f - sliderRadius,
  1207. sliderRadius * 2.0f,
  1208. knobColour, outlineThickness);
  1209. }
  1210. if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical)
  1211. {
  1212. const float sr = jmin (sliderRadius, width * 0.4f);
  1213. drawGlassPointer (g, jmax (0.0f, x + width * 0.5f - sliderRadius * 2.0f),
  1214. minSliderPos - sliderRadius,
  1215. sliderRadius * 2.0f, knobColour, outlineThickness, 1);
  1216. drawGlassPointer (g, jmin (x + width - sliderRadius * 2.0f, x + width * 0.5f), maxSliderPos - sr,
  1217. sliderRadius * 2.0f, knobColour, outlineThickness, 3);
  1218. }
  1219. else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal)
  1220. {
  1221. const float sr = jmin (sliderRadius, height * 0.4f);
  1222. drawGlassPointer (g, minSliderPos - sr,
  1223. jmax (0.0f, y + height * 0.5f - sliderRadius * 2.0f),
  1224. sliderRadius * 2.0f, knobColour, outlineThickness, 2);
  1225. drawGlassPointer (g, maxSliderPos - sliderRadius,
  1226. jmin (y + height - sliderRadius * 2.0f, y + height * 0.5f),
  1227. sliderRadius * 2.0f, knobColour, outlineThickness, 4);
  1228. }
  1229. }
  1230. }
  1231. void LookAndFeel::drawLinearSlider (Graphics& g,
  1232. int x, int y,
  1233. int width, int height,
  1234. float sliderPos,
  1235. float minSliderPos,
  1236. float maxSliderPos,
  1237. const Slider::SliderStyle style,
  1238. Slider& slider)
  1239. {
  1240. g.fillAll (slider.findColour (Slider::backgroundColourId));
  1241. if (style == Slider::LinearBar)
  1242. {
  1243. const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
  1244. Colour baseColour (LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId)
  1245. .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f),
  1246. false, isMouseOver,
  1247. isMouseOver || slider.isMouseButtonDown()));
  1248. drawShinyButtonShape (g,
  1249. (float) x, (float) y, sliderPos - (float) x, (float) height, 0.0f,
  1250. baseColour,
  1251. slider.isEnabled() ? 0.9f : 0.3f,
  1252. true, true, true, true);
  1253. }
  1254. else
  1255. {
  1256. drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
  1257. drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
  1258. }
  1259. }
  1260. int LookAndFeel::getSliderThumbRadius (Slider& slider)
  1261. {
  1262. return jmin (7,
  1263. slider.getHeight() / 2,
  1264. slider.getWidth() / 2) + 2;
  1265. }
  1266. void LookAndFeel::drawRotarySlider (Graphics& g,
  1267. int x, int y,
  1268. int width, int height,
  1269. float sliderPos,
  1270. const float rotaryStartAngle,
  1271. const float rotaryEndAngle,
  1272. Slider& slider)
  1273. {
  1274. const float radius = jmin (width / 2, height / 2) - 2.0f;
  1275. const float centreX = x + width * 0.5f;
  1276. const float centreY = y + height * 0.5f;
  1277. const float rx = centreX - radius;
  1278. const float ry = centreY - radius;
  1279. const float rw = radius * 2.0f;
  1280. const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
  1281. const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
  1282. if (radius > 12.0f)
  1283. {
  1284. if (slider.isEnabled())
  1285. g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
  1286. else
  1287. g.setColour (Colour (0x80808080));
  1288. const float thickness = 0.7f;
  1289. {
  1290. Path filledArc;
  1291. filledArc.addPieSegment (rx, ry, rw, rw,
  1292. rotaryStartAngle,
  1293. angle,
  1294. thickness);
  1295. g.fillPath (filledArc);
  1296. }
  1297. if (thickness > 0)
  1298. {
  1299. const float innerRadius = radius * 0.2f;
  1300. Path p;
  1301. p.addTriangle (-innerRadius, 0.0f,
  1302. 0.0f, -radius * thickness * 1.1f,
  1303. innerRadius, 0.0f);
  1304. p.addEllipse (-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f);
  1305. g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY));
  1306. }
  1307. if (slider.isEnabled())
  1308. g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId));
  1309. else
  1310. g.setColour (Colour (0x80808080));
  1311. Path outlineArc;
  1312. outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness);
  1313. outlineArc.closeSubPath();
  1314. g.strokePath (outlineArc, PathStrokeType (slider.isEnabled() ? (isMouseOver ? 2.0f : 1.2f) : 0.3f));
  1315. }
  1316. else
  1317. {
  1318. if (slider.isEnabled())
  1319. g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
  1320. else
  1321. g.setColour (Colour (0x80808080));
  1322. Path p;
  1323. p.addEllipse (-0.4f * rw, -0.4f * rw, rw * 0.8f, rw * 0.8f);
  1324. PathStrokeType (rw * 0.1f).createStrokedPath (p, p);
  1325. p.addLineSegment (Line<float> (0.0f, 0.0f, 0.0f, -radius), rw * 0.2f);
  1326. g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY));
  1327. }
  1328. }
  1329. Button* LookAndFeel::createSliderButton (const bool isIncrement)
  1330. {
  1331. return new TextButton (isIncrement ? "+" : "-", String::empty);
  1332. }
  1333. class SliderLabelComp : public Label
  1334. {
  1335. public:
  1336. SliderLabelComp() : Label (String::empty, String::empty) {}
  1337. void mouseWheelMove (const MouseEvent&, float, float) {}
  1338. };
  1339. Label* LookAndFeel::createSliderTextBox (Slider& slider)
  1340. {
  1341. Label* const l = new SliderLabelComp();
  1342. l->setJustificationType (Justification::centred);
  1343. l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId));
  1344. l->setColour (Label::backgroundColourId,
  1345. (slider.getSliderStyle() == Slider::LinearBar) ? Colours::transparentBlack
  1346. : slider.findColour (Slider::textBoxBackgroundColourId));
  1347. l->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));
  1348. l->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId));
  1349. l->setColour (TextEditor::backgroundColourId,
  1350. slider.findColour (Slider::textBoxBackgroundColourId)
  1351. .withAlpha (slider.getSliderStyle() == Slider::LinearBar ? 0.7f : 1.0f));
  1352. l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));
  1353. return l;
  1354. }
  1355. ImageEffectFilter* LookAndFeel::getSliderEffect()
  1356. {
  1357. return nullptr;
  1358. }
  1359. //==============================================================================
  1360. void LookAndFeel::getTooltipSize (const String& tipText, int& width, int& height)
  1361. {
  1362. const TextLayout tl (LookAndFeelHelpers::layoutTooltipText (tipText));
  1363. width = (int) (tl.getWidth() + 14.0f);
  1364. height = (int) (tl.getHeight() + 6.0f);
  1365. }
  1366. void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height)
  1367. {
  1368. g.fillAll (findColour (TooltipWindow::backgroundColourId));
  1369. #if ! JUCE_MAC // The mac windows already have a non-optional 1 pix outline, so don't double it here..
  1370. g.setColour (findColour (TooltipWindow::outlineColourId));
  1371. g.drawRect (0, 0, width, height, 1);
  1372. #endif
  1373. const TextLayout tl (LookAndFeelHelpers::layoutTooltipText (text));
  1374. g.setColour (findColour (TooltipWindow::textColourId));
  1375. tl.draw (g, Rectangle<float> (0.0f, 0.0f, (float) width, (float) height));
  1376. }
  1377. //==============================================================================
  1378. Button* LookAndFeel::createFilenameComponentBrowseButton (const String& text)
  1379. {
  1380. return new TextButton (text, TRANS("click to browse for a different file"));
  1381. }
  1382. void LookAndFeel::layoutFilenameComponent (FilenameComponent& filenameComp,
  1383. ComboBox* filenameBox,
  1384. Button* browseButton)
  1385. {
  1386. browseButton->setSize (80, filenameComp.getHeight());
  1387. TextButton* const tb = dynamic_cast <TextButton*> (browseButton);
  1388. if (tb != nullptr)
  1389. tb->changeWidthToFitText();
  1390. browseButton->setTopRightPosition (filenameComp.getWidth(), 0);
  1391. filenameBox->setBounds (0, 0, browseButton->getX(), filenameComp.getHeight());
  1392. }
  1393. //==============================================================================
  1394. void LookAndFeel::drawImageButton (Graphics& g, Image* image,
  1395. int imageX, int imageY, int imageW, int imageH,
  1396. const Colour& overlayColour,
  1397. float imageOpacity,
  1398. ImageButton& button)
  1399. {
  1400. if (! button.isEnabled())
  1401. imageOpacity *= 0.3f;
  1402. if (! overlayColour.isOpaque())
  1403. {
  1404. g.setOpacity (imageOpacity);
  1405. g.drawImage (*image, imageX, imageY, imageW, imageH,
  1406. 0, 0, image->getWidth(), image->getHeight(), false);
  1407. }
  1408. if (! overlayColour.isTransparent())
  1409. {
  1410. g.setColour (overlayColour);
  1411. g.drawImage (*image, imageX, imageY, imageW, imageH,
  1412. 0, 0, image->getWidth(), image->getHeight(), true);
  1413. }
  1414. }
  1415. //==============================================================================
  1416. void LookAndFeel::drawCornerResizer (Graphics& g,
  1417. int w, int h,
  1418. bool /*isMouseOver*/,
  1419. bool /*isMouseDragging*/)
  1420. {
  1421. const float lineThickness = jmin (w, h) * 0.075f;
  1422. for (float i = 0.0f; i < 1.0f; i += 0.3f)
  1423. {
  1424. g.setColour (Colours::lightgrey);
  1425. g.drawLine (w * i,
  1426. h + 1.0f,
  1427. w + 1.0f,
  1428. h * i,
  1429. lineThickness);
  1430. g.setColour (Colours::darkgrey);
  1431. g.drawLine (w * i + lineThickness,
  1432. h + 1.0f,
  1433. w + 1.0f,
  1434. h * i + lineThickness,
  1435. lineThickness);
  1436. }
  1437. }
  1438. void LookAndFeel::drawResizableFrame (Graphics& g, int w, int h, const BorderSize<int>& border)
  1439. {
  1440. if (! border.isEmpty())
  1441. {
  1442. const Rectangle<int> fullSize (0, 0, w, h);
  1443. const Rectangle<int> centreArea (border.subtractedFrom (fullSize));
  1444. g.saveState();
  1445. g.excludeClipRegion (centreArea);
  1446. g.setColour (Colour (0x50000000));
  1447. g.drawRect (fullSize);
  1448. g.setColour (Colour (0x19000000));
  1449. g.drawRect (centreArea.expanded (1, 1));
  1450. g.restoreState();
  1451. }
  1452. }
  1453. //==============================================================================
  1454. void LookAndFeel::fillResizableWindowBackground (Graphics& g, int /*w*/, int /*h*/,
  1455. const BorderSize<int>& /*border*/, ResizableWindow& window)
  1456. {
  1457. g.fillAll (window.getBackgroundColour());
  1458. }
  1459. void LookAndFeel::drawResizableWindowBorder (Graphics&, int /*w*/, int /*h*/,
  1460. const BorderSize<int>& /*border*/, ResizableWindow&)
  1461. {
  1462. }
  1463. void LookAndFeel::drawDocumentWindowTitleBar (DocumentWindow& window,
  1464. Graphics& g, int w, int h,
  1465. int titleSpaceX, int titleSpaceW,
  1466. const Image* icon,
  1467. bool drawTitleTextOnLeft)
  1468. {
  1469. const bool isActive = window.isActiveWindow();
  1470. g.setGradientFill (ColourGradient (window.getBackgroundColour(),
  1471. 0.0f, 0.0f,
  1472. window.getBackgroundColour().contrasting (isActive ? 0.15f : 0.05f),
  1473. 0.0f, (float) h, false));
  1474. g.fillAll();
  1475. Font font (h * 0.65f, Font::bold);
  1476. g.setFont (font);
  1477. int textW = font.getStringWidth (window.getName());
  1478. int iconW = 0;
  1479. int iconH = 0;
  1480. if (icon != nullptr)
  1481. {
  1482. iconH = (int) font.getHeight();
  1483. iconW = icon->getWidth() * iconH / icon->getHeight() + 4;
  1484. }
  1485. textW = jmin (titleSpaceW, textW + iconW);
  1486. int textX = drawTitleTextOnLeft ? titleSpaceX
  1487. : jmax (titleSpaceX, (w - textW) / 2);
  1488. if (textX + textW > titleSpaceX + titleSpaceW)
  1489. textX = titleSpaceX + titleSpaceW - textW;
  1490. if (icon != nullptr)
  1491. {
  1492. g.setOpacity (isActive ? 1.0f : 0.6f);
  1493. g.drawImageWithin (*icon, textX, (h - iconH) / 2, iconW, iconH,
  1494. RectanglePlacement::centred, false);
  1495. textX += iconW;
  1496. textW -= iconW;
  1497. }
  1498. if (window.isColourSpecified (DocumentWindow::textColourId) || isColourSpecified (DocumentWindow::textColourId))
  1499. g.setColour (findColour (DocumentWindow::textColourId));
  1500. else
  1501. g.setColour (window.getBackgroundColour().contrasting (isActive ? 0.7f : 0.4f));
  1502. g.drawText (window.getName(), textX, 0, textW, h, Justification::centredLeft, true);
  1503. }
  1504. //==============================================================================
  1505. class GlassWindowButton : public Button
  1506. {
  1507. public:
  1508. //==============================================================================
  1509. GlassWindowButton (const String& name, const Colour& col,
  1510. const Path& normalShape_,
  1511. const Path& toggledShape_) noexcept
  1512. : Button (name),
  1513. colour (col),
  1514. normalShape (normalShape_),
  1515. toggledShape (toggledShape_)
  1516. {
  1517. }
  1518. ~GlassWindowButton()
  1519. {
  1520. }
  1521. //==============================================================================
  1522. void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown)
  1523. {
  1524. float alpha = isMouseOverButton ? (isButtonDown ? 1.0f : 0.8f) : 0.55f;
  1525. if (! isEnabled())
  1526. alpha *= 0.5f;
  1527. float x = 0, y = 0, diam;
  1528. if (getWidth() < getHeight())
  1529. {
  1530. diam = (float) getWidth();
  1531. y = (getHeight() - getWidth()) * 0.5f;
  1532. }
  1533. else
  1534. {
  1535. diam = (float) getHeight();
  1536. y = (getWidth() - getHeight()) * 0.5f;
  1537. }
  1538. x += diam * 0.05f;
  1539. y += diam * 0.05f;
  1540. diam *= 0.9f;
  1541. g.setGradientFill (ColourGradient (Colour::greyLevel (0.9f).withAlpha (alpha), 0, y + diam,
  1542. Colour::greyLevel (0.6f).withAlpha (alpha), 0, y, false));
  1543. g.fillEllipse (x, y, diam, diam);
  1544. x += 2.0f;
  1545. y += 2.0f;
  1546. diam -= 4.0f;
  1547. LookAndFeel::drawGlassSphere (g, x, y, diam, colour.withAlpha (alpha), 1.0f);
  1548. Path& p = getToggleState() ? toggledShape : normalShape;
  1549. const AffineTransform t (p.getTransformToScaleToFit (x + diam * 0.3f, y + diam * 0.3f,
  1550. diam * 0.4f, diam * 0.4f, true));
  1551. g.setColour (Colours::black.withAlpha (alpha * 0.6f));
  1552. g.fillPath (p, t);
  1553. }
  1554. private:
  1555. Colour colour;
  1556. Path normalShape, toggledShape;
  1557. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlassWindowButton);
  1558. };
  1559. Button* LookAndFeel::createDocumentWindowButton (int buttonType)
  1560. {
  1561. Path shape;
  1562. const float crossThickness = 0.25f;
  1563. if (buttonType == DocumentWindow::closeButton)
  1564. {
  1565. shape.addLineSegment (Line<float> (0.0f, 0.0f, 1.0f, 1.0f), crossThickness * 1.4f);
  1566. shape.addLineSegment (Line<float> (1.0f, 0.0f, 0.0f, 1.0f), crossThickness * 1.4f);
  1567. return new GlassWindowButton ("close", Colour (0xffdd1100), shape, shape);
  1568. }
  1569. else if (buttonType == DocumentWindow::minimiseButton)
  1570. {
  1571. shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), crossThickness);
  1572. return new GlassWindowButton ("minimise", Colour (0xffaa8811), shape, shape);
  1573. }
  1574. else if (buttonType == DocumentWindow::maximiseButton)
  1575. {
  1576. shape.addLineSegment (Line<float> (0.5f, 0.0f, 0.5f, 1.0f), crossThickness);
  1577. shape.addLineSegment (Line<float> (0.0f, 0.5f, 1.0f, 0.5f), crossThickness);
  1578. Path fullscreenShape;
  1579. fullscreenShape.startNewSubPath (45.0f, 100.0f);
  1580. fullscreenShape.lineTo (0.0f, 100.0f);
  1581. fullscreenShape.lineTo (0.0f, 0.0f);
  1582. fullscreenShape.lineTo (100.0f, 0.0f);
  1583. fullscreenShape.lineTo (100.0f, 45.0f);
  1584. fullscreenShape.addRectangle (45.0f, 45.0f, 100.0f, 100.0f);
  1585. PathStrokeType (30.0f).createStrokedPath (fullscreenShape, fullscreenShape);
  1586. return new GlassWindowButton ("maximise", Colour (0xff119911), shape, fullscreenShape);
  1587. }
  1588. jassertfalse;
  1589. return nullptr;
  1590. }
  1591. void LookAndFeel::positionDocumentWindowButtons (DocumentWindow&,
  1592. int titleBarX,
  1593. int titleBarY,
  1594. int titleBarW,
  1595. int titleBarH,
  1596. Button* minimiseButton,
  1597. Button* maximiseButton,
  1598. Button* closeButton,
  1599. bool positionTitleBarButtonsOnLeft)
  1600. {
  1601. const int buttonW = titleBarH - titleBarH / 8;
  1602. int x = positionTitleBarButtonsOnLeft ? titleBarX + 4
  1603. : titleBarX + titleBarW - buttonW - buttonW / 4;
  1604. if (closeButton != nullptr)
  1605. {
  1606. closeButton->setBounds (x, titleBarY, buttonW, titleBarH);
  1607. x += positionTitleBarButtonsOnLeft ? buttonW : -(buttonW + buttonW / 4);
  1608. }
  1609. if (positionTitleBarButtonsOnLeft)
  1610. std::swap (minimiseButton, maximiseButton);
  1611. if (maximiseButton != nullptr)
  1612. {
  1613. maximiseButton->setBounds (x, titleBarY, buttonW, titleBarH);
  1614. x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW;
  1615. }
  1616. if (minimiseButton != nullptr)
  1617. minimiseButton->setBounds (x, titleBarY, buttonW, titleBarH);
  1618. }
  1619. int LookAndFeel::getDefaultMenuBarHeight()
  1620. {
  1621. return 24;
  1622. }
  1623. //==============================================================================
  1624. DropShadower* LookAndFeel::createDropShadowerForComponent (Component*)
  1625. {
  1626. return new DropShadower (0.4f, 1, 5, 10);
  1627. }
  1628. //==============================================================================
  1629. void LookAndFeel::drawStretchableLayoutResizerBar (Graphics& g,
  1630. int w, int h,
  1631. bool /*isVerticalBar*/,
  1632. bool isMouseOver,
  1633. bool isMouseDragging)
  1634. {
  1635. float alpha = 0.5f;
  1636. if (isMouseOver || isMouseDragging)
  1637. {
  1638. g.fillAll (Colour (0x190000ff));
  1639. alpha = 1.0f;
  1640. }
  1641. const float cx = w * 0.5f;
  1642. const float cy = h * 0.5f;
  1643. const float cr = jmin (w, h) * 0.4f;
  1644. g.setGradientFill (ColourGradient (Colours::white.withAlpha (alpha), cx + cr * 0.1f, cy + cr,
  1645. Colours::black.withAlpha (alpha), cx, cy - cr * 4.0f,
  1646. true));
  1647. g.fillEllipse (cx - cr, cy - cr, cr * 2.0f, cr * 2.0f);
  1648. }
  1649. //==============================================================================
  1650. void LookAndFeel::drawGroupComponentOutline (Graphics& g, int width, int height,
  1651. const String& text,
  1652. const Justification& position,
  1653. GroupComponent& group)
  1654. {
  1655. const float textH = 15.0f;
  1656. const float indent = 3.0f;
  1657. const float textEdgeGap = 4.0f;
  1658. float cs = 5.0f;
  1659. Font f (textH);
  1660. Path p;
  1661. float x = indent;
  1662. float y = f.getAscent() - 3.0f;
  1663. float w = jmax (0.0f, width - x * 2.0f);
  1664. float h = jmax (0.0f, height - y - indent);
  1665. cs = jmin (cs, w * 0.5f, h * 0.5f);
  1666. const float cs2 = 2.0f * cs;
  1667. float textW = text.isEmpty() ? 0 : jlimit (0.0f, jmax (0.0f, w - cs2 - textEdgeGap * 2), f.getStringWidth (text) + textEdgeGap * 2.0f);
  1668. float textX = cs + textEdgeGap;
  1669. if (position.testFlags (Justification::horizontallyCentred))
  1670. textX = cs + (w - cs2 - textW) * 0.5f;
  1671. else if (position.testFlags (Justification::right))
  1672. textX = w - cs - textW - textEdgeGap;
  1673. p.startNewSubPath (x + textX + textW, y);
  1674. p.lineTo (x + w - cs, y);
  1675. p.addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f);
  1676. p.lineTo (x + w, y + h - cs);
  1677. p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi);
  1678. p.lineTo (x + cs, y + h);
  1679. p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f);
  1680. p.lineTo (x, y + cs);
  1681. p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f);
  1682. p.lineTo (x + textX, y);
  1683. const float alpha = group.isEnabled() ? 1.0f : 0.5f;
  1684. g.setColour (group.findColour (GroupComponent::outlineColourId)
  1685. .withMultipliedAlpha (alpha));
  1686. g.strokePath (p, PathStrokeType (2.0f));
  1687. g.setColour (group.findColour (GroupComponent::textColourId)
  1688. .withMultipliedAlpha (alpha));
  1689. g.setFont (f);
  1690. g.drawText (text,
  1691. roundToInt (x + textX), 0,
  1692. roundToInt (textW),
  1693. roundToInt (textH),
  1694. Justification::centred, true);
  1695. }
  1696. //==============================================================================
  1697. int LookAndFeel::getTabButtonOverlap (int tabDepth)
  1698. {
  1699. return 1 + tabDepth / 3;
  1700. }
  1701. int LookAndFeel::getTabButtonSpaceAroundImage()
  1702. {
  1703. return 4;
  1704. }
  1705. void LookAndFeel::createTabButtonShape (Path& p,
  1706. int width, int height,
  1707. int /*tabIndex*/,
  1708. const String& /*text*/,
  1709. Button& /*button*/,
  1710. TabbedButtonBar::Orientation orientation,
  1711. const bool /*isMouseOver*/,
  1712. const bool /*isMouseDown*/,
  1713. const bool /*isFrontTab*/)
  1714. {
  1715. const float w = (float) width;
  1716. const float h = (float) height;
  1717. float length = w;
  1718. float depth = h;
  1719. if (orientation == TabbedButtonBar::TabsAtLeft
  1720. || orientation == TabbedButtonBar::TabsAtRight)
  1721. {
  1722. std::swap (length, depth);
  1723. }
  1724. const float indent = (float) getTabButtonOverlap ((int) depth);
  1725. const float overhang = 4.0f;
  1726. if (orientation == TabbedButtonBar::TabsAtLeft)
  1727. {
  1728. p.startNewSubPath (w, 0.0f);
  1729. p.lineTo (0.0f, indent);
  1730. p.lineTo (0.0f, h - indent);
  1731. p.lineTo (w, h);
  1732. p.lineTo (w + overhang, h + overhang);
  1733. p.lineTo (w + overhang, -overhang);
  1734. }
  1735. else if (orientation == TabbedButtonBar::TabsAtRight)
  1736. {
  1737. p.startNewSubPath (0.0f, 0.0f);
  1738. p.lineTo (w, indent);
  1739. p.lineTo (w, h - indent);
  1740. p.lineTo (0.0f, h);
  1741. p.lineTo (-overhang, h + overhang);
  1742. p.lineTo (-overhang, -overhang);
  1743. }
  1744. else if (orientation == TabbedButtonBar::TabsAtBottom)
  1745. {
  1746. p.startNewSubPath (0.0f, 0.0f);
  1747. p.lineTo (indent, h);
  1748. p.lineTo (w - indent, h);
  1749. p.lineTo (w, 0.0f);
  1750. p.lineTo (w + overhang, -overhang);
  1751. p.lineTo (-overhang, -overhang);
  1752. }
  1753. else
  1754. {
  1755. p.startNewSubPath (0.0f, h);
  1756. p.lineTo (indent, 0.0f);
  1757. p.lineTo (w - indent, 0.0f);
  1758. p.lineTo (w, h);
  1759. p.lineTo (w + overhang, h + overhang);
  1760. p.lineTo (-overhang, h + overhang);
  1761. }
  1762. p.closeSubPath();
  1763. p = p.createPathWithRoundedCorners (3.0f);
  1764. }
  1765. void LookAndFeel::fillTabButtonShape (Graphics& g,
  1766. const Path& path,
  1767. const Colour& preferredColour,
  1768. int /*tabIndex*/,
  1769. const String& /*text*/,
  1770. Button& button,
  1771. TabbedButtonBar::Orientation /*orientation*/,
  1772. const bool /*isMouseOver*/,
  1773. const bool /*isMouseDown*/,
  1774. const bool isFrontTab)
  1775. {
  1776. g.setColour (isFrontTab ? preferredColour
  1777. : preferredColour.withMultipliedAlpha (0.9f));
  1778. g.fillPath (path);
  1779. g.setColour (button.findColour (isFrontTab ? TabbedButtonBar::frontOutlineColourId
  1780. : TabbedButtonBar::tabOutlineColourId, false)
  1781. .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f));
  1782. g.strokePath (path, PathStrokeType (isFrontTab ? 1.0f : 0.5f));
  1783. }
  1784. void LookAndFeel::drawTabButtonText (Graphics& g,
  1785. int x, int y, int w, int h,
  1786. const Colour& preferredBackgroundColour,
  1787. int /*tabIndex*/,
  1788. const String& text,
  1789. Button& button,
  1790. TabbedButtonBar::Orientation orientation,
  1791. const bool isMouseOver,
  1792. const bool isMouseDown,
  1793. const bool isFrontTab)
  1794. {
  1795. int length = w;
  1796. int depth = h;
  1797. if (orientation == TabbedButtonBar::TabsAtLeft
  1798. || orientation == TabbedButtonBar::TabsAtRight)
  1799. {
  1800. std::swap (length, depth);
  1801. }
  1802. Font font (depth * 0.6f);
  1803. font.setUnderline (button.hasKeyboardFocus (false));
  1804. GlyphArrangement textLayout;
  1805. textLayout.addFittedText (font, text.trim(),
  1806. 0.0f, 0.0f, (float) length, (float) depth,
  1807. Justification::centred,
  1808. jmax (1, depth / 12));
  1809. AffineTransform transform;
  1810. if (orientation == TabbedButtonBar::TabsAtLeft)
  1811. {
  1812. transform = transform.rotated (float_Pi * -0.5f)
  1813. .translated ((float) x, (float) (y + h));
  1814. }
  1815. else if (orientation == TabbedButtonBar::TabsAtRight)
  1816. {
  1817. transform = transform.rotated (float_Pi * 0.5f)
  1818. .translated ((float) (x + w), (float) y);
  1819. }
  1820. else
  1821. {
  1822. transform = transform.translated ((float) x, (float) y);
  1823. }
  1824. if (isFrontTab && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) || isColourSpecified (TabbedButtonBar::frontTextColourId)))
  1825. g.setColour (findColour (TabbedButtonBar::frontTextColourId));
  1826. else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) || isColourSpecified (TabbedButtonBar::tabTextColourId))
  1827. g.setColour (findColour (TabbedButtonBar::tabTextColourId));
  1828. else
  1829. g.setColour (preferredBackgroundColour.contrasting());
  1830. if (! (isMouseOver || isMouseDown))
  1831. g.setOpacity (0.8f);
  1832. if (! button.isEnabled())
  1833. g.setOpacity (0.3f);
  1834. textLayout.draw (g, transform);
  1835. }
  1836. int LookAndFeel::getTabButtonBestWidth (int /*tabIndex*/,
  1837. const String& text,
  1838. int tabDepth,
  1839. Button&)
  1840. {
  1841. Font f (tabDepth * 0.6f);
  1842. return f.getStringWidth (text.trim()) + getTabButtonOverlap (tabDepth) * 2;
  1843. }
  1844. void LookAndFeel::drawTabButton (Graphics& g,
  1845. int w, int h,
  1846. const Colour& preferredColour,
  1847. int tabIndex,
  1848. const String& text,
  1849. Button& button,
  1850. TabbedButtonBar::Orientation orientation,
  1851. const bool isMouseOver,
  1852. const bool isMouseDown,
  1853. const bool isFrontTab)
  1854. {
  1855. int length = w;
  1856. int depth = h;
  1857. if (orientation == TabbedButtonBar::TabsAtLeft
  1858. || orientation == TabbedButtonBar::TabsAtRight)
  1859. {
  1860. std::swap (length, depth);
  1861. }
  1862. Path tabShape;
  1863. createTabButtonShape (tabShape, w, h,
  1864. tabIndex, text, button, orientation,
  1865. isMouseOver, isMouseDown, isFrontTab);
  1866. fillTabButtonShape (g, tabShape, preferredColour,
  1867. tabIndex, text, button, orientation,
  1868. isMouseOver, isMouseDown, isFrontTab);
  1869. const int indent = getTabButtonOverlap (depth);
  1870. int x = 0, y = 0;
  1871. if (orientation == TabbedButtonBar::TabsAtLeft
  1872. || orientation == TabbedButtonBar::TabsAtRight)
  1873. {
  1874. y += indent;
  1875. h -= indent * 2;
  1876. }
  1877. else
  1878. {
  1879. x += indent;
  1880. w -= indent * 2;
  1881. }
  1882. drawTabButtonText (g, x, y, w, h, preferredColour,
  1883. tabIndex, text, button, orientation,
  1884. isMouseOver, isMouseDown, isFrontTab);
  1885. }
  1886. void LookAndFeel::drawTabAreaBehindFrontButton (Graphics& g,
  1887. int w, int h,
  1888. TabbedButtonBar& tabBar,
  1889. TabbedButtonBar::Orientation orientation)
  1890. {
  1891. const float shadowSize = 0.2f;
  1892. float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
  1893. Rectangle<int> shadowRect;
  1894. if (orientation == TabbedButtonBar::TabsAtLeft)
  1895. {
  1896. x1 = (float) w;
  1897. x2 = w * (1.0f - shadowSize);
  1898. shadowRect.setBounds ((int) x2, 0, w - (int) x2, h);
  1899. }
  1900. else if (orientation == TabbedButtonBar::TabsAtRight)
  1901. {
  1902. x2 = w * shadowSize;
  1903. shadowRect.setBounds (0, 0, (int) x2, h);
  1904. }
  1905. else if (orientation == TabbedButtonBar::TabsAtBottom)
  1906. {
  1907. y2 = h * shadowSize;
  1908. shadowRect.setBounds (0, 0, w, (int) y2);
  1909. }
  1910. else
  1911. {
  1912. y1 = (float) h;
  1913. y2 = h * (1.0f - shadowSize);
  1914. shadowRect.setBounds (0, (int) y2, w, h - (int) y2);
  1915. }
  1916. g.setGradientFill (ColourGradient (Colours::black.withAlpha (tabBar.isEnabled() ? 0.3f : 0.15f), x1, y1,
  1917. Colours::transparentBlack, x2, y2, false));
  1918. shadowRect.expand (2, 2);
  1919. g.fillRect (shadowRect);
  1920. g.setColour (Colour (0x80000000));
  1921. if (orientation == TabbedButtonBar::TabsAtLeft)
  1922. {
  1923. g.fillRect (w - 1, 0, 1, h);
  1924. }
  1925. else if (orientation == TabbedButtonBar::TabsAtRight)
  1926. {
  1927. g.fillRect (0, 0, 1, h);
  1928. }
  1929. else if (orientation == TabbedButtonBar::TabsAtBottom)
  1930. {
  1931. g.fillRect (0, 0, w, 1);
  1932. }
  1933. else
  1934. {
  1935. g.fillRect (0, h - 1, w, 1);
  1936. }
  1937. }
  1938. Button* LookAndFeel::createTabBarExtrasButton()
  1939. {
  1940. const float thickness = 7.0f;
  1941. const float indent = 22.0f;
  1942. Path p;
  1943. p.addEllipse (-10.0f, -10.0f, 120.0f, 120.0f);
  1944. DrawablePath ellipse;
  1945. ellipse.setPath (p);
  1946. ellipse.setFill (Colour (0x99ffffff));
  1947. p.clear();
  1948. p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f);
  1949. p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f);
  1950. p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness);
  1951. p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness);
  1952. p.setUsingNonZeroWinding (false);
  1953. DrawablePath dp;
  1954. dp.setPath (p);
  1955. dp.setFill (Colour (0x59000000));
  1956. DrawableComposite normalImage;
  1957. normalImage.addAndMakeVisible (ellipse.createCopy());
  1958. normalImage.addAndMakeVisible (dp.createCopy());
  1959. dp.setFill (Colour (0xcc000000));
  1960. DrawableComposite overImage;
  1961. overImage.addAndMakeVisible (ellipse.createCopy());
  1962. overImage.addAndMakeVisible (dp.createCopy());
  1963. DrawableButton* db = new DrawableButton ("tabs", DrawableButton::ImageFitted);
  1964. db->setImages (&normalImage, &overImage, nullptr);
  1965. return db;
  1966. }
  1967. //==============================================================================
  1968. void LookAndFeel::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header)
  1969. {
  1970. g.fillAll (Colours::white);
  1971. const int w = header.getWidth();
  1972. const int h = header.getHeight();
  1973. g.setGradientFill (ColourGradient (Colour (0xffe8ebf9), 0.0f, h * 0.5f,
  1974. Colour (0xfff6f8f9), 0.0f, h - 1.0f,
  1975. false));
  1976. g.fillRect (0, h / 2, w, h);
  1977. g.setColour (Colour (0x33000000));
  1978. g.fillRect (0, h - 1, w, 1);
  1979. for (int i = header.getNumColumns (true); --i >= 0;)
  1980. g.fillRect (header.getColumnPosition (i).getRight() - 1, 0, 1, h - 1);
  1981. }
  1982. void LookAndFeel::drawTableHeaderColumn (Graphics& g, const String& columnName, int /*columnId*/,
  1983. int width, int height,
  1984. bool isMouseOver, bool isMouseDown,
  1985. int columnFlags)
  1986. {
  1987. if (isMouseDown)
  1988. g.fillAll (Colour (0x8899aadd));
  1989. else if (isMouseOver)
  1990. g.fillAll (Colour (0x5599aadd));
  1991. int rightOfText = width - 4;
  1992. if ((columnFlags & (TableHeaderComponent::sortedForwards | TableHeaderComponent::sortedBackwards)) != 0)
  1993. {
  1994. const float top = height * ((columnFlags & TableHeaderComponent::sortedForwards) != 0 ? 0.35f : (1.0f - 0.35f));
  1995. const float bottom = height - top;
  1996. const float w = height * 0.5f;
  1997. const float x = rightOfText - (w * 1.25f);
  1998. rightOfText = (int) x;
  1999. Path sortArrow;
  2000. sortArrow.addTriangle (x, bottom, x + w * 0.5f, top, x + w, bottom);
  2001. g.setColour (Colour (0x99000000));
  2002. g.fillPath (sortArrow);
  2003. }
  2004. g.setColour (Colours::black);
  2005. g.setFont (height * 0.5f, Font::bold);
  2006. const int textX = 4;
  2007. g.drawFittedText (columnName, textX, 0, rightOfText - textX, height, Justification::centredLeft, 1);
  2008. }
  2009. //==============================================================================
  2010. void LookAndFeel::paintToolbarBackground (Graphics& g, int w, int h, Toolbar& toolbar)
  2011. {
  2012. const Colour background (toolbar.findColour (Toolbar::backgroundColourId));
  2013. g.setGradientFill (ColourGradient (background, 0.0f, 0.0f,
  2014. background.darker (0.1f),
  2015. toolbar.isVertical() ? w - 1.0f : 0.0f,
  2016. toolbar.isVertical() ? 0.0f : h - 1.0f,
  2017. false));
  2018. g.fillAll();
  2019. }
  2020. Button* LookAndFeel::createToolbarMissingItemsButton (Toolbar& /*toolbar*/)
  2021. {
  2022. return createTabBarExtrasButton();
  2023. }
  2024. void LookAndFeel::paintToolbarButtonBackground (Graphics& g, int /*width*/, int /*height*/,
  2025. bool isMouseOver, bool isMouseDown,
  2026. ToolbarItemComponent& component)
  2027. {
  2028. if (isMouseDown)
  2029. g.fillAll (component.findColour (Toolbar::buttonMouseDownBackgroundColourId, true));
  2030. else if (isMouseOver)
  2031. g.fillAll (component.findColour (Toolbar::buttonMouseOverBackgroundColourId, true));
  2032. }
  2033. void LookAndFeel::paintToolbarButtonLabel (Graphics& g, int x, int y, int width, int height,
  2034. const String& text, ToolbarItemComponent& component)
  2035. {
  2036. g.setColour (component.findColour (Toolbar::labelTextColourId, true)
  2037. .withAlpha (component.isEnabled() ? 1.0f : 0.25f));
  2038. const float fontHeight = jmin (14.0f, height * 0.85f);
  2039. g.setFont (fontHeight);
  2040. g.drawFittedText (text,
  2041. x, y, width, height,
  2042. Justification::centred,
  2043. jmax (1, height / (int) fontHeight));
  2044. }
  2045. //==============================================================================
  2046. void LookAndFeel::drawPropertyPanelSectionHeader (Graphics& g, const String& name,
  2047. bool isOpen, int width, int height)
  2048. {
  2049. const int buttonSize = (height * 3) / 4;
  2050. const int buttonIndent = (height - buttonSize) / 2;
  2051. drawTreeviewPlusMinusBox (g, buttonIndent, buttonIndent, buttonSize, buttonSize, ! isOpen, false);
  2052. const int textX = buttonIndent * 2 + buttonSize + 2;
  2053. g.setColour (Colours::black);
  2054. g.setFont (height * 0.7f, Font::bold);
  2055. g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true);
  2056. }
  2057. void LookAndFeel::drawPropertyComponentBackground (Graphics& g, int width, int height,
  2058. PropertyComponent&)
  2059. {
  2060. g.setColour (Colour (0x66ffffff));
  2061. g.fillRect (0, 0, width, height - 1);
  2062. }
  2063. void LookAndFeel::drawPropertyComponentLabel (Graphics& g, int, int height,
  2064. PropertyComponent& component)
  2065. {
  2066. g.setColour (Colours::black);
  2067. if (! component.isEnabled())
  2068. g.setOpacity (0.6f);
  2069. g.setFont (jmin (height, 24) * 0.65f);
  2070. const Rectangle<int> r (getPropertyComponentContentPosition (component));
  2071. g.drawFittedText (component.getName(),
  2072. 3, r.getY(), r.getX() - 5, r.getHeight(),
  2073. Justification::centredLeft, 2);
  2074. }
  2075. const Rectangle<int> LookAndFeel::getPropertyComponentContentPosition (PropertyComponent& component)
  2076. {
  2077. return Rectangle<int> (component.getWidth() / 3, 1,
  2078. component.getWidth() - component.getWidth() / 3 - 1, component.getHeight() - 3);
  2079. }
  2080. //==============================================================================
  2081. void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path)
  2082. {
  2083. Image content (Image::ARGB, box.getWidth(), box.getHeight(), true);
  2084. {
  2085. Graphics g2 (content);
  2086. g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f));
  2087. g2.fillPath (path);
  2088. g2.setColour (Colours::white.withAlpha (0.8f));
  2089. g2.strokePath (path, PathStrokeType (2.0f));
  2090. }
  2091. DropShadowEffect shadow;
  2092. shadow.setShadowProperties (5.0f, 0.4f, 0, 2);
  2093. shadow.applyEffect (content, g, 1.0f);
  2094. }
  2095. //==============================================================================
  2096. void LookAndFeel::createFileChooserHeaderText (const String& title,
  2097. const String& instructions,
  2098. GlyphArrangement& text,
  2099. int width)
  2100. {
  2101. text.clear();
  2102. text.addJustifiedText (Font (17.0f, Font::bold), title,
  2103. 8.0f, 22.0f, width - 16.0f,
  2104. Justification::centred);
  2105. text.addJustifiedText (Font (14.0f), instructions,
  2106. 8.0f, 24.0f + 16.0f, width - 16.0f,
  2107. Justification::centred);
  2108. }
  2109. void LookAndFeel::drawFileBrowserRow (Graphics& g, int width, int height,
  2110. const String& filename, Image* icon,
  2111. const String& fileSizeDescription,
  2112. const String& fileTimeDescription,
  2113. const bool isDirectory,
  2114. const bool isItemSelected,
  2115. const int /*itemIndex*/,
  2116. DirectoryContentsDisplayComponent&)
  2117. {
  2118. if (isItemSelected)
  2119. g.fillAll (findColour (DirectoryContentsDisplayComponent::highlightColourId));
  2120. const int x = 32;
  2121. g.setColour (Colours::black);
  2122. if (icon != nullptr && icon->isValid())
  2123. {
  2124. g.drawImageWithin (*icon, 2, 2, x - 4, height - 4,
  2125. RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize,
  2126. false);
  2127. }
  2128. else
  2129. {
  2130. const Drawable* d = isDirectory ? getDefaultFolderImage()
  2131. : getDefaultDocumentFileImage();
  2132. if (d != nullptr)
  2133. d->drawWithin (g, Rectangle<float> (2.0f, 2.0f, x - 4.0f, height - 4.0f),
  2134. RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, 1.0f);
  2135. }
  2136. g.setColour (findColour (DirectoryContentsDisplayComponent::textColourId));
  2137. g.setFont (height * 0.7f);
  2138. if (width > 450 && ! isDirectory)
  2139. {
  2140. const int sizeX = roundToInt (width * 0.7f);
  2141. const int dateX = roundToInt (width * 0.8f);
  2142. g.drawFittedText (filename,
  2143. x, 0, sizeX - x, height,
  2144. Justification::centredLeft, 1);
  2145. g.setFont (height * 0.5f);
  2146. g.setColour (Colours::darkgrey);
  2147. if (! isDirectory)
  2148. {
  2149. g.drawFittedText (fileSizeDescription,
  2150. sizeX, 0, dateX - sizeX - 8, height,
  2151. Justification::centredRight, 1);
  2152. g.drawFittedText (fileTimeDescription,
  2153. dateX, 0, width - 8 - dateX, height,
  2154. Justification::centredRight, 1);
  2155. }
  2156. }
  2157. else
  2158. {
  2159. g.drawFittedText (filename,
  2160. x, 0, width - x, height,
  2161. Justification::centredLeft, 1);
  2162. }
  2163. }
  2164. Button* LookAndFeel::createFileBrowserGoUpButton()
  2165. {
  2166. DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground);
  2167. Path arrowPath;
  2168. arrowPath.addArrow (Line<float> (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f);
  2169. DrawablePath arrowImage;
  2170. arrowImage.setFill (Colours::black.withAlpha (0.4f));
  2171. arrowImage.setPath (arrowPath);
  2172. goUpButton->setImages (&arrowImage);
  2173. return goUpButton;
  2174. }
  2175. void LookAndFeel::layoutFileBrowserComponent (FileBrowserComponent& browserComp,
  2176. DirectoryContentsDisplayComponent* fileListComponent,
  2177. FilePreviewComponent* previewComp,
  2178. ComboBox* currentPathBox,
  2179. TextEditor* filenameBox,
  2180. Button* goUpButton)
  2181. {
  2182. const int x = 8;
  2183. int w = browserComp.getWidth() - x - x;
  2184. if (previewComp != nullptr)
  2185. {
  2186. const int previewWidth = w / 3;
  2187. previewComp->setBounds (x + w - previewWidth, 0, previewWidth, browserComp.getHeight());
  2188. w -= previewWidth + 4;
  2189. }
  2190. int y = 4;
  2191. const int controlsHeight = 22;
  2192. const int bottomSectionHeight = controlsHeight + 8;
  2193. const int upButtonWidth = 50;
  2194. currentPathBox->setBounds (x, y, w - upButtonWidth - 6, controlsHeight);
  2195. goUpButton->setBounds (x + w - upButtonWidth, y, upButtonWidth, controlsHeight);
  2196. y += controlsHeight + 4;
  2197. Component* const listAsComp = dynamic_cast <Component*> (fileListComponent);
  2198. listAsComp->setBounds (x, y, w, browserComp.getHeight() - y - bottomSectionHeight);
  2199. y = listAsComp->getBottom() + 4;
  2200. filenameBox->setBounds (x + 50, y, w - 50, controlsHeight);
  2201. }
  2202. // Pulls a drawable out of compressed valuetree data..
  2203. Drawable* LookAndFeel::loadDrawableFromData (const void* data, size_t numBytes)
  2204. {
  2205. MemoryInputStream m (data, numBytes, false);
  2206. GZIPDecompressorInputStream gz (m);
  2207. ValueTree drawable (ValueTree::readFromStream (gz));
  2208. return Drawable::createFromValueTree (drawable.getChild (0), nullptr);
  2209. }
  2210. const Drawable* LookAndFeel::getDefaultFolderImage()
  2211. {
  2212. if (folderImage == nullptr)
  2213. {
  2214. static const unsigned char drawableData[] =
  2215. { 120,218,197,86,77,111,27,55,16,229,182,161,237,6,61,39,233,77,63,192,38,56,195,225,215,209,105,210,2,141,13,20,201,193,109,111,178,181,178,183,145,181,130,180,110,145,127,159,199,93,73,137,87,53,218,91,109,192,160,151,179,156,55,111,222,188,229,155,247,
  2216. 231,87,231,175,47,222,170,234,155,229,244,190,86,213,115,253,102,61,253,123,122,189,168,85,51,83,213,119,250,238,221,47,231,151,175,223,169,170,250,121,221,62,172,84,245,172,60,63,209,243,118,49,171,215,170,107,87,23,245,188,83,213,145,182,167,19,91,
  2217. 254,127,223,220,222,117,37,68,82,40,143,174,219,174,107,239,135,168,147,18,37,108,85,245,237,46,207,70,33,249,175,211,238,78,85,186,28,253,76,175,73,109,186,117,251,177,190,106,102,229,241,247,58,24,103,203,15,101,245,103,219,44,187,15,221,39,0,172,142,
  2218. 245,125,211,1,196,205,116,181,125,114,164,175,31,186,78,45,219,229,31,245,186,189,106,150,179,102,121,139,100,154,240,231,167,102,177,64,72,247,105,213,23,122,187,158,206,154,122,217,169,85,57,18,1,47,53,101,107,18,135,204,167,147,192,201,216,20,114,
  2219. 244,195,62,171,234,7,125,198,100,136,216,145,149,211,9,57,103,40,249,72,219,8,167,170,87,250,140,162,199,123,226,3,34,82,202,134,131,13,172,74,170,233,162,0,177,234,166,93,180,15,235,141,170,206,180,157,204,231,150,156,159,207,39,195,50,214,88,18,150,
  2220. 245,205,124,250,104,169,212,135,158,19,144,53,20,112,172,55,237,2,132,13,199,149,130,230,115,145,112,147,147,82,61,157,32,238,178,253,11,145,213,138,10,52,138,38,103,111,99,164,211,137,139,198,35,177,35,167,212,143,15,215,205,13,160,109,163,172,225,152,
  2221. 16,232,17,149,140,103,144,158,146,90,113,217,12,6,197,167,236,3,54,5,181,101,73,54,138,90,245,165,227,120,18,252,150,77,15,242,188,228,204,81,169,139,102,249,5,68,192,145,14,244,112,1,145,29,94,137,96,235,49,136,151,58,246,32,88,192,161,88,176,76,226,
  2222. 36,247,24,176,7,232,62,16,83,42,155,201,160,30,222,65,72,98,82,76,33,198,254,197,96,124,10,150,243,8,130,48,228,36,94,124,6,4,43,38,0,142,205,99,30,4,221,13,33,230,220,71,177,65,49,142,243,150,7,1,51,20,2,5,96,96,84,225,56,217,188,3,33,46,24,228,112,
  2223. 69,69,12,68,228,108,242,99,16,165,118,208,28,51,200,98,87,42,74,62,209,24,4,206,48,22,153,125,132,220,196,56,15,234,99,216,130,0,141,38,74,162,130,48,35,163,141,94,196,245,32,94,104,7,154,132,209,40,108,162,165,232,153,165,17,4,138,201,176,135,58,49,
  2224. 165,130,122,108,114,54,28,240,64,17,89,188,79,177,116,149,10,4,246,91,30,94,104,112,96,226,144,131,144,142,98,78,177,7,128,81,242,224,140,36,249,80,208,145,196,12,202,15,16,60,161,200,69,187,169,213,86,198,123,87,224,255,199,21,94,105,134,72,40,177,245,
  2225. 14,182,32,232,54,196,231,100,111,11,189,168,201,39,177,84,102,38,139,177,168,74,210,87,174,64,20,138,160,67,111,10,4,98,196,97,60,158,118,133,25,111,173,224,171,37,97,185,119,133,221,242,63,184,194,140,71,174,240,252,145,43,72,32,147,146,147,4,104,104,
  2226. 117,134,10,18,12,107,212,40,72,148,57,6,71,69,135,222,248,16,160,168,3,169,144,55,201,69,41,147,137,134,99,50,97,8,178,85,43,217,140,201,151,192,152,10,242,190,24,11,59,183,29,25,42,115,236,98,14,229,252,32,80,66,0,162,17,136,72,6,67,5,45,242,224,10,
  2227. 193,102,71,50,6,17,129,212,18,115,105,150,80,169,45,123,222,141,76,178,70,32,55,24,90,217,132,71,73,200,57,238,204,3,136,49,144,185,55,183,190,20,137,52,246,47,113,232,158,69,35,49,145,208,129,193,56,178,77,135,230,145,113,22,140,69,74,20,146,2,120,218,
  2228. 155,135,48,32,10,89,30,156,165,204,254,222,193,160,12,19,49,6,210,59,11,70,62,4,31,15,64,196,2,157,98,33,58,1,104,32,152,50,31,128,64,148,183,197,108,209,89,107,240,41,75,36,123,16,208,108,180,44,236,250,182,227,27,20,137,118,76,60,165,137,221,92,94,
  2229. 78,215,31,235,245,230,183,242,229,30,214,251,251,195,145,94,148,15,253,170,221,52,93,211,46,7,109,171,81,208,177,94,247,119,132,47,81,186,92,22,246,7,255,254,15,7,107,141,171,197,191,156,123,162,135,187,198,227,131,113,219,80,159,1,4,239,223,231,0,0 };
  2230. folderImage = loadDrawableFromData (drawableData, sizeof (drawableData));
  2231. }
  2232. return folderImage;
  2233. }
  2234. const Drawable* LookAndFeel::getDefaultDocumentFileImage()
  2235. {
  2236. if (documentImage == nullptr)
  2237. {
  2238. static const unsigned char drawableData[] =
  2239. { 120,218,213,88,77,115,219,54,16,37,147,208,246,228,214,75,155,246,164,123,29,12,176,216,197,199,49,105,218,94,156,153,78,114,72,219,155,108,75,137,26,89,212,200,116,59,233,175,239,3,105,201,164,68,50,158,166,233,76,196,11,69,60,173,128,197,123,139,183,
  2240. 124,241,234,217,155,103,207,207,126,204,242,7,171,233,213,44,203,31,23,47,54,211,191,166,231,203,89,182,184,204,242,147,226,195,165,219,252,125,150,229,249,207,155,242,102,157,229,143,210,227,199,197,101,121,113,115,53,91,85,89,85,174,207,102,243,42,
  2241. 203,143,10,125,58,209,233,251,171,197,219,119,85,250,173,97,151,30,157,151,85,85,94,53,168,147,132,50,226,179,252,225,246,143,174,179,44,63,254,101,90,189,203,242,34,5,127,84,172,77,118,93,109,202,247,179,55,139,203,244,248,97,161,179,63,202,197,170,
  2242. 122,93,125,192,196,242,227,226,106,81,205,54,217,197,116,125,251,228,168,56,191,169,170,108,85,174,126,159,109,202,55,139,213,229,98,245,182,249,97,254,240,167,197,114,137,5,86,31,214,245,111,175,203,37,254,230,162,92,150,55,155,180,148,249,237,39,203,
  2243. 94,215,127,58,10,213,245,39,203,234,249,102,249,87,47,203,63,129,204,49,227,252,73,225,149,145,104,131,245,254,116,34,202,82,164,16,153,179,236,108,177,234,7,49,41,237,130,144,167,17,144,15,42,104,239,93,12,35,32,99,68,9,187,24,125,7,244,77,23,36,164,
  2244. 40,56,226,61,12,107,229,130,215,100,105,24,227,89,17,246,211,105,55,140,49,218,43,207,100,245,72,28,195,70,17,230,201,118,8,243,164,139,233,95,88,23,52,152,162,54,104,48,217,237,105,15,111,91,107,253,131,160,118,34,239,69,128,54,232,135,101,121,61,203,
  2245. 110,169,181,147,2,253,159,82,48,180,229,247,167,74,193,41,141,188,35,93,241,116,18,148,113,214,120,207,113,47,19,109,16,51,182,153,193,5,59,2,10,90,69,114,218,135,48,2,50,198,43,171,189,152,81,144,88,108,85,136,78,246,64,54,42,163,35,69,30,3,121,82,38,
  2246. 98,81,98,70,64,70,139,34,111,163,167,49,144,13,202,138,179,58,220,23,52,180,186,54,104,48,79,109,208,96,198,219,19,31,220,187,118,10,6,65,237,100,222,139,5,109,80,191,30,236,151,162,135,147,142,30,68,105,182,58,6,22,84,43,229,124,148,116,97,145,55,231,
  2247. 139,11,76,228,16,37,14,48,205,145,77,134,34,176,55,152,182,200,57,99,93,204,144,145,253,65,97,229,132,72,104,63,62,71,21,140,54,186,41,226,59,84,19,63,130,15,222,235,224,185,59,104,27,226,68,101,153,241,227,177,248,29,20,136,26,8,252,178,183,241,219,
  2248. 131,137,160,209,107,109,92,79,124,16,211,184,104,93,77,130,110,124,2,65,172,67,201,60,157,88,163,2,91,99,92,216,198,55,78,69,75,190,150,119,84,98,200,71,150,109,124,36,204,227,52,8,33,229,223,68,167,173,167,131,248,137,212,226,141,19,233,160,154,248,
  2249. 144,142,195,140,137,185,59,104,15,247,119,40,126,23,69,81,200,242,110,254,123,20,49,94,112,110,245,199,111,241,167,87,36,252,101,138,132,149,22,22,38,65,134,29,182,139,24,230,192,31,144,184,133,130,72,44,131,210,142,111,147,216,30,76,123,30,113,206,242,
  2250. 150,196,157,65,129,130,76,180,194,61,34,225,160,5,228,233,160,118,34,137,26,202,115,212,29,108,72,134,243,223,90,114,226,199,226,119,80,6,245,152,197,122,217,146,184,53,24,140,210,30,21,59,80,79,124,182,202,71,207,218,112,159,72,80,53,140,109,68,2,191,
  2251. 227,217,210,78,36,94,137,88,231,82,157,8,176,61,0,122,191,19,137,3,255,13,39,183,228,20,193,151,144,119,166,79,36,40,253,156,138,72,11,181,19,137,14,46,176,217,27,180,135,251,219,31,255,235,61,148,165,96,72,122,118,23,229,81,52,135,24,250,163,183,216,
  2252. 211,43,17,217,151,136,253,116,137,28,53,188,127,92,188,221,76,47,23,169,59,90,167,144,141,239,197,86,104,141,189,60,157,80,84,142,140,4,31,154,241,122,105,132,41,107,13,201,39,86,120,24,82,114,206,198,6,96,27,227,172,36,232,168,201,36,219,24,113,62,163,
  2253. 154,101,233,143,166,203,102,26,141,206,174,179,252,89,161,39,243,249,197,121,186,38,233,246,146,211,53,1,123,56,194,231,122,143,103,179,217,60,204,167,19,147,110,41,93,173,219,123,72,89,248,35,173,16,220,50,179,111,60,181,24,88,103,156,235,7,78,248,14,
  2254. 4,119,78,162,93,60,112,35,109,16,124,126,12,17,71,67,24,1,165,142,1,181,215,248,56,6,66,235,193,137,167,61,22,30,5,3,27,101,71,64,169,25,112,216,2,63,22,169,110,43,18,200,140,129,208,160,88,44,220,208,125,65,67,171,107,131,6,243,212,6,13,102,188,61,241,
  2255. 225,189,107,165,96,16,212,78,230,189,88,208,6,245,235,214,237,235,150,62,167,110,155,106,170,53,133,192,117,193,20,84,78,74,174,98,39,92,156,8,112,21,46,80,106,12,209,207,225,228,16,113,59,225,126,87,60,133,25,209,34,36,2,99,242,52,197,48,30,75,244,247,
  2256. 212,238,246,182,173,221,185,78,215,127,167,221,162,163,221,250,152,217,146,196,222,145,100,223,235,105,108,28,250,149,212,74,224,86,2,213,118,110,119,204,224,144,208,38,214,131,200,14,214,223,120,189,230,53,1,193,70,133,154,131,56,223,16,229,48,188,14,
  2257. 201,205,213,121,71,233,68,89,15,124,103,37,53,26,11,118,176,127,169,88,166,158,219,178,117,173,83,108,75,95,55,68,186,193,53,246,146,206,127,6,63,53,78,58,228,204,155,224,113,74,91,232,221,195,240,105,215,34,29,138,64,128,183,8,130,233,71,173,56,54,101,
  2258. 99,75,186,111,65,58,28,229,145,82,19,152,12,99,180,81,130,131,75,234,229,220,247,53,231,154,79,205,185,185,155,199,249,172,38,85,253,204,76,68,95,92,204,207,255,221,75,178,227,14,187,224,224,97,202,172,173,219,12,167,130,133,9,54,135,245,92,176,29,134,
  2259. 165,110,139,141,18,16,223,29,188,183,65,207,144,106,144,151,143,128,224,176,168,110,140,32,62,56,110,219,195,54,235,20,68,209,216,34,232,21,6,41,234,157,39,211,201,107,160,230,66,225,56,153,9,101,21,37,237,150,204,14,115,208,22,221,54,216,230,33,116,
  2260. 14,65,14,44,19,8,236,73,71,246,182,110,125,224,75,132,195,214,247,163,36,51,252,84,76,124,37,212,100,88,62,183,179,76,67,217,218,242,244,229,116,243,126,182,185,254,21,105,126,208,220,239,94,229,30,21,203,244,202,117,93,94,47,170,69,185,106,246,60,219,
  2261. 3,29,23,155,250,109,237,29,170,72,175,109,119,129,127,235,9,92,20,85,185,254,72,220,147,162,121,235,219,13,44,144,225,63,241,244,165,51,0,0 };
  2262. documentImage = loadDrawableFromData (drawableData, sizeof (drawableData));
  2263. }
  2264. return documentImage;
  2265. }
  2266. //==============================================================================
  2267. void LookAndFeel::drawLevelMeter (Graphics& g, int width, int height, float level)
  2268. {
  2269. g.setColour (Colours::white.withAlpha (0.7f));
  2270. g.fillRoundedRectangle (0.0f, 0.0f, (float) width, (float) height, 3.0f);
  2271. g.setColour (Colours::black.withAlpha (0.2f));
  2272. g.drawRoundedRectangle (1.0f, 1.0f, width - 2.0f, height - 2.0f, 3.0f, 1.0f);
  2273. const int totalBlocks = 7;
  2274. const int numBlocks = roundToInt (totalBlocks * level);
  2275. const float w = (width - 6.0f) / (float) totalBlocks;
  2276. for (int i = 0; i < totalBlocks; ++i)
  2277. {
  2278. if (i >= numBlocks)
  2279. g.setColour (Colours::lightblue.withAlpha (0.6f));
  2280. else
  2281. g.setColour (i < totalBlocks - 1 ? Colours::blue.withAlpha (0.5f)
  2282. : Colours::red);
  2283. g.fillRoundedRectangle (3.0f + i * w + w * 0.1f, 3.0f, w * 0.8f, height - 6.0f, w * 0.4f);
  2284. }
  2285. }
  2286. //==============================================================================
  2287. void LookAndFeel::drawKeymapChangeButton (Graphics& g, int width, int height, Button& button, const String& keyDescription)
  2288. {
  2289. const Colour textColour (button.findColour (0x100ad01 /*KeyMappingEditorComponent::textColourId*/, true));
  2290. if (keyDescription.isNotEmpty())
  2291. {
  2292. if (button.isEnabled())
  2293. {
  2294. const float alpha = button.isDown() ? 0.3f : (button.isOver() ? 0.15f : 0.08f);
  2295. g.fillAll (textColour.withAlpha (alpha));
  2296. g.setOpacity (0.3f);
  2297. g.drawBevel (0, 0, width, height, 2);
  2298. }
  2299. g.setColour (textColour);
  2300. g.setFont (height * 0.6f);
  2301. g.drawFittedText (keyDescription,
  2302. 3, 0, width - 6, height,
  2303. Justification::centred, 1);
  2304. }
  2305. else
  2306. {
  2307. const float thickness = 7.0f;
  2308. const float indent = 22.0f;
  2309. Path p;
  2310. p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f);
  2311. p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f);
  2312. p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness);
  2313. p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness);
  2314. p.setUsingNonZeroWinding (false);
  2315. g.setColour (textColour.withAlpha (button.isDown() ? 0.7f : (button.isOver() ? 0.5f : 0.3f)));
  2316. g.fillPath (p, p.getTransformToScaleToFit (2.0f, 2.0f, width - 4.0f, height - 4.0f, true));
  2317. }
  2318. if (button.hasKeyboardFocus (false))
  2319. {
  2320. g.setColour (textColour.withAlpha (0.4f));
  2321. g.drawRect (0, 0, width, height);
  2322. }
  2323. }
  2324. //==============================================================================
  2325. void LookAndFeel::drawShinyButtonShape (Graphics& g,
  2326. float x, float y, float w, float h,
  2327. float maxCornerSize,
  2328. const Colour& baseColour,
  2329. const float strokeWidth,
  2330. const bool flatOnLeft,
  2331. const bool flatOnRight,
  2332. const bool flatOnTop,
  2333. const bool flatOnBottom) noexcept
  2334. {
  2335. if (w <= strokeWidth * 1.1f || h <= strokeWidth * 1.1f)
  2336. return;
  2337. const float cs = jmin (maxCornerSize, w * 0.5f, h * 0.5f);
  2338. Path outline;
  2339. LookAndFeelHelpers::createRoundedPath (outline, x, y, w, h, cs,
  2340. ! (flatOnLeft || flatOnTop),
  2341. ! (flatOnRight || flatOnTop),
  2342. ! (flatOnLeft || flatOnBottom),
  2343. ! (flatOnRight || flatOnBottom));
  2344. ColourGradient cg (baseColour, 0.0f, y,
  2345. baseColour.overlaidWith (Colour (0x070000ff)), 0.0f, y + h,
  2346. false);
  2347. cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff)));
  2348. cg.addColour (0.51, baseColour.overlaidWith (Colour (0x110000ff)));
  2349. g.setGradientFill (cg);
  2350. g.fillPath (outline);
  2351. g.setColour (Colour (0x80000000));
  2352. g.strokePath (outline, PathStrokeType (strokeWidth));
  2353. }
  2354. //==============================================================================
  2355. void LookAndFeel::drawGlassSphere (Graphics& g,
  2356. const float x, const float y,
  2357. const float diameter,
  2358. const Colour& colour,
  2359. const float outlineThickness) noexcept
  2360. {
  2361. if (diameter <= outlineThickness)
  2362. return;
  2363. Path p;
  2364. p.addEllipse (x, y, diameter, diameter);
  2365. {
  2366. ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y,
  2367. Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false);
  2368. cg.addColour (0.4, Colours::white.overlaidWith (colour));
  2369. g.setGradientFill (cg);
  2370. g.fillPath (p);
  2371. }
  2372. g.setGradientFill (ColourGradient (Colours::white, 0, y + diameter * 0.06f,
  2373. Colours::transparentWhite, 0, y + diameter * 0.3f, false));
  2374. g.fillEllipse (x + diameter * 0.2f, y + diameter * 0.05f, diameter * 0.6f, diameter * 0.4f);
  2375. ColourGradient cg (Colours::transparentBlack,
  2376. x + diameter * 0.5f, y + diameter * 0.5f,
  2377. Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()),
  2378. x, y + diameter * 0.5f, true);
  2379. cg.addColour (0.7, Colours::transparentBlack);
  2380. cg.addColour (0.8, Colours::black.withAlpha (0.1f * outlineThickness));
  2381. g.setGradientFill (cg);
  2382. g.fillPath (p);
  2383. g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha()));
  2384. g.drawEllipse (x, y, diameter, diameter, outlineThickness);
  2385. }
  2386. //==============================================================================
  2387. void LookAndFeel::drawGlassPointer (Graphics& g,
  2388. const float x, const float y,
  2389. const float diameter,
  2390. const Colour& colour, const float outlineThickness,
  2391. const int direction) noexcept
  2392. {
  2393. if (diameter <= outlineThickness)
  2394. return;
  2395. Path p;
  2396. p.startNewSubPath (x + diameter * 0.5f, y);
  2397. p.lineTo (x + diameter, y + diameter * 0.6f);
  2398. p.lineTo (x + diameter, y + diameter);
  2399. p.lineTo (x, y + diameter);
  2400. p.lineTo (x, y + diameter * 0.6f);
  2401. p.closeSubPath();
  2402. p.applyTransform (AffineTransform::rotation (direction * (float_Pi * 0.5f), x + diameter * 0.5f, y + diameter * 0.5f));
  2403. {
  2404. ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y,
  2405. Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false);
  2406. cg.addColour (0.4, Colours::white.overlaidWith (colour));
  2407. g.setGradientFill (cg);
  2408. g.fillPath (p);
  2409. }
  2410. ColourGradient cg (Colours::transparentBlack,
  2411. x + diameter * 0.5f, y + diameter * 0.5f,
  2412. Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()),
  2413. x - diameter * 0.2f, y + diameter * 0.5f, true);
  2414. cg.addColour (0.5, Colours::transparentBlack);
  2415. cg.addColour (0.7, Colours::black.withAlpha (0.07f * outlineThickness));
  2416. g.setGradientFill (cg);
  2417. g.fillPath (p);
  2418. g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha()));
  2419. g.strokePath (p, PathStrokeType (outlineThickness));
  2420. }
  2421. //==============================================================================
  2422. void LookAndFeel::drawGlassLozenge (Graphics& g,
  2423. const float x, const float y,
  2424. const float width, const float height,
  2425. const Colour& colour,
  2426. const float outlineThickness,
  2427. const float cornerSize,
  2428. const bool flatOnLeft,
  2429. const bool flatOnRight,
  2430. const bool flatOnTop,
  2431. const bool flatOnBottom) noexcept
  2432. {
  2433. if (width <= outlineThickness || height <= outlineThickness)
  2434. return;
  2435. const int intX = (int) x;
  2436. const int intY = (int) y;
  2437. const int intW = (int) width;
  2438. const int intH = (int) height;
  2439. const float cs = cornerSize < 0 ? jmin (width * 0.5f, height * 0.5f) : cornerSize;
  2440. const float edgeBlurRadius = height * 0.75f + (height - cs * 2.0f);
  2441. const int intEdge = (int) edgeBlurRadius;
  2442. Path outline;
  2443. LookAndFeelHelpers::createRoundedPath (outline, x, y, width, height, cs,
  2444. ! (flatOnLeft || flatOnTop),
  2445. ! (flatOnRight || flatOnTop),
  2446. ! (flatOnLeft || flatOnBottom),
  2447. ! (flatOnRight || flatOnBottom));
  2448. {
  2449. ColourGradient cg (colour.darker (0.2f), 0, y,
  2450. colour.darker (0.2f), 0, y + height, false);
  2451. cg.addColour (0.03, colour.withMultipliedAlpha (0.3f));
  2452. cg.addColour (0.4, colour);
  2453. cg.addColour (0.97, colour.withMultipliedAlpha (0.3f));
  2454. g.setGradientFill (cg);
  2455. g.fillPath (outline);
  2456. }
  2457. ColourGradient cg (Colours::transparentBlack, x + edgeBlurRadius, y + height * 0.5f,
  2458. colour.darker (0.2f), x, y + height * 0.5f, true);
  2459. cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.5f) / edgeBlurRadius), Colours::transparentBlack);
  2460. cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.25f) / edgeBlurRadius), colour.darker (0.2f).withMultipliedAlpha (0.3f));
  2461. if (! (flatOnLeft || flatOnTop || flatOnBottom))
  2462. {
  2463. g.saveState();
  2464. g.setGradientFill (cg);
  2465. g.reduceClipRegion (intX, intY, intEdge, intH);
  2466. g.fillPath (outline);
  2467. g.restoreState();
  2468. }
  2469. if (! (flatOnRight || flatOnTop || flatOnBottom))
  2470. {
  2471. cg.point1.setX (x + width - edgeBlurRadius);
  2472. cg.point2.setX (x + width);
  2473. g.saveState();
  2474. g.setGradientFill (cg);
  2475. g.reduceClipRegion (intX + intW - intEdge, intY, 2 + intEdge, intH);
  2476. g.fillPath (outline);
  2477. g.restoreState();
  2478. }
  2479. {
  2480. const float leftIndent = flatOnTop || flatOnLeft ? 0.0f : cs * 0.4f;
  2481. const float rightIndent = flatOnTop || flatOnRight ? 0.0f : cs * 0.4f;
  2482. Path highlight;
  2483. LookAndFeelHelpers::createRoundedPath (highlight,
  2484. x + leftIndent,
  2485. y + cs * 0.1f,
  2486. width - (leftIndent + rightIndent),
  2487. height * 0.4f, cs * 0.4f,
  2488. ! (flatOnLeft || flatOnTop),
  2489. ! (flatOnRight || flatOnTop),
  2490. ! (flatOnLeft || flatOnBottom),
  2491. ! (flatOnRight || flatOnBottom));
  2492. g.setGradientFill (ColourGradient (colour.brighter (10.0f), 0, y + height * 0.06f,
  2493. Colours::transparentWhite, 0, y + height * 0.4f, false));
  2494. g.fillPath (highlight);
  2495. }
  2496. g.setColour (colour.darker().withMultipliedAlpha (1.5f));
  2497. g.strokePath (outline, PathStrokeType (outlineThickness));
  2498. }
  2499. END_JUCE_NAMESPACE