diff --git a/CrystalizerEQ/AXIOMDesignSystem.h b/CrystalizerEQ/AXIOMDesignSystem.h index e75b68e..2fcc92f 100644 --- a/CrystalizerEQ/AXIOMDesignSystem.h +++ b/CrystalizerEQ/AXIOMDesignSystem.h @@ -534,12 +534,16 @@ namespace AXIOM { svgXml = juce::XmlDocument::parse(BinaryData::bypass_icon_svg); } + + void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override { + bool isHovered = button.isMouseOverOrDragging(); bool isToggled = button.getToggleState(); + bool isEnabled = button.isEnabled(); auto bounds = button.getLocalBounds().toFloat(); auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 0.5f; auto hoverFactor = isHovered ? 1.05f : 1.0f; @@ -547,15 +551,19 @@ namespace AXIOM { auto iconBounds = juce::Rectangle(iconSize * hoverFactor, iconSize * hoverFactor) .withCentre(bounds.getCentre()); + float bypassOpacity = 1.0f; if (svgXml != nullptr) { // WICHTIG: Erstelle für jeden Frame ein NEUES Drawable! auto icon = juce::Drawable::createFromSVG(*svgXml); - //TODO ON MASTERBYPASS SET TO HOVER COLOUR if (icon != nullptr) { + juce::Colour colour; - if (isHovered) { + if (!isEnabled) { + colour = isToggled ? Colours::SURFACEBYPASS : Colours::ACCENTWEAKCOLOUR; + } + else if (isHovered) { colour = isToggled ? Colours::SURFACEHOVER : Colours::ACCENTHOVER; } else { colour = isToggled @@ -563,10 +571,11 @@ namespace AXIOM { : Colours::ACCENTCOLOUR; } + // Icon zeichnen icon->replaceColour(juce::Colours::black, colour); icon->drawWithin(g, iconBounds, - juce::RectanglePlacement::centred, 1.0f); + juce::RectanglePlacement::centred, bypassOpacity); } } } @@ -575,6 +584,49 @@ namespace AXIOM { std::unique_ptr svgXml; }; + class SvgToggleButtonLookAndFeel : public juce::LookAndFeel_V4 { + public: + SvgToggleButtonLookAndFeel() { + passiveSvg = juce::XmlDocument::parse(BinaryData::crystalize_button_passive_icon_svg); + activeSvg = juce::XmlDocument::parse(BinaryData::crystalize_button_active_icon_svg); + }; + float passiveIconOpacity = 1.0f; + float activeIconOpacity = 0.0f; + + + void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button, + bool shouldDrawButtonAsHighlighted, + bool shouldDrawButtonAsDown) override { + + + const bool isHovered = button.isMouseOverOrDragging(); + const bool isToggled = button.getToggleState(); + auto bounds = button.getLocalBounds().toFloat(); + auto iconSize = juce::jmin(bounds.getWidth(), bounds.getHeight()) * 0.5f; + auto hoverFactor = isHovered ? 1.05f : 1.0f; + + auto iconBounds = juce::Rectangle(iconSize * hoverFactor, iconSize * hoverFactor) + .withCentre(bounds.getCentre()); + + auto passiveIcon = juce::Drawable::createFromSVG(*passiveSvg); + auto activeIcon = juce::Drawable::createFromSVG(*activeSvg); + + if (passiveIcon != nullptr && activeIcon != nullptr) { + + activeIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, activeIconOpacity); + passiveIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, passiveIconOpacity); + + } + + } + + private: + std::unique_ptr passiveSvg; + std::unique_ptr activeSvg; + + }; struct ButtonStyles { }; diff --git a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj index 9defeaf..719172e 100644 --- a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj +++ b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj @@ -164,6 +164,9 @@ + + + diff --git a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters index 5f1420c..30d5fd8 100644 --- a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters +++ b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters @@ -74,6 +74,15 @@ CrystalizerEQ\Resources\Fonts + + CrystalizerEQ\Resources\Icons + + + CrystalizerEQ\Resources\Icons + + + CrystalizerEQ\Resources\Icons + CrystalizerEQ\Resources\Icons diff --git a/CrystalizerEQ/CrystalizerEQ.jucer b/CrystalizerEQ/CrystalizerEQ.jucer index 1531822..5e0294f 100644 --- a/CrystalizerEQ/CrystalizerEQ.jucer +++ b/CrystalizerEQ/CrystalizerEQ.jucer @@ -21,6 +21,12 @@ file="Resources/Fonts/Roboto-Regular.ttf"/> + + + diff --git a/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp b/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp index c6baa55..006e9fc 100644 --- a/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp +++ b/CrystalizerEQ/JuceLibraryCode/BinaryData.cpp @@ -11413,15 +11413,74 @@ static const unsigned char temp_binary_data_6[] = const char* RobotoRegular_ttf = (const char*) temp_binary_data_6; -//================== bypass_icon.svg ================== +//================== preset_menu_icon.svg ================== static const unsigned char temp_binary_data_7[] = +""; + +const char* preset_menu_icon_svg = (const char*) temp_binary_data_7; + +//================== crystalize_button_active_icon.svg ================== +static const unsigned char temp_binary_data_8[] = +"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +""; + +const char* crystalize_button_active_icon_svg = (const char*) temp_binary_data_8; + +//================== crystalize_button_passive_icon.svg ================== +static const unsigned char temp_binary_data_9[] = +"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +""; + +const char* crystalize_button_passive_icon_svg = (const char*) temp_binary_data_9; + +//================== bypass_icon.svg ================== +static const unsigned char temp_binary_data_10[] = "\n" "\n" " \n" " \n" ""; -const char* bypass_icon_svg = (const char*) temp_binary_data_7; +const char* bypass_icon_svg = (const char*) temp_binary_data_10; const char* getNamedResource (const char* resourceNameUTF8, int& numBytes); @@ -11442,6 +11501,9 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) case 0xcbceafb3: numBytes = 24668; return OrbitronBold_ttf; case 0x9c8232dc: numBytes = 24716; return OrbitronRegular_ttf; case 0x93fe9a1e: numBytes = 146004; return RobotoRegular_ttf; + case 0x4df4bf1e: numBytes = 337; return preset_menu_icon_svg; + case 0xd842aaab: numBytes = 2478; return crystalize_button_active_icon_svg; + case 0x885a7642: numBytes = 2508; return crystalize_button_passive_icon_svg; case 0xe49e0f15: numBytes = 406; return bypass_icon_svg; default: break; } @@ -11459,6 +11521,9 @@ const char* namedResourceList[] = "OrbitronBold_ttf", "OrbitronRegular_ttf", "RobotoRegular_ttf", + "preset_menu_icon_svg", + "crystalize_button_active_icon_svg", + "crystalize_button_passive_icon_svg", "bypass_icon_svg" }; @@ -11471,6 +11536,9 @@ const char* originalFilenames[] = "Orbitron-Bold.ttf", "Orbitron-Regular.ttf", "Roboto-Regular.ttf", + "preset_menu_icon.svg", + "crystalize_button_active_icon.svg", + "crystalize_button_passive_icon.svg", "bypass_icon.svg" }; diff --git a/CrystalizerEQ/JuceLibraryCode/BinaryData.h b/CrystalizerEQ/JuceLibraryCode/BinaryData.h index 62df199..641ff49 100644 --- a/CrystalizerEQ/JuceLibraryCode/BinaryData.h +++ b/CrystalizerEQ/JuceLibraryCode/BinaryData.h @@ -29,11 +29,20 @@ namespace BinaryData extern const char* RobotoRegular_ttf; const int RobotoRegular_ttfSize = 146004; + extern const char* preset_menu_icon_svg; + const int preset_menu_icon_svgSize = 337; + + extern const char* crystalize_button_active_icon_svg; + const int crystalize_button_active_icon_svgSize = 2478; + + extern const char* crystalize_button_passive_icon_svg; + const int crystalize_button_passive_icon_svgSize = 2508; + extern const char* bypass_icon_svg; const int bypass_icon_svgSize = 406; // Number of elements in the namedResourceList and originalFileNames arrays. - const int namedResourceListSize = 8; + const int namedResourceListSize = 11; // Points to the start of a list of resource names. extern const char* namedResourceList[]; diff --git a/CrystalizerEQ/PluginEditor.cpp b/CrystalizerEQ/PluginEditor.cpp index f9eedd2..29a051b 100644 --- a/CrystalizerEQ/PluginEditor.cpp +++ b/CrystalizerEQ/PluginEditor.cpp @@ -173,7 +173,6 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() { testNoiseButton.setButtonText("Test Noise"); crystalizeButton.setName("CrystalizeButton"); - crystalizeButton.setButtonText("Crystalize"); resetButton.setName("ResetButton"); resetButton.setButtonText("Reset"); @@ -191,6 +190,16 @@ void CrystalizerEQAudioProcessorEditor::setupDisplayNames() { //region setupToggleButtons void CrystalizerEQAudioProcessorEditor::setupToggleButtons() { + bypassButtonLookAndFeel = std::make_unique(); + svgToggleButtonLookAndFeel = std::make_unique(); + + peak1BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); + peak2BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); + peak3BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); + masterBypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); + + crystalizeButton.setLookAndFeel(svgToggleButtonLookAndFeel.get()); + for (auto* b : {&peak1BypassButton, &peak2BypassButton, &peak3BypassButton, &crystalizeButton, &masterBypassButton}) { b->onClick = [this, b]() { juce::String paramID; // nimm juce::String statt std::string @@ -215,10 +224,6 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() { mode = "Master"; } - - b->setColour(juce::ToggleButton::textColourId, juce::Colours::black); - b->setColour(juce::ToggleButton::tickColourId, juce::Colours::black); - if (auto* param = audioProcessor.apvts.getParameter(paramID)) { const bool isToggled = b->getToggleState(); @@ -237,14 +242,15 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() { else if (mode == "High-Mid") disableHighMidBand(target); else if (mode == "Master") disableEverything(target); } + + if (b == &crystalizeButton) { + isAnimatingCrystalize = true; + isFadingToActive = (svgToggleButtonLookAndFeel->activeIconOpacity < 1.0f); + } + }; } - bypassButtonLookAndFeel = std::make_unique(); - peak1BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); - peak2BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); - peak3BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); - masterBypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); } //endregion setupToggleButtons @@ -329,6 +335,27 @@ void CrystalizerEQAudioProcessorEditor::disableEverything(const float target) { crystalizeButton.setEnabled(isToggled); lowBandModeBox.setEnabled(isToggled); highBandModeBox.setEnabled(isToggled); + + if (!isToggled) { + if (svgToggleButtonLookAndFeel->activeIconOpacity == 1.0f) { + svgToggleButtonLookAndFeel->activeIconOpacity = 0.7f; + crystalizeButton.repaint(); + }else if (svgToggleButtonLookAndFeel->passiveIconOpacity == 1.0f) { + svgToggleButtonLookAndFeel->passiveIconOpacity = 0.7f; + crystalizeButton.repaint(); + } + } else { + if (svgToggleButtonLookAndFeel->activeIconOpacity == 0.7f) { + svgToggleButtonLookAndFeel->activeIconOpacity = 1.0f; + crystalizeButton.repaint(); + }else if (svgToggleButtonLookAndFeel->passiveIconOpacity == 0.7f) { + svgToggleButtonLookAndFeel->passiveIconOpacity = 1.0f; + crystalizeButton.repaint(); + } + } + + + } //endregion disableEverything @@ -560,6 +587,10 @@ void CrystalizerEQAudioProcessorEditor::setupEventListeners() { resetAllCheckboxes(); presetBox.setSelectedId(1, juce::dontSendNotification); }; + + + + } //endregion setupEventListeners @@ -636,6 +667,7 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() { peak2BypassButton.setLookAndFeel(nullptr); peak3BypassButton.setLookAndFeel(nullptr); masterBypassButton.setLookAndFeel(nullptr); + crystalizeButton.setLookAndFeel(nullptr); }; //region paintAnalyzer @@ -814,8 +846,21 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g) g.fillRoundedRectangle(pAX, pAY - 10.f, pAWidth, pAHeight + 10.f, 10.0f); paintBorderLines(g); - g.setColour(Colours::BACKGROUNDBYPASS); const auto fA = getLocalArea(&filterArea, filterArea.getLocalBounds()); + const int fABorderWidth = 3; + //TODO: BORDER OF FILTERAREA + auto fABorder = fA.withSizeKeepingCentre(fA.getWidth() + fABorderWidth, fA.getHeight() + fABorderWidth); + fABorder = fABorder.withX(fABorder.getX() + fABorderWidth).withY(fABorder.getY() + fABorderWidth); + + + g.setColour(Colours::SURFACECOLOUR); + g.fillRect(fABorder); + + fABorder = fABorder.withX(fABorder.getX() - 2* fABorderWidth).withY(fABorder.getY() - 2 * fABorderWidth); + //g.setColour(Colours::FOREGROUNDBYPASS); + g.fillRect(fABorder); + + g.setColour(Colours::BACKGROUNDBYPASS); g.fillRect(fA); paintBorderLines(g); @@ -823,6 +868,7 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g) //region paintBorderLines void CrystalizerEQAudioProcessorEditor::paintBorderLines(juce::Graphics &g) { + //TODO: BRODER LINES DONT ALIGN WITH MARGIN FROM ANALYZERAREA AND FILTERAREA g.setColour(DesignSystem::Colours::BACKGROUNDCOLOUR); auto prevRight = (float) lowFilterArea.getRight(); @@ -885,6 +931,40 @@ void CrystalizerEQAudioProcessorEditor::setKnobVisibility() { } //endregion setKnobVisibility +//region animateCrystalizeButton +void CrystalizerEQAudioProcessorEditor::animateCrystalizeButton() { + if (isAnimatingCrystalize) { + + const float step = 0.1f; + if (isFadingToActive) + { + svgToggleButtonLookAndFeel->activeIconOpacity += step; + svgToggleButtonLookAndFeel->passiveIconOpacity -= step; + + if (svgToggleButtonLookAndFeel->activeIconOpacity >= 1.0f) + { + svgToggleButtonLookAndFeel->activeIconOpacity = 1.0f; + svgToggleButtonLookAndFeel->passiveIconOpacity = 0.0f; + isAnimatingCrystalize = false; + } + } + else + { + svgToggleButtonLookAndFeel->activeIconOpacity -= step; + svgToggleButtonLookAndFeel->passiveIconOpacity += step; + + if (svgToggleButtonLookAndFeel->passiveIconOpacity >= 1.0f) + { + svgToggleButtonLookAndFeel->activeIconOpacity = 0.0f; + svgToggleButtonLookAndFeel->passiveIconOpacity = 1.0f; + isAnimatingCrystalize = false; + } + } + crystalizeButton.repaint(); + } +} +//endregion animateCrystalizeButton + //region timerCallback void CrystalizerEQAudioProcessorEditor::timerCallback() { @@ -892,6 +972,7 @@ void CrystalizerEQAudioProcessorEditor::timerCallback() spectrumAnalyzer.processSamples(); repaint(analyzerRect); resized(); + animateCrystalizeButton(); } //endregion timerCallback @@ -1022,6 +1103,7 @@ void CrystalizerEQAudioProcessorEditor::setupBody() { const auto bodyHeight = static_cast(bounds.getHeight()); const auto bodyWidth = static_cast(bounds.getWidth()); const auto bodyColWidth = bodyWidth / 5.0f; + const int margin = 15; Layout::GridSpec bodySpec{ /* cols */ { Layout::fr(1), Layout::pxTrack(bodyColWidth), Layout::pxTrack(bodyColWidth), Layout::pxTrack(bodyColWidth), Layout::fr(1) }, @@ -1031,9 +1113,12 @@ void CrystalizerEQAudioProcessorEditor::setupBody() { /* pad */ Layout::padding(Spacing::SizeMode::XS) }; Layout::grid(bounds, bodySpec, { - Layout::area(analyzerArea, 1, 2, 2, 5), + Layout::area(analyzerArea, 1, 2, 2, 5) + .withMargin(juce::GridItem::Margin(margin, 0, margin, 0)), Layout::area(crystalizeButton, 1, 5, 2, 6), Layout::area(filterArea, 2, 1, 3, 6) + .withMargin(juce::GridItem::Margin(0, margin, 0, margin)) + }); const auto analyzerAreaBounds = analyzerArea.getLocalBounds(); diff --git a/CrystalizerEQ/PluginEditor.h b/CrystalizerEQ/PluginEditor.h index fa2bc78..09b5265 100644 --- a/CrystalizerEQ/PluginEditor.h +++ b/CrystalizerEQ/PluginEditor.h @@ -144,13 +144,6 @@ public: void resized() override; void resetAllCheckboxes(); -private: - - // This reference is provided as a quick way for your editor to - // access the processor object that created it. - CrystalizerEQAudioProcessor& audioProcessor; - - using SliderAttach = juce::AudioProcessorValueTreeState::SliderAttachment; using ButtonAttach = juce::AudioProcessorValueTreeState::ButtonAttachment; @@ -210,7 +203,20 @@ private: juce::TextEditor presetNameInput; + +private: + + // This reference is provided as a quick way for your editor to + // access the processor object that created it. + CrystalizerEQAudioProcessor& audioProcessor; + + + + void setKnobVisibility(); + + void animateCrystalizeButton(); + void setupModeBoxes(); void setupSliders(); @@ -247,6 +253,7 @@ private: std::unique_ptr freqQLookAndFeel; std::unique_ptr globalLookAndFeel; std::unique_ptr bypassButtonLookAndFeel; + std::unique_ptr svgToggleButtonLookAndFeel; //SPECRTRUM ANALYZER @@ -299,7 +306,8 @@ private: const float slopeMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Slope); const float globalMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Global); - + bool isAnimatingCrystalize = false; + bool isFadingToActive = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CrystalizerEQAudioProcessorEditor) };