Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1481 lines
68KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. Colour LookAndFeel_V4::ColourScheme::getUIColour (UIColour index) const noexcept
  16. {
  17. if (isPositiveAndBelow (index, numColours))
  18. return palette[index];
  19. jassertfalse;
  20. return {};
  21. }
  22. void LookAndFeel_V4::ColourScheme::setUIColour (UIColour index, Colour newColour) noexcept
  23. {
  24. if (isPositiveAndBelow (index, numColours))
  25. palette[index] = newColour;
  26. else
  27. jassertfalse;
  28. }
  29. bool LookAndFeel_V4::ColourScheme::operator== (const ColourScheme& other) const noexcept
  30. {
  31. for (auto i = 0; i < numColours; ++i)
  32. if (palette[i] != other.palette[i])
  33. return false;
  34. return true;
  35. }
  36. bool LookAndFeel_V4::ColourScheme::operator!= (const ColourScheme& other) const noexcept
  37. {
  38. return ! operator== (other);
  39. }
  40. //==============================================================================
  41. LookAndFeel_V4::LookAndFeel_V4() : currentColourScheme (getDarkColourScheme())
  42. {
  43. initialiseColours();
  44. }
  45. LookAndFeel_V4::LookAndFeel_V4 (ColourScheme scheme) : currentColourScheme (scheme)
  46. {
  47. initialiseColours();
  48. }
  49. LookAndFeel_V4::~LookAndFeel_V4() {}
  50. //==============================================================================
  51. void LookAndFeel_V4::setColourScheme (ColourScheme newColourScheme)
  52. {
  53. currentColourScheme = newColourScheme;
  54. initialiseColours();
  55. }
  56. LookAndFeel_V4::ColourScheme LookAndFeel_V4::getDarkColourScheme()
  57. {
  58. return { 0xff323e44, 0xff263238, 0xff323e44,
  59. 0xff8e989b, 0xffffffff, 0xff42a2c8,
  60. 0xffffffff, 0xff181f22, 0xffffffff };
  61. }
  62. LookAndFeel_V4::ColourScheme LookAndFeel_V4::getMidnightColourScheme()
  63. {
  64. return { 0xff2f2f3a, 0xff191926, 0xffd0d0d0,
  65. 0xff66667c, 0xc8ffffff, 0xffd8d8d8,
  66. 0xffffffff, 0xff606073, 0xff000000 };
  67. }
  68. LookAndFeel_V4::ColourScheme LookAndFeel_V4::getGreyColourScheme()
  69. {
  70. return { 0xff505050, 0xff424242, 0xff606060,
  71. 0xffa6a6a6, 0xffffffff, 0xff21ba90,
  72. 0xff000000, 0xffffffff, 0xffffffff };
  73. }
  74. LookAndFeel_V4::ColourScheme LookAndFeel_V4::getLightColourScheme()
  75. {
  76. return { 0xffefefef, 0xffffffff, 0xffffffff,
  77. 0xffdddddd, 0xff000000, 0xffa9a9a9,
  78. 0xffffffff, 0xff42a2c8, 0xff000000 };
  79. }
  80. //==============================================================================
  81. class LookAndFeel_V4_DocumentWindowButton : public Button
  82. {
  83. public:
  84. LookAndFeel_V4_DocumentWindowButton (const String& name, Colour c, const Path& normal, const Path& toggled)
  85. : Button (name), colour (c), normalShape (normal), toggledShape (toggled)
  86. {
  87. }
  88. void paintButton (Graphics& g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override
  89. {
  90. auto background = Colours::grey;
  91. if (auto* rw = findParentComponentOfClass<ResizableWindow>())
  92. if (auto lf = dynamic_cast<LookAndFeel_V4*> (&rw->getLookAndFeel()))
  93. background = lf->getCurrentColourScheme().getUIColour (LookAndFeel_V4::ColourScheme::widgetBackground);
  94. g.fillAll (background);
  95. g.setColour ((! isEnabled() || shouldDrawButtonAsDown) ? colour.withAlpha (0.6f)
  96. : colour);
  97. if (shouldDrawButtonAsHighlighted)
  98. {
  99. g.fillAll();
  100. g.setColour (background);
  101. }
  102. auto& p = getToggleState() ? toggledShape : normalShape;
  103. auto reducedRect = Justification (Justification::centred)
  104. .appliedToRectangle (Rectangle<int> (getHeight(), getHeight()), getLocalBounds())
  105. .toFloat()
  106. .reduced (getHeight() * 0.3f);
  107. g.fillPath (p, p.getTransformToScaleToFit (reducedRect, true));
  108. }
  109. private:
  110. Colour colour;
  111. Path normalShape, toggledShape;
  112. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel_V4_DocumentWindowButton)
  113. };
  114. Button* LookAndFeel_V4::createDocumentWindowButton (int buttonType)
  115. {
  116. Path shape;
  117. auto crossThickness = 0.15f;
  118. if (buttonType == DocumentWindow::closeButton)
  119. {
  120. shape.addLineSegment ({ 0.0f, 0.0f, 1.0f, 1.0f }, crossThickness);
  121. shape.addLineSegment ({ 1.0f, 0.0f, 0.0f, 1.0f }, crossThickness);
  122. return new LookAndFeel_V4_DocumentWindowButton ("close", Colour (0xff9A131D), shape, shape);
  123. }
  124. if (buttonType == DocumentWindow::minimiseButton)
  125. {
  126. shape.addLineSegment ({ 0.0f, 0.5f, 1.0f, 0.5f }, crossThickness);
  127. return new LookAndFeel_V4_DocumentWindowButton ("minimise", Colour (0xffaa8811), shape, shape);
  128. }
  129. if (buttonType == DocumentWindow::maximiseButton)
  130. {
  131. shape.addLineSegment ({ 0.5f, 0.0f, 0.5f, 1.0f }, crossThickness);
  132. shape.addLineSegment ({ 0.0f, 0.5f, 1.0f, 0.5f }, crossThickness);
  133. Path fullscreenShape;
  134. fullscreenShape.startNewSubPath (45.0f, 100.0f);
  135. fullscreenShape.lineTo (0.0f, 100.0f);
  136. fullscreenShape.lineTo (0.0f, 0.0f);
  137. fullscreenShape.lineTo (100.0f, 0.0f);
  138. fullscreenShape.lineTo (100.0f, 45.0f);
  139. fullscreenShape.addRectangle (45.0f, 45.0f, 100.0f, 100.0f);
  140. PathStrokeType (30.0f).createStrokedPath (fullscreenShape, fullscreenShape);
  141. return new LookAndFeel_V4_DocumentWindowButton ("maximise", Colour (0xff0A830A), shape, fullscreenShape);
  142. }
  143. jassertfalse;
  144. return nullptr;
  145. }
  146. void LookAndFeel_V4::positionDocumentWindowButtons (DocumentWindow&,
  147. int titleBarX, int titleBarY,
  148. int titleBarW, int titleBarH,
  149. Button* minimiseButton,
  150. Button* maximiseButton,
  151. Button* closeButton,
  152. bool positionTitleBarButtonsOnLeft)
  153. {
  154. titleBarH = jmin (titleBarH, titleBarH - titleBarY);
  155. auto buttonW = static_cast<int> (titleBarH * 1.2);
  156. auto x = positionTitleBarButtonsOnLeft ? titleBarX
  157. : titleBarX + titleBarW - buttonW;
  158. if (closeButton != nullptr)
  159. {
  160. closeButton->setBounds (x, titleBarY, buttonW, titleBarH);
  161. x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW;
  162. }
  163. if (positionTitleBarButtonsOnLeft)
  164. std::swap (minimiseButton, maximiseButton);
  165. if (maximiseButton != nullptr)
  166. {
  167. maximiseButton->setBounds (x, titleBarY, buttonW, titleBarH);
  168. x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW;
  169. }
  170. if (minimiseButton != nullptr)
  171. minimiseButton->setBounds (x, titleBarY, buttonW, titleBarH);
  172. }
  173. void LookAndFeel_V4::drawDocumentWindowTitleBar (DocumentWindow& window, Graphics& g,
  174. int w, int h, int titleSpaceX, int titleSpaceW,
  175. const Image* icon, bool drawTitleTextOnLeft)
  176. {
  177. if (w * h == 0)
  178. return;
  179. auto isActive = window.isActiveWindow();
  180. g.setColour (getCurrentColourScheme().getUIColour (ColourScheme::widgetBackground));
  181. g.fillAll();
  182. Font font (h * 0.65f, Font::plain);
  183. g.setFont (font);
  184. auto textW = font.getStringWidth (window.getName());
  185. auto iconW = 0;
  186. auto iconH = 0;
  187. if (icon != nullptr)
  188. {
  189. iconH = static_cast<int> (font.getHeight());
  190. iconW = icon->getWidth() * iconH / icon->getHeight() + 4;
  191. }
  192. textW = jmin (titleSpaceW, textW + iconW);
  193. auto textX = drawTitleTextOnLeft ? titleSpaceX
  194. : jmax (titleSpaceX, (w - textW) / 2);
  195. if (textX + textW > titleSpaceX + titleSpaceW)
  196. textX = titleSpaceX + titleSpaceW - textW;
  197. if (icon != nullptr)
  198. {
  199. g.setOpacity (isActive ? 1.0f : 0.6f);
  200. g.drawImageWithin (*icon, textX, (h - iconH) / 2, iconW, iconH,
  201. RectanglePlacement::centred, false);
  202. textX += iconW;
  203. textW -= iconW;
  204. }
  205. if (window.isColourSpecified (DocumentWindow::textColourId) || isColourSpecified (DocumentWindow::textColourId))
  206. g.setColour (window.findColour (DocumentWindow::textColourId));
  207. else
  208. g.setColour (getCurrentColourScheme().getUIColour (ColourScheme::defaultText));
  209. g.drawText (window.getName(), textX, 0, textW, h, Justification::centredLeft, true);
  210. }
  211. //==============================================================================
  212. Font LookAndFeel_V4::getTextButtonFont (TextButton&, int buttonHeight)
  213. {
  214. return { jmin (16.0f, buttonHeight * 0.6f) };
  215. }
  216. void LookAndFeel_V4::drawButtonBackground (Graphics& g,
  217. Button& button,
  218. const Colour& backgroundColour,
  219. bool shouldDrawButtonAsHighlighted,
  220. bool shouldDrawButtonAsDown)
  221. {
  222. auto cornerSize = 6.0f;
  223. auto bounds = button.getLocalBounds().toFloat().reduced (0.5f, 0.5f);
  224. auto baseColour = backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
  225. .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f);
  226. if (shouldDrawButtonAsDown || shouldDrawButtonAsHighlighted)
  227. baseColour = baseColour.contrasting (shouldDrawButtonAsDown ? 0.2f : 0.05f);
  228. g.setColour (baseColour);
  229. auto flatOnLeft = button.isConnectedOnLeft();
  230. auto flatOnRight = button.isConnectedOnRight();
  231. auto flatOnTop = button.isConnectedOnTop();
  232. auto flatOnBottom = button.isConnectedOnBottom();
  233. if (flatOnLeft || flatOnRight || flatOnTop || flatOnBottom)
  234. {
  235. Path path;
  236. path.addRoundedRectangle (bounds.getX(), bounds.getY(),
  237. bounds.getWidth(), bounds.getHeight(),
  238. cornerSize, cornerSize,
  239. ! (flatOnLeft || flatOnTop),
  240. ! (flatOnRight || flatOnTop),
  241. ! (flatOnLeft || flatOnBottom),
  242. ! (flatOnRight || flatOnBottom));
  243. g.fillPath (path);
  244. g.setColour (button.findColour (ComboBox::outlineColourId));
  245. g.strokePath (path, PathStrokeType (1.0f));
  246. }
  247. else
  248. {
  249. g.fillRoundedRectangle (bounds, cornerSize);
  250. g.setColour (button.findColour (ComboBox::outlineColourId));
  251. g.drawRoundedRectangle (bounds, cornerSize, 1.0f);
  252. }
  253. }
  254. void LookAndFeel_V4::drawToggleButton (Graphics& g, ToggleButton& button,
  255. bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown)
  256. {
  257. auto fontSize = jmin (15.0f, button.getHeight() * 0.75f);
  258. auto tickWidth = fontSize * 1.1f;
  259. drawTickBox (g, button, 4.0f, (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) + 10)
  271. .withTrimmedRight (2),
  272. Justification::centredLeft, 10);
  273. }
  274. void LookAndFeel_V4::drawTickBox (Graphics& g, Component& component,
  275. float x, float y, float w, float h,
  276. const bool ticked,
  277. const bool isEnabled,
  278. const bool shouldDrawButtonAsHighlighted,
  279. const bool shouldDrawButtonAsDown)
  280. {
  281. ignoreUnused (isEnabled, shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
  282. Rectangle<float> tickBounds (x, y, w, h);
  283. g.setColour (component.findColour (ToggleButton::tickDisabledColourId));
  284. g.drawRoundedRectangle (tickBounds, 4.0f, 1.0f);
  285. if (ticked)
  286. {
  287. g.setColour (component.findColour (ToggleButton::tickColourId));
  288. auto tick = getTickShape (0.75f);
  289. g.fillPath (tick, tick.getTransformToScaleToFit (tickBounds.reduced (4, 5).toFloat(), false));
  290. }
  291. }
  292. void LookAndFeel_V4::changeToggleButtonWidthToFitText (ToggleButton& button)
  293. {
  294. auto fontSize = jmin (15.0f, button.getHeight() * 0.75f);
  295. auto tickWidth = fontSize * 1.1f;
  296. Font font (fontSize);
  297. button.setSize (font.getStringWidth (button.getButtonText()) + roundToInt (tickWidth) + 14, button.getHeight());
  298. }
  299. //==============================================================================
  300. AlertWindow* LookAndFeel_V4::createAlertWindow (const String& title, const String& message,
  301. const String& button1, const String& button2, const String& button3,
  302. AlertWindow::AlertIconType iconType,
  303. int numButtons, Component* associatedComponent)
  304. {
  305. auto boundsOffset = 50;
  306. auto* aw = LookAndFeel_V2::createAlertWindow (title, message, button1, button2, button3,
  307. iconType, numButtons, associatedComponent);
  308. auto bounds = aw->getBounds();
  309. bounds = bounds.withSizeKeepingCentre (bounds.getWidth() + boundsOffset, bounds.getHeight() + boundsOffset);
  310. aw->setBounds (bounds);
  311. for (auto* child : aw->getChildren())
  312. if (auto* button = dynamic_cast<TextButton*> (child))
  313. button->setBounds (button->getBounds() + Point<int> (25, 40));
  314. return aw;
  315. }
  316. void LookAndFeel_V4::drawAlertBox (Graphics& g, AlertWindow& alert,
  317. const Rectangle<int>& textArea, TextLayout& textLayout)
  318. {
  319. auto cornerSize = 4.0f;
  320. g.setColour (alert.findColour (AlertWindow::outlineColourId));
  321. g.drawRoundedRectangle (alert.getLocalBounds().toFloat(), cornerSize, 2.0f);
  322. auto bounds = alert.getLocalBounds().reduced (1);
  323. g.reduceClipRegion (bounds);
  324. g.setColour (alert.findColour (AlertWindow::backgroundColourId));
  325. g.fillRoundedRectangle (bounds.toFloat(), cornerSize);
  326. auto iconSpaceUsed = 0;
  327. auto iconWidth = 80;
  328. auto iconSize = jmin (iconWidth + 50, bounds.getHeight() + 20);
  329. if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2)
  330. iconSize = jmin (iconSize, textArea.getHeight() + 50);
  331. Rectangle<int> iconRect (iconSize / -10, iconSize / -10,
  332. iconSize, iconSize);
  333. if (alert.getAlertType() != AlertWindow::NoIcon)
  334. {
  335. Path icon;
  336. char character;
  337. uint32 colour;
  338. if (alert.getAlertType() == AlertWindow::WarningIcon)
  339. {
  340. character = '!';
  341. icon.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, (float) iconRect.getY(),
  342. static_cast<float> (iconRect.getRight()), static_cast<float> (iconRect.getBottom()),
  343. static_cast<float> (iconRect.getX()), static_cast<float> (iconRect.getBottom()));
  344. icon = icon.createPathWithRoundedCorners (5.0f);
  345. colour = 0x66ff2a00;
  346. }
  347. else
  348. {
  349. colour = Colour (0xff00b0b9).withAlpha (0.4f).getARGB();
  350. character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?';
  351. icon.addEllipse (iconRect.toFloat());
  352. }
  353. GlyphArrangement ga;
  354. ga.addFittedText ({ iconRect.getHeight() * 0.9f, Font::bold },
  355. String::charToString ((juce_wchar) (uint8) character),
  356. static_cast<float> (iconRect.getX()), static_cast<float> (iconRect.getY()),
  357. static_cast<float> (iconRect.getWidth()), static_cast<float> (iconRect.getHeight()),
  358. Justification::centred, false);
  359. ga.createPath (icon);
  360. icon.setUsingNonZeroWinding (false);
  361. g.setColour (Colour (colour));
  362. g.fillPath (icon);
  363. iconSpaceUsed = iconWidth;
  364. }
  365. g.setColour (alert.findColour (AlertWindow::textColourId));
  366. Rectangle<int> alertBounds (bounds.getX() + iconSpaceUsed, 30,
  367. bounds.getWidth(), bounds.getHeight() - getAlertWindowButtonHeight() - 20);
  368. textLayout.draw (g, alertBounds.toFloat());
  369. }
  370. int LookAndFeel_V4::getAlertWindowButtonHeight() { return 40; }
  371. Font LookAndFeel_V4::getAlertWindowTitleFont() { return { 18.0f, Font::bold }; }
  372. Font LookAndFeel_V4::getAlertWindowMessageFont() { return { 16.0f }; }
  373. Font LookAndFeel_V4::getAlertWindowFont() { return { 14.0f }; }
  374. //==============================================================================
  375. void LookAndFeel_V4::drawProgressBar (Graphics& g, ProgressBar& progressBar,
  376. int width, int height, double progress, const String& textToShow)
  377. {
  378. if (width == height)
  379. drawCircularProgressBar (g, progressBar, textToShow);
  380. else
  381. drawLinearProgressBar (g, progressBar, width, height, progress, textToShow);
  382. }
  383. void LookAndFeel_V4::drawLinearProgressBar (Graphics& g, ProgressBar& progressBar,
  384. int width, int height,
  385. double progress, const String& textToShow)
  386. {
  387. auto background = progressBar.findColour (ProgressBar::backgroundColourId);
  388. auto foreground = progressBar.findColour (ProgressBar::foregroundColourId);
  389. auto barBounds = progressBar.getLocalBounds().toFloat();
  390. g.setColour (background);
  391. g.fillRoundedRectangle (barBounds, progressBar.getHeight() * 0.5f);
  392. if (progress >= 0.0f && progress <= 1.0f)
  393. {
  394. Path p;
  395. p.addRoundedRectangle (barBounds, progressBar.getHeight() * 0.5f);
  396. g.reduceClipRegion (p);
  397. barBounds.setWidth (barBounds.getWidth() * (float) progress);
  398. g.setColour (foreground);
  399. g.fillRoundedRectangle (barBounds, progressBar.getHeight() * 0.5f);
  400. }
  401. else
  402. {
  403. // spinning bar..
  404. g.setColour (background);
  405. auto stripeWidth = height * 2;
  406. auto position = static_cast<int> (Time::getMillisecondCounter() / 15) % stripeWidth;
  407. Path p;
  408. for (auto x = static_cast<float> (-position); x < width + stripeWidth; x += stripeWidth)
  409. p.addQuadrilateral (x, 0.0f,
  410. x + stripeWidth * 0.5f, 0.0f,
  411. x, static_cast<float> (height),
  412. x - stripeWidth * 0.5f, static_cast<float> (height));
  413. Image im (Image::ARGB, width, height, true);
  414. {
  415. Graphics g2 (im);
  416. g2.setColour (foreground);
  417. g2.fillRoundedRectangle (barBounds, progressBar.getHeight() * 0.5f);
  418. }
  419. g.setTiledImageFill (im, 0, 0, 0.85f);
  420. g.fillPath (p);
  421. }
  422. if (textToShow.isNotEmpty())
  423. {
  424. g.setColour (Colour::contrasting (background, foreground));
  425. g.setFont (height * 0.6f);
  426. g.drawText (textToShow, 0, 0, width, height, Justification::centred, false);
  427. }
  428. }
  429. void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progressBar, const String& progressText)
  430. {
  431. auto background = progressBar.findColour (ProgressBar::backgroundColourId);
  432. auto foreground = progressBar.findColour (ProgressBar::foregroundColourId);
  433. auto barBounds = progressBar.getLocalBounds().reduced (2, 2).toFloat();
  434. auto rotationInDegrees = static_cast<float> ((Time::getMillisecondCounter() / 10) % 360);
  435. auto normalisedRotation = rotationInDegrees / 360.0f;
  436. auto rotationOffset = 22.5f;
  437. auto maxRotation = 315.0f;
  438. auto startInDegrees = rotationInDegrees;
  439. auto endInDegrees = startInDegrees + rotationOffset;
  440. if (normalisedRotation >= 0.25f && normalisedRotation < 0.5f)
  441. {
  442. auto rescaledRotation = (normalisedRotation * 4.0f) - 1.0f;
  443. endInDegrees = startInDegrees + rotationOffset + (maxRotation * rescaledRotation);
  444. }
  445. else if (normalisedRotation >= 0.5f && normalisedRotation <= 1.0f)
  446. {
  447. endInDegrees = startInDegrees + rotationOffset + maxRotation;
  448. auto rescaledRotation = 1.0f - ((normalisedRotation * 2.0f) - 1.0f);
  449. startInDegrees = endInDegrees - rotationOffset - (maxRotation * rescaledRotation);
  450. }
  451. g.setColour (background);
  452. Path arcPath2;
  453. arcPath2.addCentredArc (barBounds.getCentreX(),
  454. barBounds.getCentreY(),
  455. barBounds.getWidth() * 0.5f,
  456. barBounds.getHeight() * 0.5f, 0.0f,
  457. 0.0f,
  458. MathConstants<float>::twoPi,
  459. true);
  460. g.strokePath (arcPath2, PathStrokeType (4.0f));
  461. g.setColour (foreground);
  462. Path arcPath;
  463. arcPath.addCentredArc (barBounds.getCentreX(),
  464. barBounds.getCentreY(),
  465. barBounds.getWidth() * 0.5f,
  466. barBounds.getHeight() * 0.5f,
  467. 0.0f,
  468. degreesToRadians (startInDegrees),
  469. degreesToRadians (endInDegrees),
  470. true);
  471. arcPath.applyTransform (AffineTransform::rotation (normalisedRotation * MathConstants<float>::pi * 2.25f, barBounds.getCentreX(), barBounds.getCentreY()));
  472. g.strokePath (arcPath, PathStrokeType (4.0f));
  473. if (progressText.isNotEmpty())
  474. {
  475. g.setColour (progressBar.findColour (TextButton::textColourOffId));
  476. g.setFont ({ 12.0f, Font::italic });
  477. g.drawText (progressText, barBounds, Justification::centred, false);
  478. }
  479. }
  480. //==============================================================================
  481. int LookAndFeel_V4::getDefaultScrollbarWidth()
  482. {
  483. return 8;
  484. }
  485. void LookAndFeel_V4::drawScrollbar (Graphics& g, ScrollBar& scrollbar, int x, int y, int width, int height,
  486. bool isScrollbarVertical, int thumbStartPosition, int thumbSize, bool isMouseOver, bool isMouseDown)
  487. {
  488. ignoreUnused (isMouseDown);
  489. Rectangle<int> thumbBounds;
  490. if (isScrollbarVertical)
  491. thumbBounds = { x, thumbStartPosition, width, thumbSize };
  492. else
  493. thumbBounds = { thumbStartPosition, y, thumbSize, height };
  494. auto c = scrollbar.findColour (ScrollBar::ColourIds::thumbColourId);
  495. g.setColour (isMouseOver ? c.brighter (0.25f) : c);
  496. g.fillRoundedRectangle (thumbBounds.reduced (1).toFloat(), 4.0f);
  497. }
  498. //==============================================================================
  499. Path LookAndFeel_V4::getTickShape (float height)
  500. {
  501. static const unsigned char pathData[] = { 110,109,32,210,202,64,126,183,148,64,108,39,244,247,64,245,76,124,64,108,178,131,27,65,246,76,252,64,108,175,242,4,65,246,76,252,
  502. 64,108,236,5,68,65,0,0,160,180,108,240,150,90,65,21,136,52,63,108,48,59,16,65,0,0,32,65,108,32,210,202,64,126,183,148,64, 99,101,0,0 };
  503. Path path;
  504. path.loadPathFromData (pathData, sizeof (pathData));
  505. path.scaleToFit (0, 0, height * 2.0f, height, true);
  506. return path;
  507. }
  508. Path LookAndFeel_V4::getCrossShape (float height)
  509. {
  510. static const unsigned char pathData[] = { 110,109,51,51,255,66,0,0,0,0,108,205,204,13,67,51,51,99,65,108,0,0,170,66,205,204,141,66,108,51,179,13,67,52,51,255,66,108,0,0,255,
  511. 66,205,204,13,67,108,205,204,141,66,0,0,170,66,108,52,51,99,65,51,179,13,67,108,0,0,0,0,51,51,255,66,108,205,204,98,66, 204,204,141,66,108,0,0,0,0,51,51,99,65,108,51,51,
  512. 99,65,0,0,0,0,108,205,204,141,66,205,204,98,66,108,51,51,255,66,0,0,0,0,99,101,0,0 };
  513. Path path;
  514. path.loadPathFromData (pathData, sizeof (pathData));
  515. path.scaleToFit (0, 0, height * 2.0f, height, true);
  516. return path;
  517. }
  518. //==============================================================================
  519. void LookAndFeel_V4::fillTextEditorBackground (Graphics& g, int width, int height, TextEditor& textEditor)
  520. {
  521. if (dynamic_cast<AlertWindow*> (textEditor.getParentComponent()) != nullptr)
  522. {
  523. g.setColour (textEditor.findColour (TextEditor::backgroundColourId));
  524. g.fillRect (0, 0, width, height);
  525. g.setColour (textEditor.findColour (TextEditor::outlineColourId));
  526. g.drawHorizontalLine (height - 1, 0.0f, static_cast<float> (width));
  527. }
  528. else
  529. {
  530. LookAndFeel_V2::fillTextEditorBackground (g, width, height, textEditor);
  531. }
  532. }
  533. void LookAndFeel_V4::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor)
  534. {
  535. if (dynamic_cast<AlertWindow*> (textEditor.getParentComponent()) == nullptr)
  536. {
  537. if (textEditor.isEnabled())
  538. {
  539. if (textEditor.hasKeyboardFocus (true) && ! textEditor.isReadOnly())
  540. {
  541. g.setColour (textEditor.findColour (TextEditor::focusedOutlineColourId));
  542. g.drawRect (0, 0, width, height, 2);
  543. }
  544. else
  545. {
  546. g.setColour (textEditor.findColour (TextEditor::outlineColourId));
  547. g.drawRect (0, 0, width, height);
  548. }
  549. }
  550. }
  551. }
  552. //==============================================================================
  553. Button* LookAndFeel_V4::createFileBrowserGoUpButton()
  554. {
  555. auto* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground);
  556. Path arrowPath;
  557. arrowPath.addArrow ({ 50.0f, 100.0f, 50.0f, 0.0f }, 40.0f, 100.0f, 50.0f);
  558. DrawablePath arrowImage;
  559. arrowImage.setFill (goUpButton->findColour (TextButton::textColourOffId));
  560. arrowImage.setPath (arrowPath);
  561. goUpButton->setImages (&arrowImage);
  562. return goUpButton;
  563. }
  564. void LookAndFeel_V4::layoutFileBrowserComponent (FileBrowserComponent& browserComp,
  565. DirectoryContentsDisplayComponent* fileListComponent,
  566. FilePreviewComponent* previewComp,
  567. ComboBox* currentPathBox,
  568. TextEditor* filenameBox,
  569. Button* goUpButton)
  570. {
  571. auto sectionHeight = 22;
  572. auto buttonWidth = 50;
  573. auto b = browserComp.getLocalBounds().reduced (20, 5);
  574. auto topSlice = b.removeFromTop (sectionHeight);
  575. auto bottomSlice = b.removeFromBottom (sectionHeight);
  576. currentPathBox->setBounds (topSlice.removeFromLeft (topSlice.getWidth() - buttonWidth));
  577. topSlice.removeFromLeft (6);
  578. goUpButton->setBounds (topSlice);
  579. bottomSlice.removeFromLeft (20);
  580. filenameBox->setBounds (bottomSlice);
  581. if (previewComp != nullptr)
  582. previewComp->setBounds (b.removeFromRight (b.getWidth() / 3));
  583. if (auto* listAsComp = dynamic_cast<Component*> (fileListComponent))
  584. listAsComp->setBounds (b.reduced (0, 10));
  585. }
  586. void LookAndFeel_V4::drawFileBrowserRow (Graphics& g, int width, int height,
  587. const File& file, const String& filename, Image* icon,
  588. const String& fileSizeDescription,
  589. const String& fileTimeDescription,
  590. bool isDirectory, bool isItemSelected,
  591. int itemIndex, DirectoryContentsDisplayComponent& dcc)
  592. {
  593. LookAndFeel_V2::drawFileBrowserRow (g, width, height, file, filename, icon,
  594. fileSizeDescription, fileTimeDescription,
  595. isDirectory, isItemSelected, itemIndex, dcc);
  596. }
  597. //==============================================================================
  598. void LookAndFeel_V4::drawPopupMenuItem (Graphics& g, const Rectangle<int>& area,
  599. const bool isSeparator, const bool isActive,
  600. const bool isHighlighted, const bool isTicked,
  601. const bool hasSubMenu, const String& text,
  602. const String& shortcutKeyText,
  603. const Drawable* icon, const Colour* const textColourToUse)
  604. {
  605. if (isSeparator)
  606. {
  607. auto r = area.reduced (5, 0);
  608. r.removeFromTop (roundToInt ((r.getHeight() * 0.5f) - 0.5f));
  609. g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.3f));
  610. g.fillRect (r.removeFromTop (1));
  611. }
  612. else
  613. {
  614. auto textColour = (textColourToUse == nullptr ? findColour (PopupMenu::textColourId)
  615. : *textColourToUse);
  616. auto r = area.reduced (1);
  617. if (isHighlighted && isActive)
  618. {
  619. g.setColour (findColour (PopupMenu::highlightedBackgroundColourId));
  620. g.fillRect (r);
  621. g.setColour (findColour (PopupMenu::highlightedTextColourId));
  622. }
  623. else
  624. {
  625. g.setColour (textColour.withMultipliedAlpha (isActive ? 1.0f : 0.5f));
  626. }
  627. r.reduce (jmin (5, area.getWidth() / 20), 0);
  628. auto font = getPopupMenuFont();
  629. auto maxFontHeight = r.getHeight() / 1.3f;
  630. if (font.getHeight() > maxFontHeight)
  631. font.setHeight (maxFontHeight);
  632. g.setFont (font);
  633. auto iconArea = r.removeFromLeft (roundToInt (maxFontHeight)).toFloat();
  634. if (icon != nullptr)
  635. {
  636. icon->drawWithin (g, iconArea, RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, 1.0f);
  637. r.removeFromLeft (roundToInt (maxFontHeight * 0.5f));
  638. }
  639. else if (isTicked)
  640. {
  641. auto tick = getTickShape (1.0f);
  642. g.fillPath (tick, tick.getTransformToScaleToFit (iconArea.reduced (iconArea.getWidth() / 5, 0).toFloat(), true));
  643. }
  644. if (hasSubMenu)
  645. {
  646. auto arrowH = 0.6f * getPopupMenuFont().getAscent();
  647. auto x = static_cast<float> (r.removeFromRight ((int) arrowH).getX());
  648. auto halfH = static_cast<float> (r.getCentreY());
  649. Path path;
  650. path.startNewSubPath (x, halfH - arrowH * 0.5f);
  651. path.lineTo (x + arrowH * 0.6f, halfH);
  652. path.lineTo (x, halfH + arrowH * 0.5f);
  653. g.strokePath (path, PathStrokeType (2.0f));
  654. }
  655. r.removeFromRight (3);
  656. g.drawFittedText (text, r, Justification::centredLeft, 1);
  657. if (shortcutKeyText.isNotEmpty())
  658. {
  659. auto f2 = font;
  660. f2.setHeight (f2.getHeight() * 0.75f);
  661. f2.setHorizontalScale (0.95f);
  662. g.setFont (f2);
  663. g.drawText (shortcutKeyText, r, Justification::centredRight, true);
  664. }
  665. }
  666. }
  667. void LookAndFeel_V4::getIdealPopupMenuItemSize (const String& text, const bool isSeparator,
  668. int standardMenuItemHeight, int& idealWidth, int& idealHeight)
  669. {
  670. if (isSeparator)
  671. {
  672. idealWidth = 50;
  673. idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight / 10 : 10;
  674. }
  675. else
  676. {
  677. auto font = getPopupMenuFont();
  678. if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f)
  679. font.setHeight (standardMenuItemHeight / 1.3f);
  680. idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundToInt (font.getHeight() * 1.3f);
  681. idealWidth = font.getStringWidth (text) + idealHeight * 2;
  682. }
  683. }
  684. void LookAndFeel_V4::drawMenuBarBackground (Graphics& g, int width, int height,
  685. bool, MenuBarComponent& menuBar)
  686. {
  687. auto colour = menuBar.findColour (TextButton::buttonColourId).withAlpha (0.4f);
  688. Rectangle<int> r (width, height);
  689. g.setColour (colour.contrasting (0.15f));
  690. g.fillRect (r.removeFromTop (1));
  691. g.fillRect (r.removeFromBottom (1));
  692. g.setGradientFill (ColourGradient::vertical (colour, 0, colour.darker (0.2f), (float) height));
  693. g.fillRect (r);
  694. }
  695. void LookAndFeel_V4::drawMenuBarItem (Graphics& g, int width, int height,
  696. int itemIndex, const String& itemText,
  697. bool isMouseOverItem, bool isMenuOpen,
  698. bool /*isMouseOverBar*/, MenuBarComponent& menuBar)
  699. {
  700. if (! menuBar.isEnabled())
  701. {
  702. g.setColour (menuBar.findColour (TextButton::textColourOffId)
  703. .withMultipliedAlpha (0.5f));
  704. }
  705. else if (isMenuOpen || isMouseOverItem)
  706. {
  707. g.fillAll (menuBar.findColour (TextButton::buttonOnColourId));
  708. g.setColour (menuBar.findColour (TextButton::textColourOnId));
  709. }
  710. else
  711. {
  712. g.setColour (menuBar.findColour (TextButton::textColourOffId));
  713. }
  714. g.setFont (getMenuBarFont (menuBar, itemIndex, itemText));
  715. g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1);
  716. }
  717. //==============================================================================
  718. void LookAndFeel_V4::drawComboBox (Graphics& g, int width, int height, bool,
  719. int, int, int, int, ComboBox& box)
  720. {
  721. auto cornerSize = box.findParentComponentOfClass<ChoicePropertyComponent>() != nullptr ? 0.0f : 3.0f;
  722. Rectangle<int> boxBounds (0, 0, width, height);
  723. g.setColour (box.findColour (ComboBox::backgroundColourId));
  724. g.fillRoundedRectangle (boxBounds.toFloat(), cornerSize);
  725. g.setColour (box.findColour (ComboBox::outlineColourId));
  726. g.drawRoundedRectangle (boxBounds.toFloat().reduced (0.5f, 0.5f), cornerSize, 1.0f);
  727. Rectangle<int> arrowZone (width - 30, 0, 20, height);
  728. Path path;
  729. path.startNewSubPath (arrowZone.getX() + 3.0f, arrowZone.getCentreY() - 2.0f);
  730. path.lineTo (static_cast<float> (arrowZone.getCentreX()), arrowZone.getCentreY() + 3.0f);
  731. path.lineTo (arrowZone.getRight() - 3.0f, arrowZone.getCentreY() - 2.0f);
  732. g.setColour (box.findColour (ComboBox::arrowColourId).withAlpha ((box.isEnabled() ? 0.9f : 0.2f)));
  733. g.strokePath (path, PathStrokeType (2.0f));
  734. }
  735. Font LookAndFeel_V4::getComboBoxFont (ComboBox& box)
  736. {
  737. return { jmin (16.0f, box.getHeight() * 0.85f) };
  738. }
  739. void LookAndFeel_V4::positionComboBoxText (ComboBox& box, Label& label)
  740. {
  741. label.setBounds (1, 1,
  742. box.getWidth() - 30,
  743. box.getHeight() - 2);
  744. label.setFont (getComboBoxFont (box));
  745. }
  746. //==============================================================================
  747. int LookAndFeel_V4::getSliderThumbRadius (Slider& slider)
  748. {
  749. return jmin (12, slider.isHorizontal() ? static_cast<int> (slider.getHeight() * 0.5f)
  750. : static_cast<int> (slider.getWidth() * 0.5f));
  751. }
  752. void LookAndFeel_V4::drawLinearSlider (Graphics& g, int x, int y, int width, int height,
  753. float sliderPos,
  754. float minSliderPos,
  755. float maxSliderPos,
  756. const Slider::SliderStyle style, Slider& slider)
  757. {
  758. if (slider.isBar())
  759. {
  760. g.setColour (slider.findColour (Slider::trackColourId));
  761. g.fillRect (slider.isHorizontal() ? Rectangle<float> (static_cast<float> (x), y + 0.5f, sliderPos - x, height - 1.0f)
  762. : Rectangle<float> (x + 0.5f, sliderPos, width - 1.0f, y + (height - sliderPos)));
  763. }
  764. else
  765. {
  766. auto isTwoVal = (style == Slider::SliderStyle::TwoValueVertical || style == Slider::SliderStyle::TwoValueHorizontal);
  767. auto isThreeVal = (style == Slider::SliderStyle::ThreeValueVertical || style == Slider::SliderStyle::ThreeValueHorizontal);
  768. auto trackWidth = jmin (6.0f, slider.isHorizontal() ? height * 0.25f : width * 0.25f);
  769. Point<float> startPoint (slider.isHorizontal() ? x : x + width * 0.5f,
  770. slider.isHorizontal() ? y + height * 0.5f : height + y);
  771. Point<float> endPoint (slider.isHorizontal() ? width + x : startPoint.x,
  772. slider.isHorizontal() ? startPoint.y : y);
  773. Path backgroundTrack;
  774. backgroundTrack.startNewSubPath (startPoint);
  775. backgroundTrack.lineTo (endPoint);
  776. g.setColour (slider.findColour (Slider::backgroundColourId));
  777. g.strokePath (backgroundTrack, { trackWidth, PathStrokeType::curved, PathStrokeType::rounded });
  778. Path valueTrack;
  779. Point<float> minPoint, maxPoint, thumbPoint;
  780. if (isTwoVal || isThreeVal)
  781. {
  782. minPoint = { slider.isHorizontal() ? minSliderPos : width * 0.5f,
  783. slider.isHorizontal() ? height * 0.5f : minSliderPos };
  784. if (isThreeVal)
  785. thumbPoint = { slider.isHorizontal() ? sliderPos : width * 0.5f,
  786. slider.isHorizontal() ? height * 0.5f : sliderPos };
  787. maxPoint = { slider.isHorizontal() ? maxSliderPos : width * 0.5f,
  788. slider.isHorizontal() ? height * 0.5f : maxSliderPos };
  789. }
  790. else
  791. {
  792. auto kx = slider.isHorizontal() ? sliderPos : (x + width * 0.5f);
  793. auto ky = slider.isHorizontal() ? (y + height * 0.5f) : sliderPos;
  794. minPoint = startPoint;
  795. maxPoint = { kx, ky };
  796. }
  797. auto thumbWidth = getSliderThumbRadius (slider);
  798. valueTrack.startNewSubPath (minPoint);
  799. valueTrack.lineTo (isThreeVal ? thumbPoint : maxPoint);
  800. g.setColour (slider.findColour (Slider::trackColourId));
  801. g.strokePath (valueTrack, { trackWidth, PathStrokeType::curved, PathStrokeType::rounded });
  802. if (! isTwoVal)
  803. {
  804. g.setColour (slider.findColour (Slider::thumbColourId));
  805. g.fillEllipse (Rectangle<float> (static_cast<float> (thumbWidth), static_cast<float> (thumbWidth)).withCentre (isThreeVal ? thumbPoint : maxPoint));
  806. }
  807. if (isTwoVal || isThreeVal)
  808. {
  809. auto sr = jmin (trackWidth, (slider.isHorizontal() ? height : width) * 0.4f);
  810. auto pointerColour = slider.findColour (Slider::thumbColourId);
  811. if (slider.isHorizontal())
  812. {
  813. drawPointer (g, minSliderPos - sr,
  814. jmax (0.0f, y + height * 0.5f - trackWidth * 2.0f),
  815. trackWidth * 2.0f, pointerColour, 2);
  816. drawPointer (g, maxSliderPos - trackWidth,
  817. jmin (y + height - trackWidth * 2.0f, y + height * 0.5f),
  818. trackWidth * 2.0f, pointerColour, 4);
  819. }
  820. else
  821. {
  822. drawPointer (g, jmax (0.0f, x + width * 0.5f - trackWidth * 2.0f),
  823. minSliderPos - trackWidth,
  824. trackWidth * 2.0f, pointerColour, 1);
  825. drawPointer (g, jmin (x + width - trackWidth * 2.0f, x + width * 0.5f), maxSliderPos - sr,
  826. trackWidth * 2.0f, pointerColour, 3);
  827. }
  828. }
  829. }
  830. }
  831. void LookAndFeel_V4::drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
  832. const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider)
  833. {
  834. auto outline = slider.findColour (Slider::rotarySliderOutlineColourId);
  835. auto fill = slider.findColour (Slider::rotarySliderFillColourId);
  836. auto bounds = Rectangle<int> (x, y, width, height).toFloat().reduced (10);
  837. auto radius = jmin (bounds.getWidth(), bounds.getHeight()) / 2.0f;
  838. auto toAngle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
  839. auto lineW = jmin (8.0f, radius * 0.5f);
  840. auto arcRadius = radius - lineW * 0.5f;
  841. Path backgroundArc;
  842. backgroundArc.addCentredArc (bounds.getCentreX(),
  843. bounds.getCentreY(),
  844. arcRadius,
  845. arcRadius,
  846. 0.0f,
  847. rotaryStartAngle,
  848. rotaryEndAngle,
  849. true);
  850. g.setColour (outline);
  851. g.strokePath (backgroundArc, PathStrokeType (lineW, PathStrokeType::curved, PathStrokeType::rounded));
  852. if (slider.isEnabled())
  853. {
  854. Path valueArc;
  855. valueArc.addCentredArc (bounds.getCentreX(),
  856. bounds.getCentreY(),
  857. arcRadius,
  858. arcRadius,
  859. 0.0f,
  860. rotaryStartAngle,
  861. toAngle,
  862. true);
  863. g.setColour (fill);
  864. g.strokePath (valueArc, PathStrokeType (lineW, PathStrokeType::curved, PathStrokeType::rounded));
  865. }
  866. auto thumbWidth = lineW * 2.0f;
  867. Point<float> thumbPoint (bounds.getCentreX() + arcRadius * std::cos (toAngle - MathConstants<float>::halfPi),
  868. bounds.getCentreY() + arcRadius * std::sin (toAngle - MathConstants<float>::halfPi));
  869. g.setColour (slider.findColour (Slider::thumbColourId));
  870. g.fillEllipse (Rectangle<float> (thumbWidth, thumbWidth).withCentre (thumbPoint));
  871. }
  872. void LookAndFeel_V4::drawPointer (Graphics& g, const float x, const float y, const float diameter,
  873. const Colour& colour, const int direction) noexcept
  874. {
  875. Path p;
  876. p.startNewSubPath (x + diameter * 0.5f, y);
  877. p.lineTo (x + diameter, y + diameter * 0.6f);
  878. p.lineTo (x + diameter, y + diameter);
  879. p.lineTo (x, y + diameter);
  880. p.lineTo (x, y + diameter * 0.6f);
  881. p.closeSubPath();
  882. p.applyTransform (AffineTransform::rotation (direction * MathConstants<float>::halfPi,
  883. x + diameter * 0.5f, y + diameter * 0.5f));
  884. g.setColour (colour);
  885. g.fillPath (p);
  886. }
  887. Label* LookAndFeel_V4::createSliderTextBox (Slider& slider)
  888. {
  889. auto* l = LookAndFeel_V2::createSliderTextBox (slider);
  890. if (getCurrentColourScheme() == LookAndFeel_V4::getGreyColourScheme() && (slider.getSliderStyle() == Slider::LinearBar
  891. || slider.getSliderStyle() == Slider::LinearBarVertical))
  892. {
  893. l->setColour (Label::textColourId, Colours::black.withAlpha (0.7f));
  894. }
  895. return l;
  896. }
  897. //==============================================================================
  898. void LookAndFeel_V4::drawTooltip (Graphics& g, const String& text, int width, int height)
  899. {
  900. Rectangle<int> bounds (width, height);
  901. auto cornerSize = 5.0f;
  902. g.setColour (findColour (TooltipWindow::backgroundColourId));
  903. g.fillRoundedRectangle (bounds.toFloat(), cornerSize);
  904. g.setColour (findColour (TooltipWindow::outlineColourId));
  905. g.drawRoundedRectangle (bounds.toFloat().reduced (0.5f, 0.5f), cornerSize, 1.0f);
  906. LookAndFeelHelpers::layoutTooltipText (text, findColour (TooltipWindow::textColourId))
  907. .draw (g, { static_cast<float> (width), static_cast<float> (height) });
  908. }
  909. //==============================================================================
  910. void LookAndFeel_V4::drawConcertinaPanelHeader (Graphics& g, const Rectangle<int>& area,
  911. bool isMouseOver, bool /*isMouseDown*/,
  912. ConcertinaPanel& concertina, Component& panel)
  913. {
  914. auto bounds = area.toFloat().reduced (0.5f);
  915. auto cornerSize = 4.0f;
  916. auto isTopPanel = (concertina.getPanel (0) == &panel);
  917. Path p;
  918. p.addRoundedRectangle (bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
  919. cornerSize, cornerSize, isTopPanel, isTopPanel, false, false);
  920. g.setGradientFill (ColourGradient::vertical (Colours::white.withAlpha (isMouseOver ? 0.4f : 0.2f), static_cast<float> (area.getY()),
  921. Colours::darkgrey.withAlpha (0.1f), static_cast<float> (area.getBottom())));
  922. g.fillPath (p);
  923. }
  924. //==============================================================================
  925. void LookAndFeel_V4::drawLevelMeter (Graphics& g, int width, int height, float level)
  926. {
  927. auto outerCornerSize = 3.0f;
  928. auto outerBorderWidth = 2.0f;
  929. auto totalBlocks = 7;
  930. auto spacingFraction = 0.03f;
  931. g.setColour (findColour (ResizableWindow::backgroundColourId));
  932. g.fillRoundedRectangle (0.0f, 0.0f, static_cast<float> (width), static_cast<float> (height), outerCornerSize);
  933. auto doubleOuterBorderWidth = 2.0f * outerBorderWidth;
  934. auto numBlocks = roundToInt (totalBlocks * level);
  935. auto blockWidth = (width - doubleOuterBorderWidth) / static_cast<float> (totalBlocks);
  936. auto blockHeight = height - doubleOuterBorderWidth;
  937. auto blockRectWidth = (1.0f - 2.0f * spacingFraction) * blockWidth;
  938. auto blockRectSpacing = spacingFraction * blockWidth;
  939. auto blockCornerSize = 0.1f * blockWidth;
  940. auto c = findColour (Slider::thumbColourId);
  941. for (auto i = 0; i < totalBlocks; ++i)
  942. {
  943. if (i >= numBlocks)
  944. g.setColour (c.withAlpha (0.5f));
  945. else
  946. g.setColour (i < totalBlocks - 1 ? c : Colours::red);
  947. g.fillRoundedRectangle (outerBorderWidth + (i * blockWidth) + blockRectSpacing,
  948. outerBorderWidth,
  949. blockRectWidth,
  950. blockHeight,
  951. blockCornerSize);
  952. }
  953. }
  954. //==============================================================================
  955. void LookAndFeel_V4::paintToolbarBackground (Graphics& g, int w, int h, Toolbar& toolbar)
  956. {
  957. auto background = toolbar.findColour (Toolbar::backgroundColourId);
  958. g.setGradientFill ({ background, 0.0f, 0.0f,
  959. background.darker (0.2f),
  960. toolbar.isVertical() ? w - 1.0f : 0.0f,
  961. toolbar.isVertical() ? 0.0f : h - 1.0f,
  962. false });
  963. g.fillAll();
  964. }
  965. void LookAndFeel_V4::paintToolbarButtonLabel (Graphics& g, int x, int y, int width, int height,
  966. const String& text, ToolbarItemComponent& component)
  967. {
  968. auto baseTextColour = component.findParentComponentOfClass<PopupMenu::CustomComponent>() != nullptr
  969. ? component.findColour (PopupMenu::textColourId)
  970. : component.findColour (Toolbar::labelTextColourId);
  971. g.setColour (baseTextColour.withAlpha (component.isEnabled() ? 1.0f : 0.25f));
  972. auto fontHeight = jmin (14.0f, height * 0.85f);
  973. g.setFont (fontHeight);
  974. g.drawFittedText (text,
  975. x, y, width, height,
  976. Justification::centred,
  977. jmax (1, height / (int) fontHeight));
  978. }
  979. //==============================================================================
  980. void LookAndFeel_V4::drawPropertyPanelSectionHeader (Graphics& g, const String& name,
  981. bool isOpen, int width, int height)
  982. {
  983. auto buttonSize = height * 0.75f;
  984. auto buttonIndent = (height - buttonSize) * 0.5f;
  985. drawTreeviewPlusMinusBox (g, { buttonIndent, buttonIndent, buttonSize, buttonSize },
  986. findColour (ResizableWindow::backgroundColourId), isOpen, false);
  987. auto textX = static_cast<int> ((buttonIndent * 2.0f + buttonSize + 2.0f));
  988. g.setColour (findColour (PropertyComponent::labelTextColourId));
  989. g.setFont ({ height * 0.7f, Font::bold });
  990. g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true);
  991. }
  992. void LookAndFeel_V4::drawPropertyComponentBackground (Graphics& g, int width, int height, PropertyComponent& component)
  993. {
  994. g.setColour (component.findColour (PropertyComponent::backgroundColourId));
  995. g.fillRect (0, 0, width, height - 1);
  996. }
  997. void LookAndFeel_V4::drawPropertyComponentLabel (Graphics& g, int width, int height, PropertyComponent& component)
  998. {
  999. ignoreUnused (width);
  1000. auto indent = getPropertyComponentIndent (component);
  1001. g.setColour (component.findColour (PropertyComponent::labelTextColourId)
  1002. .withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.6f));
  1003. g.setFont (jmin (height, 24) * 0.65f);
  1004. auto r = getPropertyComponentContentPosition (component);
  1005. g.drawFittedText (component.getName(),
  1006. indent, r.getY(), r.getX() - 5, r.getHeight(),
  1007. Justification::centredLeft, 2);
  1008. }
  1009. int LookAndFeel_V4::getPropertyComponentIndent (PropertyComponent& component)
  1010. {
  1011. return jmin (10, component.getWidth() / 10);
  1012. }
  1013. Rectangle<int> LookAndFeel_V4::getPropertyComponentContentPosition (PropertyComponent& component)
  1014. {
  1015. auto textW = jmin (200, component.getWidth() / 2);
  1016. return { textW, 0, component.getWidth() - textW, component.getHeight() - 1 };
  1017. }
  1018. //==============================================================================
  1019. void LookAndFeel_V4::drawCallOutBoxBackground (CallOutBox& box, Graphics& g,
  1020. const Path& path, Image& cachedImage)
  1021. {
  1022. if (cachedImage.isNull())
  1023. {
  1024. cachedImage = { Image::ARGB, box.getWidth(), box.getHeight(), true };
  1025. Graphics g2 (cachedImage);
  1026. DropShadow (Colours::black.withAlpha (0.7f), 8, { 0, 2 }).drawForPath (g2, path);
  1027. }
  1028. g.setColour (Colours::black);
  1029. g.drawImageAt (cachedImage, 0, 0);
  1030. g.setColour (currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).withAlpha (0.8f));
  1031. g.fillPath (path);
  1032. g.setColour (currentColourScheme.getUIColour (ColourScheme::UIColour::outline).withAlpha (0.8f));
  1033. g.strokePath (path, PathStrokeType (2.0f));
  1034. }
  1035. //==============================================================================
  1036. void LookAndFeel_V4::drawStretchableLayoutResizerBar (Graphics& g, int /*w*/, int /*h*/, bool /*isVerticalBar*/,
  1037. bool isMouseOver, bool isMouseDragging)
  1038. {
  1039. if (isMouseOver || isMouseDragging)
  1040. g.fillAll (currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).withAlpha (0.5f));
  1041. }
  1042. //==============================================================================
  1043. void LookAndFeel_V4::initialiseColours()
  1044. {
  1045. const uint32 transparent = 0x00000000;
  1046. const uint32 coloursToUse[] =
  1047. {
  1048. TextButton::buttonColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1049. TextButton::buttonOnColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1050. TextButton::textColourOnId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedText).getARGB(),
  1051. TextButton::textColourOffId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1052. ToggleButton::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1053. ToggleButton::tickColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1054. ToggleButton::tickDisabledColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).withAlpha (0.5f).getARGB(),
  1055. TextEditor::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1056. TextEditor::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1057. TextEditor::highlightColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).withAlpha (0.4f).getARGB(),
  1058. TextEditor::highlightedTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedText).getARGB(),
  1059. TextEditor::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1060. TextEditor::focusedOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1061. TextEditor::shadowColourId, transparent,
  1062. CaretComponent::caretColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
  1063. Label::backgroundColourId, transparent,
  1064. Label::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1065. Label::outlineColourId, transparent,
  1066. Label::textWhenEditingColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1067. ScrollBar::backgroundColourId, transparent,
  1068. ScrollBar::thumbColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
  1069. ScrollBar::trackColourId, transparent,
  1070. TreeView::linesColourId, transparent,
  1071. TreeView::backgroundColourId, transparent,
  1072. TreeView::dragAndDropIndicatorColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1073. TreeView::selectedItemBackgroundColourId, transparent,
  1074. TreeView::oddItemsColourId, transparent,
  1075. TreeView::evenItemsColourId, transparent,
  1076. PopupMenu::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuBackground).getARGB(),
  1077. PopupMenu::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuText).getARGB(),
  1078. PopupMenu::headerTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuText).getARGB(),
  1079. PopupMenu::highlightedTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedText).getARGB(),
  1080. PopupMenu::highlightedBackgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1081. ComboBox::buttonColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1082. ComboBox::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1083. ComboBox::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1084. ComboBox::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1085. ComboBox::arrowColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1086. ComboBox::focusedOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1087. PropertyComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1088. PropertyComponent::labelTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1089. TextPropertyComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1090. TextPropertyComponent::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1091. TextPropertyComponent::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1092. BooleanPropertyComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1093. BooleanPropertyComponent::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1094. ListBox::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1095. ListBox::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1096. ListBox::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1097. Slider::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1098. Slider::thumbColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
  1099. Slider::trackColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1100. Slider::rotarySliderFillColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1101. Slider::rotarySliderOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1102. Slider::textBoxTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1103. Slider::textBoxBackgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).withAlpha (0.0f).getARGB(),
  1104. Slider::textBoxHighlightColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).withAlpha (0.4f).getARGB(),
  1105. Slider::textBoxOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1106. ResizableWindow::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::windowBackground).getARGB(),
  1107. DocumentWindow::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1108. AlertWindow::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1109. AlertWindow::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1110. AlertWindow::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1111. ProgressBar::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1112. ProgressBar::foregroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1113. TooltipWindow::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1114. TooltipWindow::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedText).getARGB(),
  1115. TooltipWindow::outlineColourId, transparent,
  1116. TabbedComponent::backgroundColourId, transparent,
  1117. TabbedComponent::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1118. TabbedButtonBar::tabOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).withAlpha (0.5f).getARGB(),
  1119. TabbedButtonBar::frontOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1120. Toolbar::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).withAlpha (0.4f).getARGB(),
  1121. Toolbar::separatorColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1122. Toolbar::buttonMouseOverBackgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).contrasting (0.2f).getARGB(),
  1123. Toolbar::buttonMouseDownBackgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).contrasting (0.5f).getARGB(),
  1124. Toolbar::labelTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1125. Toolbar::editingModeOutlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1126. DrawableButton::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1127. DrawableButton::textColourOnId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedText).getARGB(),
  1128. DrawableButton::backgroundColourId, transparent,
  1129. DrawableButton::backgroundOnColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1130. HyperlinkButton::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).interpolatedWith (Colours::blue, 0.4f).getARGB(),
  1131. GroupComponent::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1132. GroupComponent::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1133. BubbleComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1134. BubbleComponent::outlineColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1135. DirectoryContentsDisplayComponent::highlightColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).getARGB(),
  1136. DirectoryContentsDisplayComponent::textColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuText).getARGB(),
  1137. DirectoryContentsDisplayComponent::highlightedTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedText).getARGB(),
  1138. 0x1000440, /*LassoComponent::lassoFillColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
  1139. 0x1000441, /*LassoComponent::lassoOutlineColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::outline).getARGB(),
  1140. 0x1005000, /*MidiKeyboardComponent::whiteNoteColourId*/ 0xffffffff,
  1141. 0x1005001, /*MidiKeyboardComponent::blackNoteColourId*/ 0xff000000,
  1142. 0x1005002, /*MidiKeyboardComponent::keySeparatorLineColourId*/ 0x66000000,
  1143. 0x1005003, /*MidiKeyboardComponent::mouseOverKeyOverlayColourId*/ 0x80ffff00,
  1144. 0x1005004, /*MidiKeyboardComponent::keyDownOverlayColourId*/ 0xffb6b600,
  1145. 0x1005005, /*MidiKeyboardComponent::textLabelColourId*/ 0xff000000,
  1146. 0x1005006, /*MidiKeyboardComponent::upDownButtonBackgroundColourId*/ 0xffd3d3d3,
  1147. 0x1005007, /*MidiKeyboardComponent::upDownButtonArrowColourId*/ 0xff000000,
  1148. 0x1005008, /*MidiKeyboardComponent::shadowColourId*/ 0x4c000000,
  1149. 0x1004500, /*CodeEditorComponent::backgroundColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1150. 0x1004502, /*CodeEditorComponent::highlightColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).withAlpha (0.4f).getARGB(),
  1151. 0x1004503, /*CodeEditorComponent::defaultTextColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1152. 0x1004504, /*CodeEditorComponent::lineNumberBackgroundId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::highlightedFill).withAlpha (0.5f).getARGB(),
  1153. 0x1004505, /*CodeEditorComponent::lineNumberTextId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
  1154. 0x1007000, /*ColourSelector::backgroundColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1155. 0x1007001, /*ColourSelector::labelTextColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1156. 0x100ad00, /*KeyMappingEditorComponent::backgroundColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1157. 0x100ad01, /*KeyMappingEditorComponent::textColourId*/ currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1158. FileSearchPathListComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuBackground).getARGB(),
  1159. FileChooserDialogBox::titleTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1160. SidePanel::backgroundColour, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
  1161. SidePanel::titleTextColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
  1162. SidePanel::shadowBaseColour, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).darker().getARGB(),
  1163. SidePanel::dismissButtonNormalColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
  1164. SidePanel::dismissButtonOverColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).darker().getARGB(),
  1165. SidePanel::dismissButtonDownColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).brighter().getARGB(),
  1166. FileBrowserComponent::currentPathBoxBackgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuBackground).getARGB(),
  1167. FileBrowserComponent::currentPathBoxTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuText).getARGB(),
  1168. FileBrowserComponent::currentPathBoxArrowColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuText).getARGB(),
  1169. FileBrowserComponent::filenameBoxBackgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuBackground).getARGB(),
  1170. FileBrowserComponent::filenameBoxTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuText).getARGB(),
  1171. };
  1172. for (int i = 0; i < numElementsInArray (coloursToUse); i += 2)
  1173. setColour ((int) coloursToUse [i], Colour ((uint32) coloursToUse [i + 1]));
  1174. }
  1175. } // namespace juce