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.

juce_LookAndFeel_V4.cpp 65KB

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