further progress making TableComponent use valueTree for its model. begin doing the same for Pills.

This commit is contained in:
Alex Birch 2019-07-14 14:19:27 +01:00
parent 4140b3b85b
commit 58574425f3
No known key found for this signature in database
GPG Key ID: 305EB1F98D44ACBA
6 changed files with 316 additions and 166 deletions

View File

@ -7,74 +7,163 @@
using namespace std; using namespace std;
Pill::Pill(
AudioProcessorValueTreeState& valueTreeState,
int bank,
bool isFirst,
bool isLast
)
// : pills{pills}
: bank{bank}
, textButton{String(bank)}
{
textButton.setConnectedEdges (
(isFirst ? 0 : Button::ConnectedOnLeft)
| (isLast ? 0 : Button::ConnectedOnRight)
);
textButton.setRadioGroupId(34567);
loadToggleState();
textButton.setClickingTogglesState(true);
valueTreeState.state.addListener(this);
textButton.addListener(this);
}
Pill::~Pill() {
valueTreeState.state.removeListener(this);
textButton.removeListener(this);
}
Pill::loadToggleState() {
RangedAudioParameter *param {valueTreeState.getParameter("bank")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
int value{castParam->get()};
textButton.setToggleState(value == bank, dontSendNotification);
}
void Pill::buttonClicked (Button* button) {
// selected = button;
// onItemSelected(itemToIDMapper(button->getName().toStdString()));
RangedAudioParameter *param {valueTreeState.getParameter("bank")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
*castParam = bank;
}
void Pill::parameterChanged(const String& parameterID, float newValue) {
if (parameterID == "bank") {
loadToggleState();
}
}
void Pill::valueTreePropertyChanged(
ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) {
if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) {
loadModelFrom(treeWhosePropertyHasChanged);
}
}
Pills::Pills( Pills::Pills(
string label, AudioProcessorValueTreeState& valueTreeState
const vector<string> &items, // string label,
const function<void (int)> &onItemSelected, // const vector<string> &items,
const function<int (const string&)> &itemToIDMapper, // const function<void (int)> &onItemSelected,
int initiallySelectedItem // const function<int (const string&)> &itemToIDMapper,
) : label(label), // int initiallySelectedItem
items(items), )
onItemSelected(onItemSelected), : valueTreeState{valueTreeState}
itemToIDMapper(itemToIDMapper) // , label{label}
// items(items),
// onItemSelected(onItemSelected),
// itemToIDMapper(itemToIDMapper)
{ {
// faster (rounded edges introduce transparency) // faster (rounded edges introduce transparency)
setOpaque (true); setOpaque (true);
populate(initiallySelectedItem); // populate(initiallySelectedItem);
loadModelFrom(valueTreeState.state.getChildWithName("banks"));
valueTreeState.state.addListener(this);
} }
void Pills::populate(int initiallySelectedItem) { Pills::~Pills() {
int index = 0; valueTreeState.state.removeListener(this);
for (string item : items) {
TextButton* pill = addToList(new TextButton(
item
));
// pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter());
// pill->setBounds(20 + index * 55, 260, 55, 24);
pill->setConnectedEdges (
(index == 0 ? 0 : Button::ConnectedOnLeft)
| (index == (items.size()-1) ? 0 : Button::ConnectedOnRight)
);
pill->setRadioGroupId(34567);
if (index == initiallySelectedItem) {
pill->setToggleState(true, dontSendNotification);
selected = pill;
} }
pill->setClickingTogglesState(true);
pill->addListener(this); void Pills::valueTreePropertyChanged(
index++; ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) {
if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) {
loadModelFrom(treeWhosePropertyHasChanged);
} }
} }
void Pills::setItems( void Pills::loadModelFrom(ValueTree& banks) {
const vector<string> &items, pills.clear();
int initiallySelectedItem int numChildren{banks.getNumChildren()};
) { for(int i{0}; i < numChildren; i++) {
this->items = items; ValueTree child{banks.getChild(i)};
for(TextButton* t : buttons) { int num{child.getProperty("num")};
t->removeListener(this); // rows.push_back(unique_ptr<Pill>(new Pill(), [](Pill* pill) {
// pill->remo
// }));
pills.emplace_back(
this.valueTreeState,
num,
i == 0,
i == numChildren - 1);
} }
buttons.clear(true);
populate(initiallySelectedItem);
resized();
} }
void Pills::buttonClicked (Button* button) { // void Pills::populate(int initiallySelectedItem) {
selected = button; // int index = 0;
onItemSelected(itemToIDMapper(button->getName().toStdString())); // for (string item : items) {
} // TextButton* pill = addToList(new TextButton(
// item
// ));
// // pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter());
// // pill->setBounds(20 + index * 55, 260, 55, 24);
// pill->setConnectedEdges (
// (index == 0 ? 0 : Button::ConnectedOnLeft)
// | (index == (items.size()-1) ? 0 : Button::ConnectedOnRight)
// );
// pill->setRadioGroupId(34567);
// if (index == initiallySelectedItem) {
// pill->setToggleState(true, dontSendNotification);
// selected = pill;
// }
// pill->setClickingTogglesState(true);
// pill->addListener(this);
// index++;
// }
// }
TextButton* Pills::addToList (TextButton* newButton) { // void Pills::setItems(
buttons.add (newButton); // const vector<string> &items,
addAndMakeVisible (newButton); // int initiallySelectedItem
return newButton; // ) {
} // this->items = items;
// for(TextButton* t : buttons) {
// t->removeListener(this);
// }
// buttons.clear(true);
// populate(initiallySelectedItem);
// resized();
// }
// TextButton* Pills::addToList (TextButton* newButton) {
// buttons.add (newButton);
// addAndMakeVisible (newButton);
// return newButton;
// }
void Pills::cycle(bool right) { void Pills::cycle(bool right) {
int currentIx = static_cast<const int>(distance(buttons.begin(), find(buttons.begin(), buttons.end(), selected))); // TODO: base this on valueTree
currentIx += right ? 1 : buttons.size()-1; int currentIx = static_cast<const int>(distance(pills.begin(), find(pills.begin(), pills.end(), selected)));
buttons[currentIx % buttons.size()]->triggerClick(); currentIx += right ? 1 : pills.size()-1;
pills[currentIx % pills.size()]->textButton.triggerClick();
} }
void Pills::resized() { void Pills::resized() {

View File

@ -8,35 +8,81 @@
using namespace std; using namespace std;
class Pills : public Component, class Pill
public Button::Listener { : public Button::Listener
, public AudioProcessorValueTreeState::Listener
{
public: public:
Pills( Pill(
string label, AudioProcessorValueTreeState& valueTreeState,
const vector<string> &items, int bank,
const function<void (int)> &onItemSelected, bool isFirst,
const function<int (const string&)> &itemToIDMapper, bool isLast
int initiallySelectedItem
);
void setItems(
const vector<string> &items,
int initiallySelectedItem
); );
~Pill();
void buttonClicked (Button* button) override; void buttonClicked (Button* button) override;
virtual void parameterChanged (const String& parameterID, float newValue) override;
private:
void loadToggleState();
AudioProcessorValueTreeState& valueTreeState;
int bank;
TextButton textButton;
friend class Pills;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pill)
}
class Pills
: public Component
, public ValueTree::Listener
{
public:
Pills(
AudioProcessorValueTreeState& valueTreeState
// string label
// const vector<string> &items,
// const function<void (int)> &onItemSelected,
// const function<int (const string&)> &itemToIDMapper,
// int initiallySelectedItem
);
~Pills();
// void setItems(
// const vector<string> &items,
// int initiallySelectedItem
// );
// void buttonClicked (Button* button) override;
void cycle(bool right); void cycle(bool right);
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) override;
inline virtual void valueTreeChildAdded (ValueTree& parentTree,
ValueTree& childWhichHasBeenAdded) override {};
inline virtual void valueTreeChildRemoved (ValueTree& parentTree,
ValueTree& childWhichHasBeenRemoved,
int indexFromWhichChildWasRemoved) override {};
inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
int oldIndex, int newIndex) override {};
inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {};
inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {};
private: private:
string label; void loadModelFrom(ValueTree& banks);
vector<string> items;
function<void (int)> onItemSelected;
function<int (const string&)> itemToIDMapper;
OwnedArray<TextButton> buttons; AudioProcessorValueTreeState& valueTreeState;
// string label;
// vector<string> items;
// function<void (int)> onItemSelected;
// function<int (const string&)> itemToIDMapper;
// OwnedArray<Pill> buttons;
vector<unique_ptr<Pill>> pills;
Button *selected; Button *selected;
TextButton* addToList (TextButton* newButton); // Pill* addToList (Pill* newButton);
void populate(int initiallySelectedItem); void populate(int initiallySelectedItem);
void resized() override; void resized() override;

View File

@ -20,16 +20,16 @@ TableComponent::TableComponent(
AudioProcessorValueTreeState& valueTreeState, AudioProcessorValueTreeState& valueTreeState,
// const vector<string> &columns, // const vector<string> &columns,
// const vector<TableRow> &rows, // const vector<TableRow> &rows,
const function<void (int)> &onRowSelected, // const function<void (int)> &onRowSelected,
// const function<int (const vector<string>&)> &rowToIDMapper, // const function<int (const vector<string>&)> &rowToIDMapper,
int initiallySelectedRow // int initiallySelectedRow
) )
: valueTreeState{valueTreeState} : valueTreeState{valueTreeState}
, font{14.0f} , font{14.0f}
//, columns{columns} //, columns{columns}
//, rows{rows} //, rows{rows}
, onRowSelected{onRowSelected}/*, // , onRowSelected{onRowSelected}
rowToIDMapper(rowToIDMapper)*/ // rowToIDMapper(rowToIDMapper)
{ {
// Create our table component and add it to this component.. // Create our table component and add it to this component..
addAndMakeVisible (table); addAndMakeVisible (table);
@ -73,7 +73,9 @@ rowToIDMapper(rowToIDMapper)*/
table.setWantsKeyboardFocus(false); table.setWantsKeyboardFocus(false);
table.selectRow(initiallySelectedRow); // table.selectRow();
loadModelFrom(valueTreeState.state.getChildWithName("presets"));
// selectCurrentPreset();
// we could now change some initial settings.. // we could now change some initial settings..
table.getHeader().setSortColumnId(1, false); // sort ascending by ID column table.getHeader().setSortColumnId(1, false); // sort ascending by ID column
@ -90,40 +92,38 @@ TableComponent::~TableComponent() {
valueTreeState.state.removeListener(this); valueTreeState.state.removeListener(this);
} }
// void TableComponent::parameterChanged(const String& parameterID, float newValue) { void TableComponent::loadModelFrom(ValueTree& presets) {
// // valueTreeState.getParameter rows.clear();
// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; int numChildren{presets.getNumChildren()};
// if (parameterID == "bank") { for(int i{0}; i<numChildren; i++) {
ValueTree child{presets.getChild(i)};
int num{child.getProperty("num")};
String name{child.getProperty("name")};
rows.emplace_back(num, name);
}
table.deselectAllRows();
table.updateContent();
table.getHeader().setSortColumnId(0, true);
selectCurrentPreset();
table.repaint();
}
void TableComponent::parameterChanged(const String& parameterID, float newValue) {
// valueTreeState.getParameter
if (parameterID == "preset") {
selectCurrentPreset();
// RangedAudioParameter *param {valueTreeState.getParameter("preset")};
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr); // jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)}; // AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
// int value{castParam->get()}; // int value{castParam->get()};
// } else if (parameterID == "preset") { }
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr); }
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
// int value{castParam->get()};
// }
// }
void TableComponent::valueTreePropertyChanged( void TableComponent::valueTreePropertyChanged(
ValueTree& treeWhosePropertyHasChanged, ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) { const Identifier& property) {
if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) {
rows.clear(); loadModelFrom(treeWhosePropertyHasChanged);
int numChildren{treeWhosePropertyHasChanged.getNumChildren()};
for(int i{0}; i<numChildren; i++) {
ValueTree child{treeWhosePropertyHasChanged.getChild(i)};
int num{child.getProperty("num")};
String name{child.getProperty("name")};
rows.emplace_back(num, name);
}
// if (&treeWhosePropertyHasChanged == &valueTree) {
// if (property == StringRef("path")) {
// String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")};
// DEBUG_PRINT(soundFontPath);
// if (soundFontPath.isNotEmpty()) {
// loadFont(soundFontPath);
// }
// }
} }
} }
@ -158,6 +158,13 @@ void TableComponent::paintRowBackground (
g.fillAll (alternateColour); g.fillAll (alternateColour);
} }
String TableRow::getStringContents(int columnId) {
if (columnId <= 1) {
return String(row.preset);
}
return row.name;
}
// This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom // This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
// components. // components.
void TableComponent::paintCell ( void TableComponent::paintCell (
@ -172,12 +179,7 @@ void TableComponent::paintCell (
g.setFont (font); g.setFont (font);
TableRow& row{rows[rowNumber]}; TableRow& row{rows[rowNumber]};
String text; String text{row.getStringContents(columnId)};
if (columnId <= 1) {
text = String(row.preset);
} else {
text = row.name;
}
g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true); g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId)); g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId));
@ -191,7 +193,7 @@ void TableComponent::sortOrderChanged (
bool isForwards bool isForwards
) { ) {
if (newSortColumnId != 0) { if (newSortColumnId != 0) {
int selectedRowIx = table.getSelectedRow(); // int selectedRowIx = table.getSelectedRow();
// TableRow* selectedRow; // TableRow* selectedRow;
// if (selectedRowIx >= 0) { // if (selectedRowIx >= 0) {
// selectedRow = &rows[selectedRowIx]; // selectedRow = &rows[selectedRowIx];
@ -201,13 +203,27 @@ void TableComponent::sortOrderChanged (
sort(rows.begin(), rows.end(), sorter); sort(rows.begin(), rows.end(), sorter);
table.updateContent(); table.updateContent();
selectCurrentPreset();
// if (selectedRowIx >= 0) {
// for (auto it = rows.begin(); it != rows.end(); ++it) {
// if(it->preset == value) {
// int index {static_cast<int>(std::distance(rows.begin(), it))};
// table.selectRow(index);
// break;
// }
// }
// }
}
}
void TableComponent::selectCurrentPreset() {
table.deselectAllRows();
RangedAudioParameter *param {valueTreeState.getParameter("preset")}; RangedAudioParameter *param {valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr); jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)}; AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
int value{castParam->get()}; int value{castParam->get()};
if (selectedRowIx >= 0) {
for (auto it = rows.begin(); it != rows.end(); ++it) { for (auto it = rows.begin(); it != rows.end(); ++it) {
if(it->preset == value) { if(it->preset == value) {
int index {static_cast<int>(std::distance(rows.begin(), it))}; int index {static_cast<int>(std::distance(rows.begin(), it))};
@ -216,8 +232,6 @@ void TableComponent::sortOrderChanged (
} }
} }
} }
}
}
// This is overloaded from TableListBoxModel, and should choose the best width for the specified // This is overloaded from TableListBoxModel, and should choose the best width for the specified
// column. // column.
@ -231,12 +245,7 @@ int TableComponent::getColumnAutoSizeWidth (int columnId) {
// find the widest bit of text in this column.. // find the widest bit of text in this column..
for (int i{getNumRows()}; --i >= 0;) { for (int i{getNumRows()}; --i >= 0;) {
TableRow& row{rows[i]}; TableRow& row{rows[i]};
String text; String text{row.getStringContents(columnId)};
if (columnId <= 1) {
text = String(row.preset);
} else {
text = row.name;
}
widest = jmax (widest, font.getStringWidth (text)); widest = jmax (widest, font.getStringWidth (text));
} }
@ -302,5 +311,5 @@ TableRow::TableRow(
String name String name
) )
: preset{preset} : preset{preset}
: name{name} , name{name}
{} {}

