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_V3.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. LookAndFeel_V3::LookAndFeel_V3()
  18. {
  19. setColour (TreeView::selectedItemBackgroundColourId, Colour (0x301111ee));
  20. const Colour textButtonColour (0xffeeeeff);
  21. setColour (TextButton::buttonColourId, textButtonColour);
  22. setColour (ComboBox::buttonColourId, textButtonColour);
  23. setColour (ScrollBar::thumbColourId, Colour::greyLevel (0.8f).contrasting().withAlpha (0.13f));
  24. }
  25. LookAndFeel_V3::~LookAndFeel_V3() {}
  26. bool LookAndFeel_V3::areScrollbarButtonsVisible() { return false; }
  27. void LookAndFeel_V3::drawStretchableLayoutResizerBar (Graphics& g, int /*w*/, int /*h*/, bool /*isVerticalBar*/,
  28. bool isMouseOver, bool isMouseDragging)
  29. {
  30. if (isMouseOver || isMouseDragging)
  31. g.fillAll (Colours::yellow.withAlpha (0.4f));
  32. }
  33. void LookAndFeel_V3::drawScrollbar (Graphics& g, ScrollBar& scrollbar, int x, int y, int width, int height,
  34. bool isScrollbarVertical, int thumbStartPosition, int thumbSize, bool isMouseOver, bool isMouseDown)
  35. {
  36. Path thumbPath;
  37. if (thumbSize > 0)
  38. {
  39. const float thumbIndent = (isScrollbarVertical ? width : height) * 0.25f;
  40. const float thumbIndentx2 = thumbIndent * 2.0f;
  41. if (isScrollbarVertical)
  42. thumbPath.addRoundedRectangle (x + thumbIndent, thumbStartPosition + thumbIndent,
  43. width - thumbIndentx2, thumbSize - thumbIndentx2, (width - thumbIndentx2) * 0.5f);
  44. else
  45. thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, y + thumbIndent,
  46. thumbSize - thumbIndentx2, height - thumbIndentx2, (height - thumbIndentx2) * 0.5f);
  47. }
  48. Colour thumbCol (scrollbar.findColour (ScrollBar::thumbColourId, true));
  49. if (isMouseOver || isMouseDown)
  50. thumbCol = thumbCol.withMultipliedAlpha (2.0f);
  51. g.setColour (thumbCol);
  52. g.fillPath (thumbPath);
  53. g.setColour (thumbCol.contrasting ((isMouseOver || isMouseDown) ? 0.2f : 0.1f));
  54. g.strokePath (thumbPath, PathStrokeType (1.0f));
  55. }
  56. void LookAndFeel_V3::drawConcertinaPanelHeader (Graphics& g, const Rectangle<int>& area,
  57. bool isMouseOver, bool /*isMouseDown*/,
  58. ConcertinaPanel&, Component& panel)
  59. {
  60. const Colour bkg (Colours::grey);
  61. g.setGradientFill (ColourGradient (Colours::white.withAlpha (isMouseOver ? 0.4f : 0.2f), 0, (float) area.getY(),
  62. Colours::darkgrey.withAlpha (0.2f), 0, (float) area.getBottom(), false));
  63. g.fillAll();
  64. g.setColour (bkg.contrasting().withAlpha (0.04f));
  65. g.fillRect (area.withHeight (1));
  66. g.fillRect (area.withTop (area.getBottom() - 1));
  67. g.setColour (bkg.contrasting());
  68. g.setFont (Font (area.getHeight() * 0.6f).boldened());
  69. g.drawFittedText (panel.getName(), 4, 0, area.getWidth() - 6, area.getHeight(), Justification::centredLeft, 1);
  70. }
  71. static void drawButtonShape (Graphics& g, const Path& outline, Colour baseColour, float height)
  72. {
  73. const float mainBrightness = baseColour.getBrightness();
  74. const float mainAlpha = baseColour.getFloatAlpha();
  75. g.setGradientFill (ColourGradient (baseColour.brighter (0.2f), 0.0f, 0.0f,
  76. baseColour.darker (0.25f), 0.0f, height, false));
  77. g.fillPath (outline);
  78. g.setColour (Colours::white.withAlpha (0.4f * mainAlpha * mainBrightness * mainBrightness));
  79. g.strokePath (outline, PathStrokeType (1.0f), AffineTransform::translation (0.0f, 1.0f)
  80. .scaled (1.0f, (height - 1.6f) / height));
  81. g.setColour (Colours::black.withAlpha (0.4f * mainAlpha));
  82. g.strokePath (outline, PathStrokeType (1.0f));
  83. }
  84. void LookAndFeel_V3::drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
  85. bool isMouseOverButton, bool isButtonDown)
  86. {
  87. Colour baseColour (backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
  88. .withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f));
  89. if (isButtonDown || isMouseOverButton)
  90. baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f);
  91. const bool flatOnLeft = button.isConnectedOnLeft();
  92. const bool flatOnRight = button.isConnectedOnRight();
  93. const bool flatOnTop = button.isConnectedOnTop();
  94. const bool flatOnBottom = button.isConnectedOnBottom();
  95. const float width = button.getWidth() - 1.0f;
  96. const float height = button.getHeight() - 1.0f;
  97. const float cornerSize = 4.0f;
  98. Path outline;
  99. outline.addRoundedRectangle (0.5f, 0.5f, width, height, cornerSize, cornerSize,
  100. ! (flatOnLeft || flatOnTop),
  101. ! (flatOnRight || flatOnTop),
  102. ! (flatOnLeft || flatOnBottom),
  103. ! (flatOnRight || flatOnBottom));
  104. drawButtonShape (g, outline, baseColour, height);
  105. }
  106. void LookAndFeel_V3::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header)
  107. {
  108. Rectangle<int> r (header.getLocalBounds());
  109. g.setColour (Colours::black.withAlpha (0.5f));
  110. g.fillRect (r.removeFromBottom (1));
  111. g.setColour (Colours::white.withAlpha (0.6f));
  112. g.fillRect (r);
  113. g.setColour (Colours::black.withAlpha (0.5f));
  114. for (int i = header.getNumColumns (true); --i >= 0;)
  115. g.fillRect (header.getColumnPosition (i).removeFromRight (1));
  116. }
  117. int LookAndFeel_V3::getTabButtonOverlap (int /*tabDepth*/) { return -1; }
  118. int LookAndFeel_V3::getTabButtonSpaceAroundImage() { return 1; }
  119. void LookAndFeel_V3::createTabTextLayout (const TabBarButton& button, float length, float depth,
  120. Colour colour, TextLayout& textLayout)
  121. {
  122. Font font (depth * 0.5f);
  123. font.setUnderline (button.hasKeyboardFocus (false));
  124. AttributedString s;
  125. s.setJustification (Justification::centred);
  126. s.append (button.getButtonText().trim(), font, colour);
  127. textLayout.createLayout (s, length);
  128. }
  129. void LookAndFeel_V3::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
  130. {
  131. const Rectangle<int> activeArea (button.getActiveArea());
  132. const TabbedButtonBar::Orientation o = button.getTabbedButtonBar().getOrientation();
  133. const Colour bkg (button.getTabBackgroundColour());
  134. if (button.getToggleState())
  135. {
  136. g.setColour (bkg);
  137. g.fillRect (activeArea);
  138. }
  139. else
  140. {
  141. Point<int> p1, p2;
  142. switch (o)
  143. {
  144. case TabbedButtonBar::TabsAtBottom: p1 = activeArea.getBottomLeft(); p2 = activeArea.getTopLeft(); break;
  145. case TabbedButtonBar::TabsAtTop: p1 = activeArea.getTopLeft(); p2 = activeArea.getBottomLeft(); break;
  146. case TabbedButtonBar::TabsAtRight: p1 = activeArea.getTopRight(); p2 = activeArea.getTopLeft(); break;
  147. case TabbedButtonBar::TabsAtLeft: p1 = activeArea.getTopLeft(); p2 = activeArea.getTopRight(); break;
  148. default: jassertfalse; break;
  149. }
  150. g.setGradientFill (ColourGradient (bkg.brighter (0.2f), (float) p1.x, (float) p1.y,
  151. bkg.darker (0.1f), (float) p2.x, (float) p2.y, false));
  152. g.fillRect (activeArea);
  153. }
  154. g.setColour (bkg.contrasting (0.3f));
  155. Rectangle<int> r (activeArea);
  156. if (o != TabbedButtonBar::TabsAtBottom) g.fillRect (r.removeFromTop (1));
  157. if (o != TabbedButtonBar::TabsAtTop) g.fillRect (r.removeFromBottom (1));
  158. if (o != TabbedButtonBar::TabsAtRight) g.fillRect (r.removeFromLeft (1));
  159. if (o != TabbedButtonBar::TabsAtLeft) g.fillRect (r.removeFromRight (1));
  160. const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f;
  161. const Colour col (bkg.contrasting().withMultipliedAlpha (alpha));
  162. const Rectangle<float> area (button.getTextArea().toFloat());
  163. float length = area.getWidth();
  164. float depth = area.getHeight();
  165. if (button.getTabbedButtonBar().isVertical())
  166. std::swap (length, depth);
  167. TextLayout textLayout;
  168. createTabTextLayout (button, length, depth, col, textLayout);
  169. AffineTransform t;
  170. switch (o)
  171. {
  172. case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom()); break;
  173. case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated (area.getRight(), area.getY()); break;
  174. case TabbedButtonBar::TabsAtTop:
  175. case TabbedButtonBar::TabsAtBottom: t = t.translated (area.getX(), area.getY()); break;
  176. default: jassertfalse; break;
  177. }
  178. g.addTransform (t);
  179. textLayout.draw (g, Rectangle<float> (length, depth));
  180. }
  181. void LookAndFeel_V3::drawTreeviewPlusMinusBox (Graphics& g, const Rectangle<float>& area,
  182. Colour backgroundColour, bool isOpen, bool isMouseOver)
  183. {
  184. Path p;
  185. p.addTriangle (0.0f, 0.0f, 1.0f, isOpen ? 0.0f : 0.5f, isOpen ? 0.5f : 0.0f, 1.0f);
  186. g.setColour (backgroundColour.contrasting().withAlpha (isMouseOver ? 0.5f : 0.3f));
  187. g.fillPath (p, p.getTransformToScaleToFit (area.reduced (2, area.getHeight() / 4), true));
  188. }
  189. bool LookAndFeel_V3::areLinesDrawnForTreeView (TreeView&)
  190. {
  191. return false;
  192. }
  193. int LookAndFeel_V3::getTreeViewIndentSize (TreeView&)
  194. {
  195. return 20;
  196. }
  197. void LookAndFeel_V3::drawComboBox (Graphics& g, int width, int height, const bool isButtonDown,
  198. int buttonX, int buttonY, int buttonW, int buttonH, ComboBox& box)
  199. {
  200. g.fillAll (box.findColour (ComboBox::backgroundColourId));
  201. const Colour buttonColour (box.findColour (ComboBox::buttonColourId));
  202. if (box.isEnabled() && box.hasKeyboardFocus (false))
  203. {
  204. g.setColour (buttonColour);
  205. g.drawRect (0, 0, width, height, 2);
  206. }
  207. else
  208. {
  209. g.setColour (box.findColour (ComboBox::outlineColourId));
  210. g.drawRect (0, 0, width, height);
  211. }
  212. const float outlineThickness = box.isEnabled() ? (isButtonDown ? 1.2f : 0.5f) : 0.3f;
  213. Path buttonShape;
  214. buttonShape.addRectangle (buttonX + outlineThickness,
  215. buttonY + outlineThickness,
  216. buttonW - outlineThickness * 2.0f,
  217. buttonH - outlineThickness * 2.0f);
  218. drawButtonShape (g, buttonShape,
  219. buttonColour.withMultipliedSaturation (box.hasKeyboardFocus (true) ? 1.3f : 0.9f)
  220. .withMultipliedAlpha (box.isEnabled() ? 0.9f : 0.5f),
  221. (float) height);
  222. if (box.isEnabled())
  223. {
  224. const float arrowX = 0.3f;
  225. const float arrowH = 0.2f;
  226. Path p;
  227. p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH),
  228. buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f,
  229. buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f);
  230. p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH),
  231. buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f,
  232. buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f);
  233. g.setColour (box.findColour (ComboBox::arrowColourId));
  234. g.fillPath (p);
  235. }
  236. }
  237. void LookAndFeel_V3::drawPopupMenuBackground (Graphics& g, int width, int height)
  238. {
  239. g.fillAll (findColour (PopupMenu::backgroundColourId));
  240. (void) width; (void) height;
  241. #if ! JUCE_MAC
  242. g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f));
  243. g.drawRect (0, 0, width, height);
  244. #endif
  245. }
  246. void LookAndFeel_V3::drawKeymapChangeButton (Graphics& g, int width, int height,
  247. Button& button, const String& keyDescription)
  248. {
  249. const Colour textColour (button.findColour (0x100ad01 /*KeyMappingEditorComponent::textColourId*/, true));
  250. if (keyDescription.isNotEmpty())
  251. {
  252. if (button.isEnabled())
  253. {
  254. g.setColour (textColour.withAlpha (button.isDown() ? 0.4f : (button.isOver() ? 0.2f : 0.1f)));
  255. g.fillRoundedRectangle (button.getLocalBounds().toFloat(), 4.0f);
  256. g.drawRoundedRectangle (button.getLocalBounds().toFloat(), 4.0f, 1.0f);
  257. }
  258. g.setColour (textColour);
  259. g.setFont (height * 0.6f);
  260. g.drawFittedText (keyDescription, 4, 0, width - 8, height, Justification::centred, 1);
  261. }
  262. else
  263. {
  264. const float thickness = 7.0f;
  265. const float indent = 22.0f;
  266. Path p;
  267. p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f);
  268. p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f);
  269. p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness);
  270. p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness);
  271. p.setUsingNonZeroWinding (false);
  272. g.setColour (textColour.darker(0.1f).withAlpha (button.isDown() ? 0.7f : (button.isOver() ? 0.5f : 0.3f)));
  273. g.fillPath (p, p.getTransformToScaleToFit (2.0f, 2.0f, width - 4.0f, height - 4.0f, true));
  274. }
  275. if (button.hasKeyboardFocus (false))
  276. {
  277. g.setColour (textColour.withAlpha (0.4f));
  278. g.drawRect (0, 0, width, height);
  279. }
  280. }