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.

3064 lines
126KB

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