JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

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