diff --git a/CrystalizerEQ/AXIOMDesignSystem.h b/CrystalizerEQ/AXIOMDesignSystem.h index 5bf6565..5a83208 100644 --- a/CrystalizerEQ/AXIOMDesignSystem.h +++ b/CrystalizerEQ/AXIOMDesignSystem.h @@ -5,6 +5,7 @@ #include #include "JuceLibraryCode/BinaryData.h" +#include "PluginEditor.h" namespace AXIOM { class DesignSystem : public juce::LookAndFeel_V4 { @@ -679,6 +680,185 @@ class PresetMenuButtonLookAndFeel : public juce::LookAndFeel_V4 { std::unique_ptr activeSvg; }; + + class lowBandButtonLookAndFeel : public juce::LookAndFeel_V4 { + public: + lowBandButtonLookAndFeel() + { + lowBypassIcon = juce::XmlDocument::parse(BinaryData::bypass_icon_svg); + lowCutIcon = juce::XmlDocument::parse(BinaryData::low_cut_icon_svg); + lowBellIcon = juce::XmlDocument::parse(BinaryData::bell_icon_svg); + lowShelfIcon = juce::XmlDocument::parse(BinaryData::low_shelf_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; + + auto iconBounds = juce::Rectangle(iconSize * hoverFactor, iconSize * hoverFactor) + .withCentre(bounds.getCentre()); + + float bypassOpacity = 1.0f; + + if (lowBypassIcon != nullptr && lowCutIcon != nullptr && lowBellIcon != nullptr && lowShelfIcon != nullptr) { + // WICHTIG: Erstelle für jeden Frame ein NEUES Drawable! + auto bypassIcon = juce::Drawable::createFromSVG(*lowBypassIcon); + auto cutIcon = juce::Drawable::createFromSVG(*lowCutIcon); + auto bellIcon = juce::Drawable::createFromSVG(*lowBellIcon); + auto shelfIcon = juce::Drawable::createFromSVG(*lowShelfIcon); + + if (bypassIcon != nullptr && cutIcon != nullptr && bellIcon != nullptr && shelfIcon != nullptr) + { + + juce::Colour colour; + if (!isEnabled) { + colour = isToggled ? Colours::ACCENTWEAKCOLOUR : Colours::SURFACEBYPASS; + } + else if (isHovered) { + colour = isToggled ? Colours::ACCENTHOVER : Colours::SURFACEHOVER; + } else { + colour = isToggled + ? Colours::ACCENTCOLOUR + : Colours::SURFACECOLOUR; + } + + + // Icon zeichnen + if (button.getName() == "LowBypass") { + bypassIcon->replaceColour(juce::Colours::black, colour); + bypassIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + else if (button.getName() == "LowCut") { + cutIcon->replaceColour(juce::Colours::black, colour); + cutIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + else if (button.getName() == "LowBell") { + bellIcon->replaceColour(juce::Colours::black, colour); + bellIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + else if (button.getName() == "LowShelf") { + shelfIcon->replaceColour(juce::Colours::black, colour); + shelfIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + + + + + + } + } + + } + + private: + std::unique_ptr lowBypassIcon; + std::unique_ptr lowCutIcon; + std::unique_ptr lowBellIcon; + std::unique_ptr lowShelfIcon; + }; + + class highBandButtonLookAndFeel : public juce::LookAndFeel_V4 { + public: + highBandButtonLookAndFeel() + { + highBypassIcon = juce::XmlDocument::parse(BinaryData::bypass_icon_svg); + highCutIcon = juce::XmlDocument::parse(BinaryData::high_cut_icon_svg); + highBellIcon = juce::XmlDocument::parse(BinaryData::bell_icon_svg); + highShelfIcon = juce::XmlDocument::parse(BinaryData::high_shelf_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; + + auto iconBounds = juce::Rectangle(iconSize * hoverFactor, iconSize * hoverFactor) + .withCentre(bounds.getCentre()); + + float bypassOpacity = 1.0f; + + if (highBypassIcon != nullptr && highCutIcon != nullptr && highBellIcon != nullptr && highShelfIcon != nullptr) { + // WICHTIG: Erstelle für jeden Frame ein NEUES Drawable! + auto bypassIcon = juce::Drawable::createFromSVG(*highBypassIcon); + auto cutIcon = juce::Drawable::createFromSVG(*highCutIcon); + auto bellIcon = juce::Drawable::createFromSVG(*highBellIcon); + auto shelfIcon = juce::Drawable::createFromSVG(*highShelfIcon); + + if (bypassIcon != nullptr && cutIcon != nullptr && bellIcon != nullptr && shelfIcon != nullptr) + { + + juce::Colour colour; + if (!isEnabled) { + colour = isToggled ? Colours::ACCENTWEAKCOLOUR : Colours::SURFACEBYPASS; + } + else if (isHovered) { + colour = isToggled ? Colours::ACCENTHOVER : Colours::SURFACEHOVER; + } else { + colour = isToggled + ? Colours::ACCENTCOLOUR + : Colours::SURFACECOLOUR; + } + + + // Icon zeichnen + if (button.getName() == "HighBypass") { + bypassIcon->replaceColour(juce::Colours::black, colour); + bypassIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + else if (button.getName() == "HighCut") { + cutIcon->replaceColour(juce::Colours::black, colour); + cutIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + else if (button.getName() == "HighBell") { + bellIcon->replaceColour(juce::Colours::black, colour); + bellIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + else if (button.getName() == "HighShelf") { + shelfIcon->replaceColour(juce::Colours::black, colour); + shelfIcon->drawWithin(g, iconBounds, + juce::RectanglePlacement::centred, bypassOpacity); + } + } + } + + } + + private: + std::unique_ptr highBypassIcon; + std::unique_ptr highCutIcon; + std::unique_ptr highBellIcon; + std::unique_ptr highShelfIcon; + }; + + struct ButtonStyles { }; diff --git a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj index 719172e..eac76fd 100644 --- a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj +++ b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj @@ -164,6 +164,11 @@ + + + + + diff --git a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters index 30d5fd8..6b9dbc6 100644 --- a/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters +++ b/CrystalizerEQ/Builds/VisualStudio2022/CrystalizerEQ_SharedCode.vcxproj.filters @@ -74,6 +74,21 @@ CrystalizerEQ\Resources\Fonts + + CrystalizerEQ\Resources\Icons + + + CrystalizerEQ\Resources\Icons + + + CrystalizerEQ\Resources\Icons + + + CrystalizerEQ\Resources\Icons + + + CrystalizerEQ\Resources\Icons + CrystalizerEQ\Resources\Icons diff --git a/CrystalizerEQ/CrystalizerEQ.jucer b/CrystalizerEQ/CrystalizerEQ.jucer index 5e0294f..901c814 100644 --- a/CrystalizerEQ/CrystalizerEQ.jucer +++ b/CrystalizerEQ/CrystalizerEQ.jucer @@ -21,6 +21,15 @@ file="Resources/Fonts/Roboto-Regular.ttf"/> + + + + + \n" +"\n" +" \n" +""; + +const char* bell_icon_svg = (const char*) temp_binary_data_7; + +//================== high_cut_icon.svg ================== +static const unsigned char temp_binary_data_8[] = +"\n" +"\n" +" \n" +""; + +const char* high_cut_icon_svg = (const char*) temp_binary_data_8; + +//================== high_shelf_icon.svg ================== +static const unsigned char temp_binary_data_9[] = +"\n" +"\n" +" \n" +""; + +const char* high_shelf_icon_svg = (const char*) temp_binary_data_9; + +//================== low_cut_icon.svg ================== +static const unsigned char temp_binary_data_10[] = +"\n" +"\n" +" \n" +""; + +const char* low_cut_icon_svg = (const char*) temp_binary_data_10; + +//================== low_shelf_icon.svg ================== +static const unsigned char temp_binary_data_11[] = +"\n" +"\n" +" \n" +""; + +const char* low_shelf_icon_svg = (const char*) temp_binary_data_11; + +//================== preset_menu_icon.svg ================== +static const unsigned char temp_binary_data_12[] = ""; -const char* preset_menu_icon_svg = (const char*) temp_binary_data_7; +const char* preset_menu_icon_svg = (const char*) temp_binary_data_12; //================== crystalize_button_active_icon.svg ================== -static const unsigned char temp_binary_data_8[] = +static const unsigned char temp_binary_data_13[] = "\n" "\n" " \n" @@ -11463,10 +11511,10 @@ static const unsigned char temp_binary_data_8[] = " \n" ""; -const char* crystalize_button_active_icon_svg = (const char*) temp_binary_data_8; +const char* crystalize_button_active_icon_svg = (const char*) temp_binary_data_13; //================== crystalize_button_passive_icon.svg ================== -static const unsigned char temp_binary_data_9[] = +static const unsigned char temp_binary_data_14[] = "\n" "\n" " \n" @@ -11507,17 +11555,17 @@ static const unsigned char temp_binary_data_9[] = " \n" ""; -const char* crystalize_button_passive_icon_svg = (const char*) temp_binary_data_9; +const char* crystalize_button_passive_icon_svg = (const char*) temp_binary_data_14; //================== bypass_icon.svg ================== -static const unsigned char temp_binary_data_10[] = +static const unsigned char temp_binary_data_15[] = "\n" "\n" " \n" " \n" ""; -const char* bypass_icon_svg = (const char*) temp_binary_data_10; +const char* bypass_icon_svg = (const char*) temp_binary_data_15; const char* getNamedResource (const char* resourceNameUTF8, int& numBytes); @@ -11538,6 +11586,11 @@ 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 0x1024555a: numBytes = 397; return bell_icon_svg; + case 0x46c5a278: numBytes = 316; return high_cut_icon_svg; + case 0x0133c050: numBytes = 420; return high_shelf_icon_svg; + case 0x31532e06: numBytes = 317; return low_cut_icon_svg; + case 0x7e8ca05e: numBytes = 410; return low_shelf_icon_svg; case 0x4df4bf1e: numBytes = 350; return preset_menu_icon_svg; case 0xd842aaab: numBytes = 4212; return crystalize_button_active_icon_svg; case 0x885a7642: numBytes = 4141; return crystalize_button_passive_icon_svg; @@ -11558,6 +11611,11 @@ const char* namedResourceList[] = "OrbitronBold_ttf", "OrbitronRegular_ttf", "RobotoRegular_ttf", + "bell_icon_svg", + "high_cut_icon_svg", + "high_shelf_icon_svg", + "low_cut_icon_svg", + "low_shelf_icon_svg", "preset_menu_icon_svg", "crystalize_button_active_icon_svg", "crystalize_button_passive_icon_svg", @@ -11573,6 +11631,11 @@ const char* originalFilenames[] = "Orbitron-Bold.ttf", "Orbitron-Regular.ttf", "Roboto-Regular.ttf", + "bell_icon.svg", + "high_cut_icon.svg", + "high_shelf_icon.svg", + "low_cut_icon.svg", + "low_shelf_icon.svg", "preset_menu_icon.svg", "crystalize_button_active_icon.svg", "crystalize_button_passive_icon.svg", diff --git a/CrystalizerEQ/JuceLibraryCode/BinaryData.h b/CrystalizerEQ/JuceLibraryCode/BinaryData.h index 2fea0aa..0cd2b1a 100644 --- a/CrystalizerEQ/JuceLibraryCode/BinaryData.h +++ b/CrystalizerEQ/JuceLibraryCode/BinaryData.h @@ -29,6 +29,21 @@ namespace BinaryData extern const char* RobotoRegular_ttf; const int RobotoRegular_ttfSize = 146004; + extern const char* bell_icon_svg; + const int bell_icon_svgSize = 397; + + extern const char* high_cut_icon_svg; + const int high_cut_icon_svgSize = 316; + + extern const char* high_shelf_icon_svg; + const int high_shelf_icon_svgSize = 420; + + extern const char* low_cut_icon_svg; + const int low_cut_icon_svgSize = 317; + + extern const char* low_shelf_icon_svg; + const int low_shelf_icon_svgSize = 410; + extern const char* preset_menu_icon_svg; const int preset_menu_icon_svgSize = 350; @@ -42,7 +57,7 @@ namespace BinaryData const int bypass_icon_svgSize = 406; // Number of elements in the namedResourceList and originalFileNames arrays. - const int namedResourceListSize = 11; + const int namedResourceListSize = 16; // 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 151b5e8..10f2d43 100644 --- a/CrystalizerEQ/PluginEditor.cpp +++ b/CrystalizerEQ/PluginEditor.cpp @@ -178,6 +178,8 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() { bypassButtonLookAndFeel = std::make_unique(); svgToggleButtonLookAndFeel = std::make_unique(); presetMenuButtonLookAndFeel = std::make_unique(); + lowBandButtonLookAndFeel = std::make_unique(); + highBandButtonLookAndFeel = std::make_unique(); peak1BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); peak2BypassButton.setLookAndFeel(bypassButtonLookAndFeel.get()); @@ -190,6 +192,17 @@ void CrystalizerEQAudioProcessorEditor::setupToggleButtons() { lowShelf.setToggleState(true, juce::dontSendNotification); highShelf.setToggleState(true, juce::dontSendNotification); + + lowBypass.setLookAndFeel(lowBandButtonLookAndFeel.get()); + lowCut.setLookAndFeel(lowBandButtonLookAndFeel.get()); + lowBell.setLookAndFeel(lowBandButtonLookAndFeel.get()); + lowShelf.setLookAndFeel(lowBandButtonLookAndFeel.get()); + + highBypass.setLookAndFeel(highBandButtonLookAndFeel.get()); + highCut.setLookAndFeel(highBandButtonLookAndFeel.get()); + highBell.setLookAndFeel(highBandButtonLookAndFeel.get()); + highShelf.setLookAndFeel(highBandButtonLookAndFeel.get()); + for (auto* b : {&peak1BypassButton, &peak2BypassButton, &peak3BypassButton, &crystalizeButton, &masterBypassButton}) { b->onClick = [this, b]() { juce::String paramID; // nimm juce::String statt std::string @@ -561,6 +574,9 @@ void CrystalizerEQAudioProcessorEditor::handleLowBandModes() { param->endChangeGesture(); } } + else if (lowBandBools[i]) { + lowBandModeButtons[i]->setToggleState(true, juce::dontSendNotification); + } }; } } @@ -593,11 +609,14 @@ void CrystalizerEQAudioProcessorEditor::handleHighBandModes() { param->endChangeGesture(); } } + else if (highBandBools[i]) { + highBandModeButtons[i]->setToggleState(true, juce::dontSendNotification); + } }; } } //endregion handleHighBandModes - + //region setupEventListeners void CrystalizerEQAudioProcessorEditor::setupEventListeners() { presetMenuButton.onClick = [this]() { @@ -743,6 +762,13 @@ CrystalizerEQAudioProcessorEditor::~CrystalizerEQAudioProcessorEditor() { masterBypassButton.setLookAndFeel(nullptr); crystalizeButton.setLookAndFeel(nullptr); presetMenuButton.setLookAndFeel(nullptr); + + for (auto* b : lowBandModeButtons) { + b->setLookAndFeel(nullptr); + } + for (auto* b : highBandModeButtons) { + b->setLookAndFeel(nullptr); + } }; //region paintAnalyzer @@ -763,6 +789,28 @@ void CrystalizerEQAudioProcessorEditor::paintAnalyzer(juce::Graphics &g) { } //endregion paintAnalyzer +void CrystalizerEQAudioProcessorEditor::paintModeBoxBorders(juce::Graphics &g) { + for (auto* b : lowBandModeButtons) { + if (b->getToggleState()) { + auto r = getLocalArea(&lowBandModeBox, b->getBounds()); + int side = juce::jmin(r.getWidth(), r.getHeight()); + r = r.withSizeKeepingCentre(side, side); + + auto rf = r.toFloat(); + auto rfFilled = rf.toNearestInt().toFloat(); + const float stroke = 1.0f; + auto rfStroke = rfFilled.reduced(stroke * 0.5f); + + g.setColour(juce::Colours::white.withAlpha(0.3f)); + g.fillRoundedRectangle(r.toFloat(), 5); + g.setColour(juce::Colours::white.withAlpha(0.9f)); + g.drawRoundedRectangle(rfStroke, 5, stroke); + } + } + + +} + //============================================================================== void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g) { @@ -815,6 +863,8 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g) g.fillRect(fA); paintBorderLines(g); + paintModeBoxBorders(g); + g.setColour(Colours::SURFACEBYPASS); const auto fB = getLocalArea(&footerBar, footerBar.getLocalBounds()); g.fillRect(fB); @@ -883,6 +933,15 @@ void CrystalizerEQAudioProcessorEditor::paint (juce::Graphics& g) g.setColour(juce::Colours::cyan.withAlpha(0.9f)); g.drawRect(r, 1.0f); } + for (auto* c : lowBandModeBox.getChildren()) + { + auto r = getLocalArea(&lowBandModeBox, c->getBounds()); // lokal in presetArea + + g.setColour(juce::Colours::yellow.withAlpha(0.3f)); + g.fillRect(r); + g.setColour(juce::Colours::yellow.withAlpha(0.9f)); + g.drawRect(r, 1.0f); + } for (auto* c : lowMidFilterArea.getChildren()) { auto r = getLocalArea(&lowMidFilterArea, c->getBounds()); // lokal in presetArea @@ -1313,7 +1372,7 @@ void CrystalizerEQAudioProcessorEditor::setupLowBandLayout() { const auto modeBoxButtonColWidth = modeBoxAreaWidth / 4.0f; Layout::GridSpec lowBandModeBoxSpec{ - /* cols */ { Layout::fr(1), Layout::pxTrack(modeBoxButtonColWidth), Layout::pxTrack(modeBoxButtonColWidth), Layout::fr(1) }, + /* cols */ { Layout::fr(1), Layout::fr(1), Layout::fr(1), Layout::fr(1) }, /* rows */ { Layout::fr(1)}, /* colGap */ Spacing::SizeMode::XS, /* rowGap */ Spacing::SizeMode::XS, @@ -1543,7 +1602,7 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() { const auto modeBoxButtonColWidth = modeBoxAreaWidth / 4.0f; Layout::GridSpec highBandModeBoxSpec{ - /* cols */ { Layout::fr(1), Layout::pxTrack(modeBoxButtonColWidth), Layout::pxTrack(modeBoxButtonColWidth), Layout::fr(1) }, + /* cols */ { Layout::fr(1), Layout::fr(1), Layout::fr(1), Layout::fr(1) }, /* rows */ { Layout::fr(1)}, /* colGap */ Spacing::SizeMode::XS, /* rowGap */ Spacing::SizeMode::XS, @@ -1551,10 +1610,10 @@ void CrystalizerEQAudioProcessorEditor::setupHighBandLayout() { }; Layout::grid(modeBoxBounds, highBandModeBoxSpec, { - Layout::area(highBypass, 1, 1, 2, 2), - Layout::area(highCut, 1, 2, 2, 3), - Layout::area(highBell, 1, 3, 2, 4), - Layout::area(highShelf, 1, 4, 2, 5) + Layout::area(highBypass, 1, 4, 2, 5), + Layout::area(highCut, 1, 3, 2, 4), + Layout::area(highBell, 1, 2, 2, 3), + Layout::area(highShelf, 1, 1, 2, 2) }); } //endregion setupHighBandLayout diff --git a/CrystalizerEQ/PluginEditor.h b/CrystalizerEQ/PluginEditor.h index d1f5327..9c77321 100644 --- a/CrystalizerEQ/PluginEditor.h +++ b/CrystalizerEQ/PluginEditor.h @@ -134,6 +134,9 @@ public: ~CrystalizerEQAudioProcessorEditor() override; void paintAnalyzer(juce::Graphics &g); + + void paintModeBoxBorders(juce::Graphics &g); + void paintBorderLines(juce::Graphics& g); //=================================================================== @@ -264,6 +267,10 @@ private: std::unique_ptr globalLookAndFeel; std::unique_ptr bypassButtonLookAndFeel; std::unique_ptr presetMenuButtonLookAndFeel; + std::unique_ptr lowBandButtonLookAndFeel; + std::unique_ptr highBandButtonLookAndFeel; + + std::unique_ptr svgToggleButtonLookAndFeel;