diff --git a/src-ui/components/HeaderRegion.cpp b/src-ui/components/HeaderRegion.cpp index 7d09de53..1ac18703 100644 --- a/src-ui/components/HeaderRegion.cpp +++ b/src-ui/components/HeaderRegion.cpp @@ -100,19 +100,14 @@ HeaderRegion::HeaderRegion(SCXTEditor *e) : HasEditor(e) }); addAndMakeVisible(*scMenu); - auto comingSoon = []() { - juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::InfoIcon, "Coming Soon", - "This feature is not yet implemented", "OK"); - }; - undoButton = std::make_unique(); undoButton->setLabel("Undo"); - undoButton->setOnCallback(comingSoon); + undoButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*undoButton); redoButton = std::make_unique(); redoButton->setLabel("Redo"); - redoButton->setOnCallback(comingSoon); + redoButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*redoButton); tuningButton = std::make_unique(); @@ -138,7 +133,7 @@ HeaderRegion::HeaderRegion(SCXTEditor *e) : HasEditor(e) addAndMakeVisible(*zoomButton); chipButton = std::make_unique(jcmp::GlyphPainter::MEMORY); - chipButton->setOnCallback(comingSoon); + chipButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*chipButton); saveAsButton = std::make_unique(jcmp::GlyphPainter::SAVE); diff --git a/src-ui/components/SCXTEditor.cpp b/src-ui/components/SCXTEditor.cpp index 33e83e36..5c758c4a 100644 --- a/src-ui/components/SCXTEditor.cpp +++ b/src-ui/components/SCXTEditor.cpp @@ -434,4 +434,12 @@ void SCXTEditor::setTabSelection(const std::string &k, const std::string &t) otherTabSelection[k] = t; sendToSerialization(messaging::client::UpdateOtherTabSelection({k, t})); } + +std::function SCXTEditor::makeComingSoon(const std::string &feature) const +{ + return [f = feature]() { + juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::InfoIcon, "Coming Soon", + f + " is not yet implemented", "OK"); + }; +} } // namespace scxt::ui diff --git a/src-ui/components/SCXTEditor.h b/src-ui/components/SCXTEditor.h index 98c959f8..202ffb08 100644 --- a/src-ui/components/SCXTEditor.h +++ b/src-ui/components/SCXTEditor.h @@ -201,6 +201,9 @@ struct SCXTEditor : sst::jucegui::components::WindowPanel, juce::DragAndDropCont void onSelectedPart(const int16_t); int16_t getSelectedPart() const; + void onPartConfiguration(const scxt::messaging::client::partConfigurationPayload_t &); + std::array partConfigurations; + selection::SelectionManager::otherTabSelection_t otherTabSelection; void onOtherTabSelection(const scxt::selection::SelectionManager::otherTabSelection_t &p); std::string queryTabSelection(const std::string &k); @@ -263,6 +266,8 @@ struct SCXTEditor : sst::jucegui::components::WindowPanel, juce::DragAndDropCont std::queue callbackQueue; engine::Engine::EngineStatusMessage engineStatus; + std::function makeComingSoon(const std::string &feature = "This feature") const; + /* * Items to deal with the shared memory reads */ diff --git a/src-ui/components/SCXTEditorResponseHandlers.cpp b/src-ui/components/SCXTEditorResponseHandlers.cpp index 06029707..9e25f2d2 100644 --- a/src-ui/components/SCXTEditorResponseHandlers.cpp +++ b/src-ui/components/SCXTEditorResponseHandlers.cpp @@ -389,4 +389,17 @@ void SCXTEditor::onOtherTabSelection( multiScreen->onOtherTabSelection(); } } + +void SCXTEditor::onPartConfiguration( + const scxt::messaging::client::partConfigurationPayload_t &payload) +{ + const auto &[pt, c] = payload; + assert(pt >= 0 && pt < scxt::numParts); + partConfigurations[pt] = c; + // When I have active show/hide i will need to rewrite this i bet + if (playScreen && playScreen->partSidebars[pt]) + playScreen->partSidebars[pt]->resetFromEditorCache(); + if (multiScreen && multiScreen->parts) + multiScreen->parts->partConfigurationChanged(pt); +} } // namespace scxt::ui \ No newline at end of file diff --git a/src-ui/components/browser/BrowserPane.cpp b/src-ui/components/browser/BrowserPane.cpp index 42a55736..f49b85ea 100644 --- a/src-ui/components/browser/BrowserPane.cpp +++ b/src-ui/components/browser/BrowserPane.cpp @@ -70,13 +70,14 @@ struct DriveListBoxModel : juce::ListBoxModel jcmp::Label::Styles::labelfont)); // TODO: Style all of these - auto textColor = juce::Colour(190, 190, 190); + auto textColor = + browserPane->editor->themeColor(theme::ColorMap::generic_content_medium); auto fillColor = juce::Colour(0, 0, 0).withAlpha(0.f); if (rowIsSelected) { - fillColor = juce::Colour(0x40, 0x20, 0x00); - textColor = textColor.brighter(0.4); + fillColor = browserPane->editor->themeColor(theme::ColorMap::bg_3); + textColor = browserPane->editor->themeColor(theme::ColorMap::generic_content_high); } g.setColour(fillColor); g.fillRect(0, 0, width, height); @@ -398,13 +399,14 @@ struct DriveFSListBoxRow : public juce::Component jcmp::Label::Styles::labelfont)); // TODO: Style all of these - auto textColor = juce::Colour(190, 190, 190); + auto textColor = + browserPane->editor->themeColor(theme::ColorMap::generic_content_medium); auto fillColor = juce::Colour(0, 0, 0).withAlpha(0.f); if (isSelected) { - fillColor = juce::Colour(0x40, 0x20, 0x00); - textColor = textColor.brighter(0.4); + fillColor = browserPane->editor->themeColor(theme::ColorMap::bg_3); + textColor = browserPane->editor->themeColor(theme::ColorMap::generic_content_high); } g.setColour(fillColor); g.fillRect(0, 0, width, height); diff --git a/src-ui/components/multi/LFOPane.cpp b/src-ui/components/multi/LFOPane.cpp index f0be7670..7a01853d 100644 --- a/src-ui/components/multi/LFOPane.cpp +++ b/src-ui/components/multi/LFOPane.cpp @@ -447,7 +447,8 @@ struct CurveLFOPane : juce::Component, HasEditor // parent->selectedTab); // angleK = std::make_unique(); // angleK->setSource(fakeModel->getDummySourceFor('knb2')); - angleK = connectors::makeConnectedToDummy('angl'); + angleK = connectors::makeConnectedToDummy('angl', "Angle", 0, true, + editor->makeComingSoon("Angle")); addAndMakeVisible(*angleK); makeLabel(angleL, "Angle"); @@ -578,16 +579,20 @@ struct ENVLFOPane : juce::Component, HasEditor makeO(ms.envLfoStorage.sustain, sustainA, sustainS, sustainL); makeO(ms.envLfoStorage.release, releaseA, releaseS, releaseL); - factorK = connectors::makeConnectedToDummy('fact'); + factorK = connectors::makeConnectedToDummy('fact', "Factor", 0.5, false, + editor->makeComingSoon("Factor")); addAndMakeVisible(*factorK); makeLabel(factorL, "Factor"); - curveA = connectors::makeConnectedToDummy('crva'); + curveA = connectors::makeConnectedToDummy( + 'crva', "Curve A", 0.5, false, editor->makeComingSoon("Curve Attack")); addAndMakeVisible(*curveA); makeLabel(curveAL, "Curve A"); - curveD = connectors::makeConnectedToDummy('crvd'); + curveD = connectors::makeConnectedToDummy( + 'crvd', "Curve D", 0.5, false, editor->makeComingSoon("Curve Decay")); addAndMakeVisible(*curveD); makeLabel(curveDL, "Curve D"); - curveR = connectors::makeConnectedToDummy('crvr'); + curveR = connectors::makeConnectedToDummy( + 'crvr', "Curve R", 0.5, false, editor->makeComingSoon("Curve Release")); addAndMakeVisible(*curveR); makeLabel(curveRL, "Curve R"); } diff --git a/src-ui/components/multi/MappingPane.cpp b/src-ui/components/multi/MappingPane.cpp index fa7ab017..cb42ca78 100644 --- a/src-ui/components/multi/MappingPane.cpp +++ b/src-ui/components/multi/MappingPane.cpp @@ -181,47 +181,42 @@ struct MappingZoneHeader : HasEditor, juce::Component MappingZoneHeader(SCXTEditor *ed) : HasEditor(ed) { - auto comingSoon = []() { - juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::InfoIcon, "Coming Soon", - "This feature is not yet implemented", "OK"); - }; - autoMap = std::make_unique(); autoMap->setLabel("AUTO-MAP"); - autoMap->setOnCallback(comingSoon); + autoMap->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*autoMap); midiButton = std::make_unique(jcmp::GlyphPainter::GlyphType::MIDI); - midiButton->setOnCallback(comingSoon); + midiButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*midiButton); midiUDButton = std::make_unique( jcmp::GlyphPainter::GlyphType::MIDI, jcmp::GlyphPainter::GlyphType::UP_DOWN, 16); - midiUDButton->setOnCallback(comingSoon); + midiUDButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*midiUDButton); midiLRButton = std::make_unique( jcmp::GlyphPainter::GlyphType::MIDI, jcmp::GlyphPainter::GlyphType::LEFT_RIGHT, 16); - midiLRButton->setOnCallback(comingSoon); + midiLRButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*midiLRButton); fixOverlap = std::make_unique(); fixOverlap->setLabel("FIX OVERLAP"); - fixOverlap->setOnCallback(comingSoon); + fixOverlap->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*fixOverlap); fadeOverlap = std::make_unique(); fadeOverlap->setLabel("FADE OVERLAP"); - fadeOverlap->setOnCallback(comingSoon); + fadeOverlap->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*fadeOverlap); zoneSolo = std::make_unique(); zoneSolo->setLabel("ZONE SOLO"); - zoneSolo->setOnCallback(comingSoon); + zoneSolo->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*zoneSolo); lockButton = std::make_unique(jcmp::GlyphPainter::GlyphType::LOCK); - lockButton->setOnCallback(comingSoon); + lockButton->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*lockButton); fileLabel = std::make_unique(); @@ -230,7 +225,7 @@ struct MappingZoneHeader : HasEditor, juce::Component fileMenu = std::make_unique(); fileMenu->setLabel("sample-name-to-come.wav"); - fileMenu->setOnCallback(comingSoon); + fileMenu->setOnCallback(editor->makeComingSoon()); addAndMakeVisible(*fileMenu); } diff --git a/src-ui/components/multi/PartGroupSidebar.cpp b/src-ui/components/multi/PartGroupSidebar.cpp index 5a660de8..d8e09221 100644 --- a/src-ui/components/multi/PartGroupSidebar.cpp +++ b/src-ui/components/multi/PartGroupSidebar.cpp @@ -465,4 +465,9 @@ void PartGroupSidebar::setSelectedTab(int t) (t == 0 ? "part" : (t == 1 ? "group" : "zone"))); repaint(); } + +void PartGroupSidebar::partConfigurationChanged(int i) +{ + partSidebar->parts[i]->resetFromEditorCache(); +} } // namespace scxt::ui::multi \ No newline at end of file diff --git a/src-ui/components/multi/PartGroupSidebar.h b/src-ui/components/multi/PartGroupSidebar.h index a89c8d8a..ccfcb068 100644 --- a/src-ui/components/multi/PartGroupSidebar.h +++ b/src-ui/components/multi/PartGroupSidebar.h @@ -58,6 +58,8 @@ struct PartGroupSidebar : sst::jucegui::components::NamedPanel, HasEditor std::unique_ptr groupSidebar; std::unique_ptr partSidebar; + void partConfigurationChanged(int i); + void resized() override; }; } // namespace scxt::ui::multi diff --git a/src-ui/components/multi/PartSidebarCard.cpp b/src-ui/components/multi/PartSidebarCard.cpp index 8eb15374..d6abe9da 100644 --- a/src-ui/components/multi/PartSidebarCard.cpp +++ b/src-ui/components/multi/PartSidebarCard.cpp @@ -26,33 +26,195 @@ */ #include "PartSidebarCard.h" +#include "sst/jucegui/components/GlyphPainter.h" #include "messaging/messaging.h" #include "components/SCXTEditor.h" namespace scxt::ui::multi { namespace cmsg = scxt::messaging::client; +namespace jcmp = sst::jucegui::components; + +PartSidebarCard::PartSidebarCard(int p, SCXTEditor *e) : part(p), HasEditor(e) +{ + midiMode = std::make_unique(); + midiMode->setLabel("MIDI"); + midiMode->setOnCallback([w = juce::Component::SafePointer(this)]() { + if (!w) + return; + w->showMidiModeMenu(); + }); + addAndMakeVisible(*midiMode); + + outBus = std::make_unique(); + outBus->setLabel("OUT"); + outBus->setOnCallback(editor->makeComingSoon("Editing the output bus from here")); + addAndMakeVisible(*outBus); + + polyCount = std::make_unique(); + polyCount->setLabel("256"); + polyCount->setOnCallback(editor->makeComingSoon("Polyphony Limit/Monophony")); + addAndMakeVisible(*polyCount); + + patchName = std::make_unique(); + patchName->setLabel("Instrument Name"); + patchName->setOnCallback(editor->makeComingSoon("Instrument save/load")); + addAndMakeVisible(*patchName); + + auto onChange = [w = juce::Component::SafePointer(this)](const auto &a) { + if (w) + { + w->resetFromEditorCache(); + w->sendToSerialization( + cmsg::UpdatePartFullConfig({w->part, w->editor->partConfigurations[w->part]})); + } + }; + muteAtt = + std::make_unique("Mute", onChange, editor->partConfigurations[part].mute); + mute = std::make_unique(); + mute->setSource(muteAtt.get()); + mute->setLabel("M"); + addAndMakeVisible(*mute); + + auto soloOnChange = [w = juce::Component::SafePointer(this)](const auto &a) { + if (w) + { + static bool everWarned{false}; + if (!everWarned) + { + w->editor->makeComingSoon("Solo on Parts")(); + everWarned = true; + } + w->resetFromEditorCache(); + w->sendToSerialization( + cmsg::UpdatePartFullConfig({w->part, w->editor->partConfigurations[w->part]})); + } + }; + soloAtt = std::make_unique("Solo", soloOnChange, + editor->partConfigurations[part].solo); + solo = std::make_unique(); + solo->setSource(soloAtt.get()); + solo->setLabel("S"); + addAndMakeVisible(*solo); + + level = connectors::makeConnectedToDummy( + 'ptlv', "Level", 1.0, false, editor->makeComingSoon("Editing the Part Level Control")); + addAndMakeVisible(*level); + pan = connectors::makeConnectedToDummy( + 'ptpn', "Pan", 0.0, true, editor->makeComingSoon("Editing the Part Pan Control")); + addAndMakeVisible(*pan); + tuning = connectors::makeConnectedToDummy( + 'pttn', "Tuning", 0.0, true, editor->makeComingSoon("Editing the part Tuning")); + addAndMakeVisible(*tuning); +} + void PartSidebarCard::mouseDown(const juce::MouseEvent &event) { sendToSerialization(cmsg::DoSelectPart(part)); } void PartSidebarCard::paint(juce::Graphics &g) +{ + if (editor->getSelectedPart() == part) + { + g.setColour(editor->themeColor(theme::ColorMap::accent_1a)); + } + else + { + g.setColour(editor->themeColor(theme::ColorMap::generic_content_medium)); + } + if (selfAccent) + { + auto rb = getLocalBounds().reduced(1); + g.drawRect(rb); + } + auto r = juce::Rectangle(5, row0 + rowMargin, 18, rowHeight - 2 * rowMargin); + g.setFont(editor->themeApplier.interMediumFor(12)); + g.drawText(std::to_string(part + 1), r, juce::Justification::centred); + auto med = editor->themeColor(theme::ColorMap::generic_content_medium); + r = r.translated(0, rowHeight); + jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::GlyphType::MIDI, med); + r = r.translated(0, rowHeight); + jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::GlyphType::VOLUME, med); + r = r.translated(0, rowHeight); + jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::GlyphType::POLYPHONY, med); + + r = juce::Rectangle(66, row0 + rowMargin, 18, rowHeight - 2 * rowMargin); + r = r.translated(0, rowHeight); + jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::GlyphType::VOLUME, med); + r = r.translated(0, rowHeight); + jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::GlyphType::PAN, med); + r = r.translated(0, rowHeight); + jcmp::GlyphPainter::paintGlyph(g, r, jcmp::GlyphPainter::GlyphType::TUNING, med); +} + +void PartSidebarCard::resized() { - auto r = getLocalBounds().reduced(1); + auto sideBits = juce::Rectangle(5 + 18 + 2, row0 + rowMargin + rowHeight, 35, + rowHeight - 2 * rowMargin); + midiMode->setBounds(sideBits); + sideBits = sideBits.translated(0, rowHeight); + outBus->setBounds(sideBits); + sideBits = sideBits.translated(0, rowHeight); + polyCount->setBounds(sideBits); - if (selfAccent) + auto nameR = + juce::Rectangle(5 + 18 + 2, row0 + rowMargin + 1, 103, rowHeight - 2 * rowMargin + 2); + patchName->setBounds(nameR); + + nameR = nameR.translated(105, 0).withWidth(18); + mute->setBounds(nameR); + nameR = nameR.translated(20, 0); + solo->setBounds(nameR); + + auto slR = + juce::Rectangle(86, row0 + rowMargin + rowHeight, 82, rowHeight - 2 * rowMargin); + slR = slR.reduced(0, 1); + level->setBounds(slR); + slR = slR.translated(0, rowHeight); + pan->setBounds(slR); + slR = slR.translated(0, rowHeight); + tuning->setBounds(slR); +} + +void PartSidebarCard::showMidiModeMenu() +{ + auto makeMenuCallback = [w = juce::Component::SafePointer(this)](int ch) { + return [w, ch]() { + if (!w) + return; + w->editor->partConfigurations[w->part].channel = ch; + w->resetFromEditorCache(); + w->sendToSerialization( + cmsg::UpdatePartFullConfig({w->part, w->editor->partConfigurations[w->part]})); + }; + }; + auto p = juce::PopupMenu(); + auto ch = editor->partConfigurations[part].channel; + p.addSectionHeader("MIDI"); + p.addSeparator(); + p.addItem("OMNI", true, ch == engine::Part::PartConfiguration::omniChannel, + makeMenuCallback(engine::Part::PartConfiguration::omniChannel)); + for (int i = 0; i < 16; ++i) { - if (editor->getSelectedPart() == part) - { - g.setColour(editor->themeColor(theme::ColorMap::accent_1a)); - } - else - { - g.setColour(editor->themeColor(theme::ColorMap::generic_content_medium)); - } - g.drawRect(r); + p.addItem("Ch. " + std::to_string(i + 1), true, ch == i, makeMenuCallback(i)); + } + p.showMenuAsync(editor->defaultPopupMenuOptions(midiMode.get())); +} + +void PartSidebarCard::resetFromEditorCache() +{ + const auto &conf = editor->partConfigurations[part]; + auto mc = conf.channel; + if (mc == engine::Part::PartConfiguration::omniChannel) + { + midiMode->setLabel("OMNI"); + } + else + { + midiMode->setLabel(std::to_string(mc + 1)); } + repaint(); } } // namespace scxt::ui::multi \ No newline at end of file diff --git a/src-ui/components/multi/PartSidebarCard.h b/src-ui/components/multi/PartSidebarCard.h index af0eadc3..a4a8b39b 100644 --- a/src-ui/components/multi/PartSidebarCard.h +++ b/src-ui/components/multi/PartSidebarCard.h @@ -29,8 +29,13 @@ #define SCXT_SRC_UI_COMPONENTS_MULTI_PARTSIDEBARCARD_H #include -#include "sst/jucegui/components/Label.h" +#include "sst/jucegui/components/MenuButton.h" +#include "sst/jucegui/components/ToggleButton.h" +#include "sst/jucegui/components/TextPushButton.h" +#include "sst/jucegui/components/HSliderFilled.h" #include "components/HasEditor.h" +#include "connectors/PayloadDataAttachment.h" +#include "engine/part.h" namespace scxt::ui::multi { @@ -38,25 +43,28 @@ struct PartSidebarCard : juce::Component, HasEditor { int part; + using boolattachment_t = + scxt::ui::connectors::BooleanPayloadDataAttachment; + + static constexpr int row0{2}, rowHeight{20}, rowMargin{2}; + static constexpr int height{88}, width{172}; + + std::unique_ptr solo, mute; + std::unique_ptr muteAtt, soloAtt; + std::unique_ptr patchName; + std::unique_ptr midiMode, outBus, polyCount; + std::unique_ptr level, pan, tuning; bool selfAccent{true}; - std::unique_ptr partLabel; - PartSidebarCard(int p, SCXTEditor *e) : part(p), HasEditor(e) - { - partLabel = std::make_unique(); - partLabel->setText("Part " + std::to_string(part + 1)); - addAndMakeVisible(*partLabel); - partLabel->setInterceptsMouseClicks(false, false); - } + PartSidebarCard(int p, SCXTEditor *e); void paint(juce::Graphics &g) override; + void showMidiModeMenu(); + void mouseDown(const juce::MouseEvent &e) override; + void resized() override; - void resized() override - { - partLabel->setBounds(getLocalBounds()); - partLabel->setJustification(juce::Justification::centred); - } + void resetFromEditorCache(); }; } // namespace scxt::ui::multi #endif // SHORTCIRCUITXT_PARTSIDEBARCARD_H diff --git a/src-ui/components/multi/ProcessorPaneEQsFilters.cpp b/src-ui/components/multi/ProcessorPaneEQsFilters.cpp index 2392a75d..aef42492 100644 --- a/src-ui/components/multi/ProcessorPaneEQsFilters.cpp +++ b/src-ui/components/multi/ProcessorPaneEQsFilters.cpp @@ -280,7 +280,7 @@ void ProcessorPane::layoutControlsEQNBandParm() return; } auto bd = getContentAreaComponent()->getLocalBounds(); - auto slWidth = 20; + auto slWidth = 0; auto eq = bd.withTrimmedRight(slWidth); auto mx = bd.withLeft(bd.getWidth() - slWidth); @@ -330,7 +330,7 @@ void ProcessorPane::layoutControlsEQNBandParm() w->rebuildCurves(); } }; - lo::knobCX(*floatEditors[i], cols[(i % 3) + 1].getCentreX(), 5); + lo::knobCX(*floatEditors[i], cols[(i % 3) + 1].getCentreX(), 2); if (i < 3) floatEditors[i]->setVisible(true); else diff --git a/src-ui/components/multi/SingleMacroEditor.cpp b/src-ui/components/multi/SingleMacroEditor.cpp index 094b7f8a..1a13d156 100644 --- a/src-ui/components/multi/SingleMacroEditor.cpp +++ b/src-ui/components/multi/SingleMacroEditor.cpp @@ -110,7 +110,7 @@ SingleMacroEditor::SingleMacroEditor(SCXTEditor *e, int p, int i, bool vo) sst::jucegui::style::StyleConsumer(sst::jucegui::components::NamedPanel::Styles::styleClass), part(p), index(i), valueOnly(vo) { - knob = connectors::makeConnectedToDummy('mcro'); + knob = std::make_unique(); knob->setDrawLabel(false); addAndMakeVisible(*knob); diff --git a/src-ui/connectors/PayloadDataAttachment.h b/src-ui/connectors/PayloadDataAttachment.h index 7c1a7e9c..d2f8ef0f 100644 --- a/src-ui/connectors/PayloadDataAttachment.h +++ b/src-ui/connectors/PayloadDataAttachment.h @@ -546,31 +546,41 @@ template struct SingleValueFactor struct DummyContinuous : sst::jucegui::data::Continuous { float f{0.5}; + bool bip{false}; + std::string lab{"Unimpl"}; virtual float getValue() const override { return f; } - virtual void setValueFromGUI(const float &fv) override { f = fv; } + virtual void setValueFromGUI(const float &fv) override + { + if (!everEdited && onFirstEdit) + onFirstEdit(); + f = fv; + everEdited = true; + } virtual void setValueFromModel(const float &fv) override { f = fv; }; virtual float getDefaultValue() const override { return 0.5f; }; - virtual std::string getLabel() const override { return "Dummy"; }; -}; + virtual bool isBipolar() const override { return bip; } + float getMin() const override { return isBipolar() ? -1 : 0; } -struct DummyDiscrete : sst::jucegui::data::Discrete -{ - int v{1}; - int getValue() const override { return v; } - void setValueFromGUI(const int &f) override { v = f; }; - void setValueFromModel(const int &f) override { v = f; }; - int getDefaultValue() const override { return 0; } - std::string getLabel() const override { return "Dummy"; } + virtual std::string getLabel() const override + { + if (lab != "Unimpl") + return lab + " (Unimplemented)"; + return lab; + }; + + std::function onFirstEdit{nullptr}; + bool everEdited{false}; }; -template -std::unique_ptr makeConnectedToDummy(uint32_t index) +template +std::unique_ptr makeConnectedToDummy(uint32_t index, const std::string &lab, float initVal, + bool isBip, std::function onFirstEdit) { - static std::unordered_map> dummyMap; + static std::unordered_map> dummyMap; auto res = std::make_unique(); - C *thisWillLeak{nullptr}; + DummyContinuous *thisWillLeak{nullptr}; auto dmp = dummyMap.find(index); if (dmp != dummyMap.end()) { @@ -578,8 +588,14 @@ std::unique_ptr makeConnectedToDummy(uint32_t index) } else { - auto tmp = std::make_unique(); + auto tmp = std::make_unique(); thisWillLeak = tmp.get(); + + thisWillLeak->f = initVal; + thisWillLeak->bip = isBip; + thisWillLeak->lab = lab; + thisWillLeak->onFirstEdit = onFirstEdit; + dummyMap[index] = std::move(tmp); std::string s; @@ -591,7 +607,7 @@ std::unique_ptr makeConnectedToDummy(uint32_t index) t = t << 8; } - SCLOG("WARNING: Dummy Widget in ui. Quasi-memory leak until we attach. '" << s << "'"); + SCLOG("Incomplete UI bound to dummy source '" << s << "'"); } res->setSource(thisWillLeak); diff --git a/src-ui/json-assets/themes/wireframe-light.json b/src-ui/json-assets/themes/wireframe-light.json index 5ef1f950..bfb772ae 100644 --- a/src-ui/json-assets/themes/wireframe-light.json +++ b/src-ui/json-assets/themes/wireframe-light.json @@ -1,10 +1,11 @@ [ { + "hoverfactor": "0.100000", "version": "1" }, { - "accent_1a": "#ffffb949", - "accent_1b": "#ffd09030", + "accent_1a": "#ff268bd2", + "accent_1b": "#ff2aa198", "accent_2a": "#ff2788d6", "accent_2a_alpha_a": "#142788d6", "accent_2a_alpha_b": "#522788d6", @@ -13,22 +14,22 @@ "accent_2b_alpha_a": "#14004f8a", "accent_2b_alpha_b": "#52004f8a", "accent_2b_alpha_c": "#80004f8a", - "bg_1": "#ffcbcdc0", - "bg_2": "#ff262a2f", - "bg_3": "#ff333333", - "generic_content_high": "#ffdfdfdf", - "generic_content_highest": "#ffffffff", - "generic_content_low": "#ff777777", + "bg_1": "#fffdf6e3", + "bg_2": "#ffeee8d5", + "bg_3": "#ff93a1a1", + "generic_content_high": "#ff073642", + "generic_content_highest": "#ff002b36", + "generic_content_low": "#ff839496", "generic_content_lowest": "#ff000000", - "generic_content_medium": "#ffafafaf", + "generic_content_medium": "#ff657b83", "grid_primary": "#a8393939", "grid_secondary": "#14a6a6a6", - "gutter_2": "#ff333333", - "gutter_3": "#ff262a2f", - "knob_fill": "#ff525252", - "panel_outline_2": "#00000000", - "panel_outline_3": "#00000000", + "gutter_2": "#ff657b83", + "gutter_3": "#ff657b83", + "knob_fill": "#ff93a1a1", + "panel_outline_2": "#ff586e75", + "panel_outline_3": "#ff586e75", "warning_1a": "#ffd67272", "warning_1b": "#ff8a0000" } -] +] \ No newline at end of file diff --git a/src/configuration.h b/src/configuration.h index 2a6b8a09..6e929dae 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -33,7 +33,7 @@ namespace scxt { -static constexpr uint64_t currentStreamingVersion{0x2024'08'16}; +static constexpr uint64_t currentStreamingVersion{0x2024'08'18}; static constexpr uint16_t blockSize{16}; static constexpr uint16_t blockSizeQuad{16 >> 2}; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a09d2d7c..dd7fc770 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -968,6 +968,10 @@ void Engine::sendFullRefreshToClient() const } for (int p = 0; p < numParts; ++p) { + serializationSendToClient( + messaging::client::s2c_send_part_configuration, + messaging::client::partConfigurationPayload_t{p, getPatch()->getPart(p)->configuration}, + *(getMessageController())); for (int i = 0; i < macrosPerPart; ++i) { serializationSendToClient( diff --git a/src/engine/engine.h b/src/engine/engine.h index 51d706b0..571c7797 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -136,7 +136,9 @@ struct Engine : MoveableOnly, SampleRateSupport std::vector res; for (const auto &[pidx, part] : sst::cpputils::enumerate(*patch)) { - if (part->channel == channel || part->channel == Part::omniChannel) + if (!part->configuration.mute && + (part->configuration.channel == channel || + part->configuration.channel == Part::PartConfiguration::omniChannel)) { for (const auto &[gidx, group] : sst::cpputils::enumerate(*part)) { diff --git a/src/engine/part.cpp b/src/engine/part.cpp index a0ff9a55..e44a2c8d 100644 --- a/src/engine/part.cpp +++ b/src/engine/part.cpp @@ -55,6 +55,7 @@ void Part::process(Engine &e) auto bi = g->outputInfo.routeTo; if (bi == DEFAULT_BUS) { + // this should be the route to point bi = (BusAddress)(PART_0 + partNumber); } auto &obus = e.getPatch()->busses.busByAddress(bi); diff --git a/src/engine/part.h b/src/engine/part.h index a90cf1bb..82816ce8 100644 --- a/src/engine/part.h +++ b/src/engine/part.h @@ -47,7 +47,7 @@ struct Engine; struct Part : MoveableOnly, SampleRateSupport { - Part(int16_t c) : id(PartID::next()), partNumber(c), channel(c) + Part(int16_t c) : id(PartID::next()), partNumber(c) { pitchBendSmoother.setTarget(0); int idx{0}; @@ -58,25 +58,33 @@ struct Part : MoveableOnly, SampleRateSupport m.name = Macro::defaultNameFor(idx); idx++; } + configuration.channel = c; } virtual ~Part() = default; PartID id; int16_t partNumber; - int16_t channel; Patch *parentPatch{nullptr}; - BusAddress routeTo{DEFAULT_BUS}; - void process(Engine &onto); + struct PartConfiguration + { + static constexpr int16_t omniChannel{-1}; - // TODO: have a channel mode like OMNI and MPE and everything - static constexpr int16_t omniChannel{-1}; + bool active{true}; + int16_t channel{omniChannel}; // a midi channel or a special value like omni + bool mute{false}; + bool solo{false}; + + BusAddress routeTo{DEFAULT_BUS}; + } configuration; + void process(Engine &onto); // TODO: editable name std::string getName() const { - return fmt::format("Part ch={}", - (channel == omniChannel ? "OMNI" : std::to_string(channel + 1))); + return fmt::format("Part ch={}", (configuration.channel == PartConfiguration::omniChannel + ? "OMNI" + : std::to_string(configuration.channel + 1))); } // TODO: Multiple outputs @@ -196,4 +204,7 @@ struct Part : MoveableOnly, SampleRateSupport }; } // namespace scxt::engine +SC_DESCRIBE(scxt::engine::Part::PartConfiguration, + SC_FIELD(channel, pmd().asInt().withRange(-1, 15));); + #endif \ No newline at end of file diff --git a/src/json/engine_traits.h b/src/json/engine_traits.h index 54e5b322..30852ef2 100644 --- a/src/json/engine_traits.h +++ b/src/json/engine_traits.h @@ -137,16 +137,33 @@ SC_STREAMDEF(scxt::engine::Macro, SC_FROM({ findOrSet(v, "nm", scxt::engine::Macro::defaultNameFor(result.index), result.name); })); +SC_STREAMDEF( + scxt::engine::Part::PartConfiguration, + SC_FROM(v = {{"a", from.active}, {"c", from.channel}, {"m", from.mute}, {"s", from.solo}};), + SC_TO({ + findOrSet(v, "c", scxt::engine::Part::PartConfiguration::omniChannel, to.channel); + findOrSet(v, "a", true, to.active); + findOrSet(v, "m", false, to.mute); + findOrSet(v, "s", false, to.solo); + })); + SC_STREAMDEF( scxt::engine::Part, SC_FROM({ // TODO: Do a non-empty part stream with the If variant - v = {{"channel", from.channel}, {"groups", from.getGroups()}, {"macros", from.macros}}; + v = {{"config", from.configuration}, {"groups", from.getGroups()}, {"macros", from.macros}}; }), SC_TO({ auto &part = to; part.clearGroups(); - findIf(v, "channel", part.channel); + if (SC_UNSTREAMING_FROM_PRIOR_TO(0x2024'08'18)) + { + findIf(v, "channel", part.configuration.channel); + } + else + { + findIf(v, "config", part.configuration); + } findIf(v, "macros", part.macros); auto vzones = v.at("groups").get_array(); for (const auto vz : vzones) diff --git a/src/messaging/client/client_serial.h b/src/messaging/client/client_serial.h index 32bd8be9..ca358946 100644 --- a/src/messaging/client/client_serial.h +++ b/src/messaging/client/client_serial.h @@ -126,6 +126,8 @@ enum ClientToSerializationMessagesIds c2s_set_othertab_selection, + c2s_send_full_part_config, + num_clientToSerializationMessages }; @@ -151,6 +153,7 @@ enum SerializationToClientMessageIds s2c_respond_single_processor_metadata_and_data, s2c_notify_mismatched_processors_for_zone, + s2c_send_part_configuration, s2c_send_selected_part, s2c_send_selected_group_zone_mapping_summary, s2c_send_selection_state, diff --git a/src/messaging/client/interaction_messages.h b/src/messaging/client/interaction_messages.h index e818bc37..6b9ccbab 100644 --- a/src/messaging/client/interaction_messages.h +++ b/src/messaging/client/interaction_messages.h @@ -54,7 +54,7 @@ inline void processMidiFromGUI(const noteOnOff_t &g, const engine::Engine &engin return; auto p = sel; - auto ch = engine.getPatch()->getPart(p)->channel; + auto ch = engine.getPatch()->getPart(p)->configuration.channel; if (ch < 0) ch = 0; diff --git a/src/messaging/client/part_messages.h b/src/messaging/client/part_messages.h index 2bb4e236..987b7ec1 100644 --- a/src/messaging/client/part_messages.h +++ b/src/messaging/client/part_messages.h @@ -37,5 +37,20 @@ namespace scxt::messaging::client SERIAL_TO_CLIENT(SelectedPart, s2c_send_selected_part, int16_t, onSelectedPart); SERIAL_TO_CLIENT(SelectedGroupZoneMappingSummary, s2c_send_selected_group_zone_mapping_summary, engine::Part::zoneMappingSummary_t, onGroupZoneMappingSummary); + +using partConfigurationPayload_t = std::pair; +SERIAL_TO_CLIENT(SendPartConfiguration, s2c_send_part_configuration, partConfigurationPayload_t, + onPartConfiguration); + +inline void updatePartFullConfig(const partConfigurationPayload_t &p, const engine::Engine &e, + messaging::MessageController &cont) +{ + auto [pt, conf] = p; + cont.scheduleAudioThreadCallback([part = pt, configuration = conf](auto &eng) { + eng.getPatch()->getPart(part)->configuration = configuration; + }); +} +CLIENT_TO_SERIAL(UpdatePartFullConfig, c2s_send_full_part_config, partConfigurationPayload_t, + updatePartFullConfig(payload, engine, cont)); } // namespace scxt::messaging::client #endif // SHORTCIRCUITXT_PART_MESSAGES_H