Make patch LCD clickable to open .cfl load/save file menu

This commit is contained in:
Armin 2026-07-15 23:48:41 +02:00
commit fcdb1939ee
2 changed files with 107 additions and 0 deletions

View file

@ -529,6 +529,7 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
addAndMakeVisible(presetButton);
addAndMakeVisible(patchLCD);
patchLCD.onClick = [this]() { showFileMenu(); };
auto setupArrow = [&](juce::TextButton& btn) {
btn.setColour(juce::TextButton::buttonColourId, juce::Colour(0xcc2a2a2a));
@ -703,6 +704,108 @@ void MainContentComponent::showPresetMenu() {
});
}
void MainContentComponent::showFileMenu() {
juce::PopupMenu menu;
menu.addItem(1, "Load Preset...");
menu.addItem(2, "Save Preset...");
menu.showMenuAsync(juce::PopupMenu::Options().withTargetComponent(&patchLCD),
[this](int result) {
if (result == 1) loadPresetFile();
else if (result == 2) savePresetFile();
});
}
static const char* kCflTag = "ChromaFlockPatch";
#include "Version.h"
static const juce::String kCflVersion = CHROMAFLOCK_VERSION;
juce::String MainContentComponent::currentPresetName() const {
auto& presets = processorRef.presetManager.getPresets();
if (currentPresetIndex >= 0 && currentPresetIndex < static_cast<int>(presets.size()))
return presets[currentPresetIndex].name;
return patchLCD.getText();
}
void MainContentComponent::savePresetFile() {
auto* chooser = new juce::FileChooser("Save ChromaFlock Preset",
juce::File::getSpecialLocation(juce::File::userHomeDirectory)
.getChildFile("chromaflock-" + currentPresetName().replaceCharacter(' ', '-') + ".cfl"),
"*.cfl");
chooser->launchAsync(juce::FileBrowserComponent::saveMode
| juce::FileBrowserComponent::canSelectFiles,
[this, chooser](const juce::FileChooser& c) {
std::unique_ptr<juce::FileChooser> deleter(chooser);
auto file = c.getResult();
if (file == juce::File())
return;
if (file.getFileExtension().toLowerCase() != ".cfl")
file = file.withFileExtension(".cfl");
auto state = processorRef.apvts.copyState();
std::unique_ptr<juce::XmlElement> presetXml(state.createXml());
std::unique_ptr<juce::XmlElement> root(new juce::XmlElement(kCflTag));
root->setAttribute("version", kCflVersion);
root->setAttribute("generator", "ChromaFlock");
root->addChildElement(presetXml.release());
if (!root->writeTo(file))
juce::NativeMessageBox::showMessageBoxAsync(juce::AlertWindow::WarningIcon,
"Save Failed", "Could not write preset file:\n" + file.getFullPathName(), this);
else
patchLCD.setText(file.getFileNameWithoutExtension());
});
}
void MainContentComponent::loadPresetFile() {
auto* chooser = new juce::FileChooser("Load ChromaFlock Preset",
juce::File::getSpecialLocation(juce::File::userHomeDirectory),
"*.cfl");
chooser->launchAsync(juce::FileBrowserComponent::openMode
| juce::FileBrowserComponent::canSelectFiles,
[this, chooser](const juce::FileChooser& c) {
std::unique_ptr<juce::FileChooser> deleter(chooser);
auto file = c.getResult();
if (file == juce::File() || !file.existsAsFile())
return;
std::unique_ptr<juce::XmlElement> root(juce::XmlDocument::parse(file));
if (root == nullptr || !root->hasTagName(kCflTag)) {
juce::NativeMessageBox::showMessageBoxAsync(juce::AlertWindow::WarningIcon,
"Invalid File", "This is not a valid ChromaFlock (.cfl) preset file.", this);
return;
}
auto fileVersion = root->getStringAttribute("version", "");
if (fileVersion != kCflVersion) {
juce::NativeMessageBox::showMessageBoxAsync(juce::AlertWindow::WarningIcon,
"Version Mismatch",
"This preset was saved with ChromaFlock " + (fileVersion.isEmpty() ? "<unknown>" : fileVersion) +
", but you are running ChromaFlock " + kCflVersion +
".\nThe preset may not sound as intended.", this);
}
auto* presetXml = root->getChildByName(processorRef.apvts.state.getType());
if (presetXml == nullptr) {
juce::NativeMessageBox::showMessageBoxAsync(juce::AlertWindow::WarningIcon,
"Invalid File", "The preset file does not contain ChromaFlock parameter data.", this);
return;
}
processorRef.fadeOutActiveVoices(0.25f);
processorRef.apvts.replaceState(juce::ValueTree::fromXml(*presetXml));
currentPresetIndex = -1;
patchLCD.setText(file.getFileNameWithoutExtension());
setupSyncKnob(lfo1RateKnob, lfo1RateAttach, lfo1BeatAttach, "lfo1Rate", "lfo1Beat", lfo1SyncBox);
setupSyncKnob(lfo2RateKnob, lfo2RateAttach, lfo2BeatAttach, "lfo2Rate", "lfo2Beat", lfo2SyncBox);
setupSyncKnob(delayTimeKnob, delayTimeAttach, delayBeatAttach, "delayTime", "delayBeat", delaySyncBox);
});
}
void MainContentComponent::applyCurrentPreset() {
auto& presets = processorRef.presetManager.getPresets();
if (presets.empty()) return;

View file

@ -207,6 +207,10 @@ private:
const juce::String& beatId,
juce::ComboBox& syncBox);
void showPresetMenu();
void showFileMenu();
void loadPresetFile();
void savePresetFile();
juce::String currentPresetName() const;
void applyCurrentPreset();
void randomizeParameters();