View File

@ -22,6 +22,9 @@ public:
String name String name
); );
private: private:
/** 1-indexed */
String getStringContents(int columnId);
int preset; int preset;
String name; String name;
@ -32,16 +35,16 @@ private:
class TableComponent : public Component, class TableComponent : public Component,
public TableListBoxModel, public TableListBoxModel,
public ValueTree::Listener/*, public ValueTree::Listener,
public AudioProcessorValueTreeState::Listener */ { public AudioProcessorValueTreeState::Listener {
public: public:
TableComponent( TableComponent(
AudioProcessorValueTreeState& valueTreeState, AudioProcessorValueTreeState& valueTreeState
// const vector<string> &columns, // const vector<string> &columns,
// const vector<TableRow> &rows, // const vector<TableRow> &rows,
const function<void (int)> &onRowSelected, // const function<void (int)> &onRowSelected,
// const function<int (const vector<string>&)> &rowToIDMapper, // const function<int (const vector<string>&)> &rowToIDMapper,
int initiallySelectedRow // int initiallySelectedRow
); );
~TableComponent(); ~TableComponent();
@ -75,7 +78,7 @@ public:
bool keyPressed(const KeyPress &key) override; bool keyPressed(const KeyPress &key) override;
// virtual void parameterChanged (const String& parameterID, float newValue) override; virtual void parameterChanged (const String& parameterID, float newValue) override;
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) override; const Identifier& property) override;
@ -89,6 +92,9 @@ public:
inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {};
inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {};
private: private:
void loadModelFrom(ValueTree& presets);
void selectCurrentPreset();
AudioProcessorValueTreeState& valueTreeState; AudioProcessorValueTreeState& valueTreeState;
TableListBox table; // the table component itself TableListBox table; // the table component itself
@ -97,7 +103,7 @@ private:
// vector<string> columns; // vector<string> columns;
vector<TableRow> rows; vector<TableRow> rows;
function<void (int)> onRowSelected; // function<void (int)> onRowSelected;
// function<int (const vector<string>&)> rowToIDMapper; // function<int (const vector<string>&)> rowToIDMapper;
// A comparator used to sort our data when the user clicks a column header // A comparator used to sort our data when the user clicks a column header

