Tabbed editor
This commit is contained in:
@@ -367,6 +367,138 @@ private:
|
||||
float morphDisplayValue { 0.0f };
|
||||
};
|
||||
|
||||
class WaveBrowserComponent : public juce::Component
|
||||
{
|
||||
public:
|
||||
explicit WaveBrowserComponent(DummyWavetableSynthAudioProcessor& processorRef)
|
||||
: audioProcessor(processorRef)
|
||||
{
|
||||
}
|
||||
|
||||
void setOnWaveSelected(std::function<void(int)> handler) { onWaveSelected = std::move(handler); }
|
||||
|
||||
void paint(juce::Graphics& g) override
|
||||
{
|
||||
g.fillAll(juce::Colours::black.withAlpha(0.35f));
|
||||
g.setColour(juce::Colours::grey);
|
||||
g.drawRect(getLocalBounds());
|
||||
|
||||
auto grid = getLocalBounds().reduced(8);
|
||||
const int cellW = juce::jmax(1, grid.getWidth() / kBrowserColumns);
|
||||
const int cellH = juce::jmax(1, grid.getHeight() / kBrowserRows);
|
||||
|
||||
browserCells.clear();
|
||||
browserCells.reserve(kBrowserColumns * kBrowserRows);
|
||||
|
||||
const int waveCount = audioProcessor.getWaveTableCount();
|
||||
|
||||
for (int r = 0; r < kBrowserRows; ++r)
|
||||
for (int c = 0; c < kBrowserColumns; ++c)
|
||||
{
|
||||
const int idx = r * kBrowserColumns + c;
|
||||
auto cell = juce::Rectangle<int>(grid.getX() + c * cellW,
|
||||
grid.getY() + r * cellH,
|
||||
cellW, cellH);
|
||||
browserCells.push_back(cell);
|
||||
|
||||
g.setColour(juce::Colours::darkgrey);
|
||||
g.drawRect(cell);
|
||||
|
||||
if (idx < waveCount)
|
||||
{
|
||||
if (const auto* tbl = audioProcessor.getPreviewTablePtr(idx))
|
||||
{
|
||||
g.setColour(juce::Colours::lime);
|
||||
juce::Path p;
|
||||
p.startNewSubPath((float) cell.getX(), (float) cell.getCentreY());
|
||||
const int n = (int) tbl->size();
|
||||
for (int i = 0; i < n; i += 32)
|
||||
{
|
||||
const float x = juce::jmap((float) i, 0.0f, (float) n,
|
||||
(float) cell.getX(), (float) cell.getRight());
|
||||
const float y = juce::jmap((*tbl)[(size_t) i], -1.0f, 1.0f,
|
||||
(float) cell.getBottom(), (float) cell.getY());
|
||||
p.lineTo(x, y);
|
||||
}
|
||||
g.strokePath(p, juce::PathStrokeType(1.0f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColour(juce::Colours::darkgrey);
|
||||
g.drawLine((float) cell.getX(), (float) cell.getY(), (float) cell.getRight(), (float) cell.getBottom(), 0.5f);
|
||||
g.drawLine((float) cell.getRight(), (float) cell.getY(), (float) cell.getX(), (float) cell.getBottom(), 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDown(const juce::MouseEvent& e) override
|
||||
{
|
||||
const int waveCount = audioProcessor.getWaveTableCount();
|
||||
for (size_t i = 0; i < browserCells.size(); ++i)
|
||||
{
|
||||
if (browserCells[i].contains(e.getPosition()))
|
||||
{
|
||||
if ((int) i >= waveCount)
|
||||
return;
|
||||
|
||||
if (onWaveSelected != nullptr)
|
||||
onWaveSelected((int) i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void refresh() { repaint(); }
|
||||
|
||||
private:
|
||||
DummyWavetableSynthAudioProcessor& audioProcessor;
|
||||
std::vector<juce::Rectangle<int>> browserCells;
|
||||
std::function<void(int)> onWaveSelected;
|
||||
};
|
||||
|
||||
class EditorTabContent : public juce::Component
|
||||
{
|
||||
public:
|
||||
EditorTabContent(juce::Label& titleLabel, DrawWaveComponent& drawComponent)
|
||||
: label(titleLabel), draw(drawComponent)
|
||||
{
|
||||
addAndMakeVisible(label);
|
||||
addAndMakeVisible(draw);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds().reduced(12);
|
||||
auto labelArea = area.removeFromTop(20);
|
||||
label.setBounds(labelArea);
|
||||
area.removeFromTop(6);
|
||||
draw.setBounds(area);
|
||||
}
|
||||
|
||||
private:
|
||||
juce::Label& label;
|
||||
DrawWaveComponent& draw;
|
||||
};
|
||||
|
||||
class LibraryTabContent : public juce::Component
|
||||
{
|
||||
public:
|
||||
explicit LibraryTabContent(WaveBrowserComponent& browserComponent)
|
||||
: browser(browserComponent)
|
||||
{
|
||||
addAndMakeVisible(browser);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
browser.setBounds(getLocalBounds().reduced(8));
|
||||
}
|
||||
|
||||
private:
|
||||
WaveBrowserComponent& browser;
|
||||
};
|
||||
|
||||
class ExampleUIPanel : public juce::Component,
|
||||
private juce::Timer
|
||||
{
|
||||
@@ -435,25 +567,31 @@ public:
|
||||
{
|
||||
slotIndices[(size_t) activeSlot] = slotIndex;
|
||||
updateSlotThumbnails();
|
||||
waveBrowser.refresh();
|
||||
repaint();
|
||||
}
|
||||
};
|
||||
clearDraw.onClick = [this] { userDraw.clear(); };
|
||||
presetButton.onClick = [this] { showPresetMenu(); };
|
||||
|
||||
addAndMakeVisible(wavetableTabs);
|
||||
wavetableTabs.addTab("Editor", juce::Colours::transparentBlack, &editorTab, false);
|
||||
wavetableTabs.addTab("Library", juce::Colours::transparentBlack, &libraryTab, false);
|
||||
wavetableTabs.setColour(juce::TabbedComponent::outlineColourId, juce::Colours::darkgrey);
|
||||
|
||||
editorTab.addAndMakeVisible(lblDrawWave);
|
||||
editorTab.addAndMakeVisible(userDraw);
|
||||
lblDrawWave.setText("DRAW WAVE", juce::dontSendNotification);
|
||||
lblDrawWave.setColour(juce::Label::textColourId, juce::Colours::white);
|
||||
lblDrawWave.setJustificationType(juce::Justification::left);
|
||||
|
||||
addAndMakeVisible(wavetableTabs);
|
||||
wavetableTabs.addTab("Editor", juce::Colours::transparentBlack, &editorTabContent, false);
|
||||
wavetableTabs.addTab("Library", juce::Colours::transparentBlack, &libraryTabContent, false);
|
||||
wavetableTabs.setColour(juce::TabbedComponent::outlineColourId, juce::Colours::darkgrey);
|
||||
|
||||
waveBrowser.setOnWaveSelected([this](int index)
|
||||
{
|
||||
slotIndices[(size_t) activeSlot] = index;
|
||||
updateSlotThumbnails();
|
||||
repaint();
|
||||
});
|
||||
|
||||
for (auto* box : { &slotABox, &slotBBox, &slotCBox })
|
||||
libraryTab.addAndMakeVisible(*box);
|
||||
addAndMakeVisible(*box);
|
||||
|
||||
slotABox.setName("A");
|
||||
slotBBox.setName("B");
|
||||
@@ -524,56 +662,6 @@ public:
|
||||
const int leftWidth = getWidth() / 2 - 20;
|
||||
const int rightX = leftWidth + 30;
|
||||
|
||||
auto browser = juce::Rectangle<int>(10, blackTop + 8, leftWidth - 20, blackBottom - blackTop - 16);
|
||||
g.setColour(juce::Colours::grey);
|
||||
g.drawRect(browser);
|
||||
|
||||
const int cellW = browser.getWidth() / kBrowserColumns;
|
||||
const int cellH = browser.getHeight() / kBrowserRows;
|
||||
browserCells.clear();
|
||||
browserCells.reserve(kBrowserColumns * kBrowserRows);
|
||||
|
||||
const int waveCount = audioProcessor.getWaveTableCount();
|
||||
|
||||
for (int r = 0; r < kBrowserRows; ++r)
|
||||
for (int c = 0; c < kBrowserColumns; ++c)
|
||||
{
|
||||
const int idx = r * kBrowserColumns + c;
|
||||
auto cell = juce::Rectangle<int>(browser.getX() + c * cellW,
|
||||
browser.getY() + r * cellH,
|
||||
cellW, cellH);
|
||||
browserCells.push_back(cell);
|
||||
|
||||
g.setColour(juce::Colours::darkgrey);
|
||||
g.drawRect(cell);
|
||||
|
||||
if (idx < waveCount)
|
||||
{
|
||||
if (const auto* tbl = audioProcessor.getPreviewTablePtr(idx))
|
||||
{
|
||||
g.setColour(juce::Colours::lime);
|
||||
juce::Path p;
|
||||
p.startNewSubPath((float) cell.getX(), (float) cell.getCentreY());
|
||||
const int n = (int) tbl->size();
|
||||
for (int i = 0; i < n; i += 32)
|
||||
{
|
||||
const float x = juce::jmap((float) i, 0.0f, (float) n,
|
||||
(float) cell.getX(), (float) cell.getRight());
|
||||
const float y = juce::jmap((*tbl)[(size_t) i], -1.0f, 1.0f,
|
||||
(float) cell.getBottom(), (float) cell.getY());
|
||||
p.lineTo(x, y);
|
||||
}
|
||||
g.strokePath(p, juce::PathStrokeType(1.0f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColour(juce::Colours::darkgrey);
|
||||
g.drawLine((float) cell.getX(), (float) cell.getY(), (float) cell.getRight(), (float) cell.getBottom(), 0.5f);
|
||||
g.drawLine((float) cell.getRight(), (float) cell.getY(), (float) cell.getX(), (float) cell.getBottom(), 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
g.setColour(juce::Colours::darkred);
|
||||
g.fillRect(juce::Rectangle<int>(rightX, blackTop + 6, getWidth() - rightX - 30, 6));
|
||||
g.setColour(juce::Colours::white);
|
||||
@@ -591,7 +679,12 @@ public:
|
||||
const int leftWidth = getWidth() / 2 - 20;
|
||||
const int rightX = leftWidth + 30;
|
||||
|
||||
morphSlider.setBounds(rightX, blackTop + 16, getWidth() - rightX - 30, 18);
|
||||
const int boxH = 28;
|
||||
slotABox.setBounds(rightX, blackTop + 6, 130, boxH);
|
||||
slotBBox.setBounds(rightX + 140, blackTop + 6, 130, boxH);
|
||||
slotCBox.setBounds(rightX + 280, blackTop + 6, 130, boxH);
|
||||
|
||||
morphSlider.setBounds(rightX, blackTop + boxH + 10, getWidth() - rightX - 30, 18);
|
||||
morphLoopToggle.setBounds(rightX, morphSlider.getBottom() + 10, 120, 22);
|
||||
morphLoopMode.setBounds(morphLoopToggle.getRight() + 8, morphSlider.getBottom() + 6, 150, 26);
|
||||
|
||||
@@ -601,49 +694,14 @@ public:
|
||||
auto tabBounds = juce::Rectangle<int>(rightX, padTop, getWidth() - rightX - 16, blackBottom - padTop - 70);
|
||||
wavetableTabs.setBounds(tabBounds);
|
||||
|
||||
auto editorBounds = editorTab.getLocalBounds().reduced(12);
|
||||
auto labelBounds = editorBounds.removeFromTop(20);
|
||||
lblDrawWave.setBounds(labelBounds.getX(), labelBounds.getY(), 140, labelBounds.getHeight());
|
||||
editorBounds.removeFromTop(6);
|
||||
userDraw.setBounds(editorBounds);
|
||||
|
||||
auto libraryBounds = libraryTab.getLocalBounds().reduced(12);
|
||||
const int slotSpacing = 12;
|
||||
const int slotHeight = 32;
|
||||
int slotWidth = (libraryBounds.getWidth() - slotSpacing * 2) / 3;
|
||||
slotWidth = juce::jmax(90, slotWidth);
|
||||
const bool fitsHorizontally = (slotWidth * 3 + slotSpacing * 2) <= libraryBounds.getWidth();
|
||||
|
||||
if (fitsHorizontally)
|
||||
{
|
||||
const int slotY = libraryBounds.getY();
|
||||
int slotX = libraryBounds.getX();
|
||||
slotABox.setBounds(slotX, slotY, slotWidth, slotHeight);
|
||||
slotX += slotWidth + slotSpacing;
|
||||
slotBBox.setBounds(slotX, slotY, slotWidth, slotHeight);
|
||||
slotX += slotWidth + slotSpacing;
|
||||
slotCBox.setBounds(slotX, slotY, slotWidth, slotHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
int slotY = libraryBounds.getY();
|
||||
const int slotX = libraryBounds.getX();
|
||||
slotWidth = libraryBounds.getWidth();
|
||||
for (auto* box : { &slotABox, &slotBBox, &slotCBox })
|
||||
{
|
||||
box->setBounds(slotX, slotY, slotWidth, slotHeight);
|
||||
slotY += slotHeight + slotSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
const int buttonRowY = wavetableTabs.getBottom() + 8;
|
||||
addToBrowser.setBounds(wavetableTabs.getX() + 220, buttonRowY, 150, 28);
|
||||
const int buttonRowY = tabBounds.getBottom() + 8;
|
||||
addToBrowser.setBounds(tabBounds.getX() + 220, buttonRowY, 150, 28);
|
||||
clearDraw.setBounds(addToBrowser.getRight() + 12, buttonRowY, 150, 28);
|
||||
|
||||
const int togglesY = bottom.getY() - 36;
|
||||
reverbOn.setBounds(getWidth() - 280, togglesY, 120, 24);
|
||||
osc2Mute .setBounds(getWidth() - 140, togglesY, 140, 24);
|
||||
chorusOn.setBounds(wavetableTabs.getX(), buttonRowY, 90, 20);
|
||||
chorusOn.setBounds(tabBounds.getX(), buttonRowY, 90, 20);
|
||||
|
||||
const int masterW = 82;
|
||||
lblMaster.setBounds(getWidth() - masterW - 150, top.getY() + 4, 80, 16);
|
||||
@@ -688,11 +746,6 @@ public:
|
||||
place (10,1,lblRvWet, rvWet, "Rev Wet");
|
||||
}
|
||||
|
||||
void mouseDown(const juce::MouseEvent& e) override
|
||||
{
|
||||
handleBrowserClick(e.getPosition());
|
||||
}
|
||||
|
||||
private:
|
||||
juce::Rectangle<int> getTopPanelBounds() const { return { 0, 0, getWidth(), 80 }; }
|
||||
juce::Rectangle<int> getBottomPanelBounds() const
|
||||
@@ -718,24 +771,6 @@ private:
|
||||
label.setColour(juce::Label::textColourId, juce::Colours::black);
|
||||
}
|
||||
|
||||
void handleBrowserClick(juce::Point<int> pos)
|
||||
{
|
||||
const int waveCount = audioProcessor.getWaveTableCount();
|
||||
for (size_t i = 0; i < browserCells.size(); ++i)
|
||||
{
|
||||
if (browserCells[i].contains(pos))
|
||||
{
|
||||
if ((int) i >= waveCount)
|
||||
return;
|
||||
|
||||
slotIndices[(size_t) activeSlot] = (int) i;
|
||||
updateSlotThumbnails();
|
||||
repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setActiveSlot(int slot)
|
||||
{
|
||||
activeSlot = juce::jlimit(0, 2, slot);
|
||||
@@ -861,20 +896,20 @@ private:
|
||||
|
||||
juce::ToggleButton chorusOn, reverbOn, osc2Mute;
|
||||
|
||||
juce::TabbedComponent wavetableTabs { juce::TabbedButtonBar::TabsAtTop };
|
||||
juce::Component editorTab, libraryTab;
|
||||
|
||||
WaveThumbnail slotABox, slotBBox, slotCBox;
|
||||
DrawWaveComponent userDraw;
|
||||
juce::TextButton addToBrowser, clearDraw, presetButton;
|
||||
juce::Label lblDrawWave;
|
||||
WaveBrowserComponent waveBrowser { audioProcessor };
|
||||
EditorTabContent editorTabContent { lblDrawWave, userDraw };
|
||||
LibraryTabContent libraryTabContent { waveBrowser };
|
||||
juce::TabbedComponent wavetableTabs { juce::TabbedButtonBar::TabsAtTop };
|
||||
|
||||
juce::TextButton addToBrowser, clearDraw, presetButton;
|
||||
|
||||
juce::Label lblCutoff, lblAttack, lblDecay, lblSustain, lblRelease;
|
||||
juce::Label lblLfoRate, lblLfoDepth, lblFenvA, lblFenvD, lblFenvS, lblFenvR, lblFenvAmt;
|
||||
juce::Label lblChRate, lblChDepth, lblChDelay, lblChFb, lblChMix;
|
||||
juce::Label lblRvRoom, lblRvDamp, lblRvWidth, lblRvWet;
|
||||
|
||||
std::vector<juce::Rectangle<int>> browserCells;
|
||||
std::array<int, 3> slotIndices { 0, 1, 2 };
|
||||
int activeSlot { 0 };
|
||||
juce::String selectedPresetLabel { "Presets" };
|
||||
|
||||
Reference in New Issue
Block a user