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.

3010 lines
122KB

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