Audio plugin host https://kx.studio/carla
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.

3192 lines
127KB

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