View File

@ -41,11 +41,11 @@ TablesComponent::TablesComponent(
// banksToPresets, // banksToPresets,
// selectedBank // selectedBank
// ), // ),
[this](int preset){ // [this](int preset){
this->onPresetSelected(preset); // this->onPresetSelected(preset);
}, // },
// rowToPresetMapper, // rowToPresetMapper,
presetToIndexMapper(selectedPreset) // presetToIndexMapper(selectedPreset)
); );
banks = new Pills( banks = new Pills(
"Banks", "Banks",
@ -108,19 +108,19 @@ int TablesComponent::presetToIndexMapper(int preset) {
return 0; return 0;
} }
void TablesComponent::onPresetSelected(int preset) { // void TablesComponent::onPresetSelected(int preset) {
if (!initialised || preset == -1) { // if (!initialised || preset == -1) {
return; // return;
} // }
cout << "Preset " << preset << endl; // cout << "Preset " << preset << endl;
// selectedPreset = preset; // // selectedPreset = preset;
fluidSynthModel.changePreset(selectedBank, preset); // fluidSynthModel.changePreset(selectedBank, preset);
} // }
TablesComponent::~TablesComponent() { TablesComponent::~TablesComponent() {
delete presetTable; delete presetTable;
delete banks; delete banks;
fluidSynthModel.removeListener(this); // fluidSynthModel.removeListener(this);
} }
vector<string> TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { vector<string> TablesComponent::mapBanks(const BanksToPresets &banksToPresets) {
@ -138,21 +138,21 @@ vector<string> TablesComponent::mapBanks(const BanksToPresets &banksToPresets) {
} }
vector<vector<string>> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) { // vector<vector<string>> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) {
vector<vector<string>> rows; // vector<vector<string>> rows;
pair<BanksToPresets::const_iterator, BanksToPresets::const_iterator> iterators = banksToPresets.equal_range(bank); // pair<BanksToPresets::const_iterator, BanksToPresets::const_iterator> iterators = banksToPresets.equal_range(bank);
for (auto it = iterators.first; it != iterators.second; ++it) { // for (auto it = iterators.first; it != iterators.second; ++it) {
Preset b = it->second; // Preset b = it->second;
vector<string> row; // vector<string> row;
row.push_back(to_string(b.getPreset())); // row.push_back(to_string(b.getPreset()));
row.push_back(b.getName()); // row.push_back(b.getName());
rows.push_back(row); // rows.push_back(row);
} // }
return rows; // return rows;
} // }
void TablesComponent::resized() { void TablesComponent::resized() {
Rectangle<int> r (getLocalBounds()); Rectangle<int> r (getLocalBounds());

View File

@ -35,12 +35,12 @@ private:
FluidSynthModel& fluidSynthModel; FluidSynthModel& fluidSynthModel;
int selectedBank; int selectedBank;
Pills* banks; Pills banks;
TableComponent* presetTable; TableComponent presetTable;
BanksToPresets banksToPresets; BanksToPresets banksToPresets;
static vector<vector<string>> mapPresets(const BanksToPresets &banksToPresets, int bank); // static vector<vector<string>> mapPresets(const BanksToPresets &banksToPresets, int bank);
static vector<string> mapBanks(const BanksToPresets &banksToPresets); static vector<string> mapBanks(const BanksToPresets &banksToPresets);
void onBankSelected(int bank); void onBankSelected(int bank);