The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3191 lines
127KB

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