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