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