MyLNF.cpp (15130B)
1 #include "MyLNF.h" 2 #include "ModulatableSlider.h" 3 4 MyLNF::MyLNF() 5 { 6 roboto = Typeface::createSystemTypefaceFor (BinaryData::RobotoCondensedRegular_ttf, 7 BinaryData::RobotoCondensedRegular_ttfSize); 8 9 robotoBold = Typeface::createSystemTypefaceFor (BinaryData::RobotoCondensedBold_ttf, 10 BinaryData::RobotoCondensedBold_ttfSize); 11 12 setColour (TabbedButtonBar::tabOutlineColourId, Colour (0xFF595C6B)); 13 } 14 15 Typeface::Ptr MyLNF::getTypefaceForFont (const Font& font) 16 { 17 return font.isBold() ? robotoBold : roboto; 18 } 19 20 void MyLNF::drawRotarySlider (juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float rotaryStartAngle, float rotaryEndAngle, juce::Slider& s) 21 { 22 int diameter = (width > height) ? height : width; 23 if (diameter < 16) 24 return; 25 26 juce::Point<float> centre (x + std::floor (width * 0.5f + 0.5f), y + std::floor (height * 0.5f + 0.5f)); 27 diameter -= (diameter % 2 == 1) ? 9 : 8; 28 float radius = (float) diameter * 0.5f; 29 x = int (centre.x - radius); 30 y = int (centre.y - radius); 31 32 const auto bounds = juce::Rectangle<int> (x, y, diameter, diameter).toFloat(); 33 34 auto b = pointer->getBounds().toFloat(); 35 pointer->setTransform (AffineTransform::rotation (MathConstants<float>::twoPi * ((sliderPos - 0.5f) * 300.0f / 360.0f), 36 b.getCentreX(), 37 b.getCentreY())); 38 39 const auto alpha = s.isEnabled() ? 1.0f : 0.4f; 40 41 auto knobBounds = (bounds * 0.75f).withCentre (centre); 42 knob->drawWithin (g, knobBounds, RectanglePlacement::stretchToFit, alpha); 43 pointer->drawWithin (g, knobBounds, RectanglePlacement::stretchToFit, alpha); 44 45 auto modSliderPos = sliderPos; 46 if (auto* modSlider = dynamic_cast<ModulatableSlider*> (&s)) 47 modSliderPos = (float) modSlider->getModulatedPosition(); 48 49 const auto toAngle = rotaryStartAngle + modSliderPos * (rotaryEndAngle - rotaryStartAngle); 50 constexpr float arcFactor = 0.9f; 51 52 Path valueArc; 53 valueArc.addPieSegment (bounds, rotaryStartAngle, rotaryEndAngle, arcFactor); 54 g.setColour (Colour (0xff595c6b).withAlpha (alpha)); 55 g.fillPath (valueArc); 56 valueArc.clear(); 57 58 valueArc.addPieSegment (bounds, rotaryStartAngle, toAngle, arcFactor); 59 g.setColour (Colour (0xff9cbcbd).withAlpha (alpha)); 60 g.fillPath (valueArc); 61 } 62 63 void MyLNF::drawToggleButton (Graphics& g, ToggleButton& button, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) 64 { 65 auto fontSize = jmin (15.0f, (float) button.getHeight() * 0.75f); 66 auto tickWidth = fontSize * 1.1f; 67 68 drawTickBox (g, button, 4.0f, ((float) button.getHeight() - tickWidth) * 0.5f, tickWidth, tickWidth, button.getToggleState(), button.isEnabled(), shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown); 69 70 g.setColour (button.findColour (ToggleButton::textColourId)); 71 g.setFont (Font (fontSize).boldened()); 72 73 if (! button.isEnabled()) 74 g.setOpacity (0.5f); 75 76 g.drawFittedText (button.getButtonText(), 77 button.getLocalBounds().withTrimmedLeft (roundToInt (tickWidth) + 10).withTrimmedRight (2), 78 Justification::centredLeft, 79 10); 80 } 81 82 void MyLNF::createTabTextLayout (const TabBarButton& button, float length, float depth, Colour colour, TextLayout& textLayout) 83 { 84 Font font (depth * 0.45f, Font::bold); 85 font.setUnderline (button.hasKeyboardFocus (false)); 86 87 AttributedString s; 88 s.setJustification (Justification::centred); 89 s.append (button.getButtonText().trim(), font, colour); 90 91 textLayout.createLayout (s, length); 92 } 93 94 void MyLNF::drawTabButton (TabBarButton& button, Graphics& g, bool /*isMouseOver*/, bool /*isMouseDown*/) 95 { 96 button.setViewportIgnoreDragFlag (true); 97 const Rectangle<int> activeArea (button.getActiveArea()); 98 99 const TabbedButtonBar::Orientation o = button.getTabbedButtonBar().getOrientation(); 100 101 const Colour bkg (button.getTabBackgroundColour()); 102 103 if (button.getToggleState()) 104 { 105 g.setColour (bkg); 106 } 107 else 108 { 109 juce::Point<int> p1, p2; 110 111 switch (o) 112 { 113 case TabbedButtonBar::TabsAtBottom: 114 p1 = activeArea.getBottomLeft(); 115 p2 = activeArea.getTopLeft(); 116 break; 117 case TabbedButtonBar::TabsAtTop: 118 p1 = activeArea.getTopLeft(); 119 p2 = activeArea.getBottomLeft(); 120 break; 121 case TabbedButtonBar::TabsAtRight: 122 p1 = activeArea.getTopRight(); 123 p2 = activeArea.getTopLeft(); 124 break; 125 case TabbedButtonBar::TabsAtLeft: 126 p1 = activeArea.getTopLeft(); 127 p2 = activeArea.getTopRight(); 128 break; 129 default: 130 jassertfalse; 131 break; 132 } 133 134 g.setGradientFill (ColourGradient (bkg.brighter (0.2f), p1.toFloat(), bkg.darker (0.1f), p2.toFloat(), false)); 135 } 136 137 g.fillRect (activeArea); 138 139 g.setColour (button.findColour (TabbedButtonBar::tabOutlineColourId)); 140 141 Rectangle<int> r (activeArea); 142 g.fillRect (r.removeFromBottom (1)); 143 144 const float alpha = 0.6f; 145 Colour col (bkg.contrasting().withMultipliedAlpha (alpha)); 146 147 if (button.isFrontTab()) 148 col = Colours::white; 149 150 const Rectangle<float> area (button.getTextArea().toFloat()); 151 152 float length = area.getWidth(); 153 float depth = area.getHeight(); 154 155 if (button.getTabbedButtonBar().isVertical()) 156 std::swap (length, depth); 157 158 TextLayout textLayout; 159 createTabTextLayout (button, length, depth, col, textLayout); 160 161 AffineTransform t; 162 163 switch (o) 164 { 165 case TabbedButtonBar::TabsAtLeft: 166 t = t.rotated (MathConstants<float>::pi * -0.5f).translated (area.getX(), area.getBottom()); 167 break; 168 case TabbedButtonBar::TabsAtRight: 169 t = t.rotated (MathConstants<float>::pi * 0.5f).translated (area.getRight(), area.getY()); 170 break; 171 case TabbedButtonBar::TabsAtTop: 172 case TabbedButtonBar::TabsAtBottom: 173 t = t.translated (area.getX(), area.getY()); 174 break; 175 default: 176 jassertfalse; 177 break; 178 } 179 180 g.addTransform (t); 181 textLayout.draw (g, Rectangle<float> (length, depth)); 182 } 183 184 Button* MyLNF::createTabBarExtrasButton() 185 { 186 Rectangle<float> arrowZone (-10.0f, -10.0f, 120.0f, 120.0f); 187 constexpr auto xPad = 25.0f; 188 const auto arrowHeight = arrowZone.getHeight() / 3.0f; 189 const auto yStart = arrowZone.getCentreY() - arrowHeight / 2.0f; 190 const auto yEnd = arrowZone.getCentreY() + arrowHeight / 2.0f; 191 192 Path path; 193 path.startNewSubPath (arrowZone.getX() + xPad, yStart); 194 path.lineTo (arrowZone.getCentreX(), yEnd); 195 path.lineTo (arrowZone.getRight() - xPad, yStart); 196 197 DrawablePath arrow; 198 arrow.setPath (path); 199 arrow.setFill (Colours::white); 200 201 path.clear(); 202 path.addEllipse (arrowZone); 203 204 DrawablePath background; 205 background.setPath (path); 206 background.setFill (findColour (TabbedComponent::ColourIds::backgroundColourId)); 207 208 DrawableComposite normalImage; 209 normalImage.addAndMakeVisible (background.createCopy().release()); 210 normalImage.addAndMakeVisible (arrow.createCopy().release()); 211 212 arrow.setFill (Colours::white.darker()); 213 214 DrawableComposite overImage; 215 overImage.addAndMakeVisible (background.createCopy().release()); 216 overImage.addAndMakeVisible (arrow.createCopy().release()); 217 218 auto db = new DrawableButton ("tabs", DrawableButton::ImageFitted); 219 db->setImages (&normalImage, &overImage, nullptr); 220 return db; 221 } 222 223 void MyLNF::drawLinearSlider (Graphics& g, int x, int y, int width, int height, float sliderPos, float /*minSliderPos*/, float /*maxSliderPos*/, const Slider::SliderStyle, Slider& slider) 224 { 225 auto trackWidth = jmin (10.0f, slider.isHorizontal() ? (float) height * 0.25f : (float) width * 0.25f); 226 227 juce::Point<float> startPoint (slider.isHorizontal() ? (float) x : (float) x + (float) width * 0.5f, 228 slider.isHorizontal() ? (float) y + (float) height * 0.5f : (float) (height + y)); 229 230 juce::Point<float> endPoint (slider.isHorizontal() ? (float) (width + x) : startPoint.x, 231 slider.isHorizontal() ? startPoint.y : (float) y); 232 233 const auto alpha = slider.isEnabled() ? 1.0f : 0.4f; 234 235 Path backgroundTrack; 236 backgroundTrack.startNewSubPath (startPoint); 237 backgroundTrack.lineTo (endPoint); 238 g.setColour (slider.findColour (Slider::backgroundColourId).withAlpha (alpha)); 239 g.strokePath (backgroundTrack, { trackWidth, PathStrokeType::curved, PathStrokeType::rounded }); 240 241 Path valueTrack; 242 juce::Point<float> minPoint, maxPoint, modPoint; 243 244 { 245 auto kx = slider.isHorizontal() ? sliderPos : ((float) x + (float) width * 0.5f); 246 auto ky = slider.isHorizontal() ? ((float) y + (float) height * 0.5f) : sliderPos; 247 248 minPoint = startPoint; 249 maxPoint = { kx, ky }; 250 251 modPoint = maxPoint; 252 if (auto* modSlider = dynamic_cast<ModulatableSlider*> (&slider)) 253 { 254 const auto modSliderPos = (float) modSlider->getModulatedPosition(); 255 auto kmx = slider.isHorizontal() ? (startPoint.x + (endPoint.x - startPoint.x) * modSliderPos) : ((float) x + (float) width * 0.5f); 256 auto kmy = slider.isHorizontal() ? ((float) y + (float) height * 0.5f) : (startPoint.y + (endPoint.y - startPoint.y) * modSliderPos); 257 258 modPoint = { kmx, kmy }; 259 } 260 } 261 262 auto thumbWidth = juce::jmax (trackWidth * 2.5f, (float) getSliderThumbRadius (slider)); 263 264 valueTrack.startNewSubPath (minPoint); 265 valueTrack.lineTo (modPoint); 266 g.setColour (slider.findColour (Slider::trackColourId).withAlpha (alpha)); 267 g.strokePath (valueTrack, { trackWidth, PathStrokeType::curved, PathStrokeType::rounded }); 268 269 auto thumbRect = Rectangle<float> (thumbWidth, thumbWidth) 270 .withCentre (maxPoint); 271 knob->drawWithin (g, thumbRect, RectanglePlacement::stretchToFit, alpha); 272 } 273 274 Slider::SliderLayout MyLNF::getSliderLayout (Slider& slider) 275 { 276 auto layout = LookAndFeel_V4::getSliderLayout (slider); 277 278 auto style = slider.getSliderStyle(); 279 if (style == Slider::LinearHorizontal) 280 layout.textBoxBounds = layout.textBoxBounds.withX (layout.sliderBounds.getX()); 281 282 return layout; 283 } 284 285 Label* MyLNF::createSliderTextBox (Slider& slider) 286 { 287 struct ReturnFocusSliderLabel : public juce::Label 288 { 289 public: 290 explicit ReturnFocusSliderLabel (Slider& baseSlider) : juce::Label ({}, {}), 291 slider (baseSlider) 292 { 293 } 294 295 ~ReturnFocusSliderLabel() override = default; 296 297 void textEditorReturnKeyPressed (TextEditor& editor) override 298 { 299 juce::Label::textEditorReturnKeyPressed (editor); 300 slider.grabKeyboardFocus(); 301 } 302 303 void textEditorEscapeKeyPressed (TextEditor& editor) override 304 { 305 juce::Label::textEditorEscapeKeyPressed (editor); 306 slider.grabKeyboardFocus(); 307 } 308 309 std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override 310 { 311 return createIgnoredAccessibilityHandler (*this); 312 } 313 314 Slider& slider; 315 }; 316 317 auto* label = new ReturnFocusSliderLabel (slider); 318 319 label->setJustificationType (Justification::centred); 320 label->setKeyboardType (TextInputTarget::decimalKeyboard); 321 322 label->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId)); 323 label->setColour (Label::backgroundColourId, 324 (slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) 325 ? Colours::transparentBlack 326 : slider.findColour (Slider::textBoxBackgroundColourId)); 327 label->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); 328 label->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId)); 329 label->setColour (TextEditor::backgroundColourId, 330 slider.findColour (Slider::textBoxBackgroundColourId) 331 .withAlpha ((slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) 332 ? 0.7f 333 : 1.0f)); 334 label->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); 335 label->setColour (TextEditor::highlightColourId, slider.findColour (Slider::textBoxHighlightColourId)); 336 337 auto style = slider.getSliderStyle(); 338 if (style == Slider::LinearHorizontal) 339 label->setJustificationType (Justification::left); 340 341 label->setFont ((float) slider.getTextBoxHeight()); 342 343 return label; 344 } 345 346 Component* MyLNF::getParentComponentForMenuOptions (const PopupMenu::Options& options) 347 { 348 #if JUCE_IOS 349 if (PluginHostType::getPluginLoadedAs() == AudioProcessor::wrapperType_AudioUnitv3) 350 { 351 if (options.getParentComponent() == nullptr && options.getTargetComponent() != nullptr) 352 return options.getTargetComponent()->getTopLevelComponent(); 353 } 354 #endif 355 return LookAndFeel_V2::getParentComponentForMenuOptions (options); 356 } 357 358 PopupMenu::Options MyLNF::getOptionsForComboBoxPopupMenu (ComboBox& comboBox, Label& label) 359 { 360 auto&& baseOptions = LookAndFeel_V4::getOptionsForComboBoxPopupMenu (comboBox, label); 361 362 #if JUCE_IOS 363 return baseOptions.withParentComponent (comboBox.getTopLevelComponent()); 364 #else 365 return baseOptions; 366 #endif 367 } 368 369 //============================================================== 370 Font ComboBoxLNF::getComboBoxFont (ComboBox& box) 371 { 372 return { juce::jmin (28.0f, (float) box.proportionOfHeight (0.48f)) }; 373 } 374 375 void ComboBoxLNF::drawComboBox (Graphics& g, int width, int height, bool, int, int, int, int, ComboBox& box) 376 { 377 auto cornerSize = 5.0f; 378 Rectangle<int> boxBounds (0, 0, width, height); 379 380 g.setColour (box.findColour (ComboBox::backgroundColourId)); 381 g.fillRoundedRectangle (boxBounds.toFloat(), cornerSize); 382 383 if (box.getName().isNotEmpty()) 384 { 385 g.setColour (Colours::white); 386 g.setFont (getComboBoxFont (box).boldened()); 387 auto nameBox = boxBounds.withWidth (boxBounds.proportionOfWidth (0.7f)); 388 g.drawFittedText (box.getName() + ": ", nameBox, Justification::right, 1); 389 } 390 } 391 392 void ComboBoxLNF::positionComboBoxText (ComboBox& box, Label& label) 393 { 394 auto b = box.getBounds().withPosition ({}); 395 396 if (box.getName().isNotEmpty()) 397 { 398 auto width = b.proportionOfWidth (0.3f); 399 auto x = b.proportionOfWidth (0.7f); 400 b = b.withX (x).withWidth (width); 401 } 402 403 label.setBounds (b); 404 label.setFont (getComboBoxFont (box).boldened()); 405 }