From 709ebb1fd04eb481c0a0a346438c282fef0404f5 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 17 Aug 2024 13:32:18 -0400 Subject: [PATCH] Tab State in Selection manager (#1134) The tab states (play/mix/multi, but also internally like selected LFO, selected bus) are saved in the selection manager and we have a way to do this for future tabbed things also which is modertaly non-tedious Also go to non-specular knobs CLoses #1112 --- libs/sst/sst-jucegui | 2 +- src-ui/components/HeaderRegion.cpp | 1 + src-ui/components/MixerScreen.cpp | 14 +++++++ src-ui/components/MixerScreen.h | 2 + src-ui/components/MultiScreen.cpp | 38 ++++++++++++++++++ src-ui/components/MultiScreen.h | 5 +++ src-ui/components/SCXTEditor.cpp | 18 +++++++++ src-ui/components/SCXTEditor.h | 5 +++ .../components/SCXTEditorResponseHandlers.cpp | 39 +++++++++++++++++++ src-ui/components/multi/LFOPane.cpp | 3 ++ src-ui/components/multi/MappingPane.cpp | 22 ++++++++--- src-ui/components/multi/MappingPane.h | 1 + src-ui/components/multi/PartGroupSidebar.cpp | 34 ++++++++++------ src-ui/components/multi/PartGroupSidebar.h | 2 + src/engine/engine.cpp | 1 + src/messaging/client/client_serial.h | 3 ++ src/messaging/client/selection_messages.h | 11 ++++++ src/selection/selection_manager.cpp | 6 +++ src/selection/selection_manager.h | 4 +- 19 files changed, 192 insertions(+), 19 deletions(-) diff --git a/libs/sst/sst-jucegui b/libs/sst/sst-jucegui index ee30fb54..91ce04b6 160000 --- a/libs/sst/sst-jucegui +++ b/libs/sst/sst-jucegui @@ -1 +1 @@ -Subproject commit ee30fb5497f466a8b818908712ef32951f7f8bb9 +Subproject commit 91ce04b6ffc7ad163b18b671425ee3a66de2acde diff --git a/src-ui/components/HeaderRegion.cpp b/src-ui/components/HeaderRegion.cpp index 31ee0555..7d09de53 100644 --- a/src-ui/components/HeaderRegion.cpp +++ b/src-ui/components/HeaderRegion.cpp @@ -336,4 +336,5 @@ void HeaderRegion::showMultiSelectionMenu() }); p.showMenuAsync(editor->defaultPopupMenuOptions(multiMenuButton.get())); } + } // namespace scxt::ui \ No newline at end of file diff --git a/src-ui/components/MixerScreen.cpp b/src-ui/components/MixerScreen.cpp index 9d769b93..469739bf 100644 --- a/src-ui/components/MixerScreen.cpp +++ b/src-ui/components/MixerScreen.cpp @@ -114,6 +114,7 @@ void MixerScreen::selectBus(int index) b->setSelected(false); } busPane->channelStrips[index]->selected = true; + editor->setTabSelection("mixer_screen", std::to_string(index)); repaint(); } @@ -197,4 +198,17 @@ void MixerScreen::setVULevelForBusses( } } +void MixerScreen::onOtherTabSelection() +{ + auto bs = editor->queryTabSelection("mixer_screen"); + if (!bs.empty()) + { + auto v = std::atoi(bs.c_str()); + if (v >= 0 && v < busPane->channelStrips.size()) + { + selectBus(v); + } + } +} + } // namespace scxt::ui \ No newline at end of file diff --git a/src-ui/components/MixerScreen.h b/src-ui/components/MixerScreen.h index 3872499b..e636e414 100644 --- a/src-ui/components/MixerScreen.h +++ b/src-ui/components/MixerScreen.h @@ -87,6 +87,8 @@ struct MixerScreen : juce::Component, HasEditor void setVULevelForBusses( const std::array, 2>, engine::Patch::Busses::busCount> &x); + + void onOtherTabSelection(); }; } // namespace scxt::ui #endif // SHORTCIRCUIT_SENDFXSCREEN_H diff --git a/src-ui/components/MultiScreen.cpp b/src-ui/components/MultiScreen.cpp index 172712e5..dabc6e08 100644 --- a/src-ui/components/MultiScreen.cpp +++ b/src-ui/components/MultiScreen.cpp @@ -236,6 +236,44 @@ void MultiScreen::ZoneOrGroupElements::layoutInto(const juce::Rectangle lfo->setBounds(envRect.withWidth(ew * 2).translated(ew * 2, 0)); } +void MultiScreen::onOtherTabSelection() +{ + auto pgz = editor->queryTabSelection(tabKey("multi.pgz")); + if (pgz.empty()) + { + } + if (pgz == "part") + parts->setSelectedTab(0); + else if (pgz == "group") + parts->setSelectedTab(1); + else if (pgz == "zone") + parts->setSelectedTab(2); + else + SCLOG("Unknown multi.pgz key " << pgz); + + auto gts = editor->queryTabSelection(tabKey("multi.group.lfo")); + if (!gts.empty()) + { + auto gt = std::atoi(gts.c_str()); + if (gt >= 0 && gt < lfosPerGroup) + groupElements->lfo->selectTab(gt); + } + auto zts = editor->queryTabSelection(tabKey("multi.zone.lfo")); + if (!zts.empty()) + { + auto zt = std::atoi(zts.c_str()); + if (zt >= 0 && zt < lfosPerZone) + zoneElements->lfo->selectTab(zt); + } + auto mts = editor->queryTabSelection(tabKey("multi.mapping")); + if (!mts.empty()) + { + auto mt = std::atoi(mts.c_str()); + if (mt >= 0 && mt < 3) + sample->setSelectedTab(mt); + } +} + template struct MultiScreen::ZoneOrGroupElements; template struct MultiScreen::ZoneOrGroupElements; } // namespace scxt::ui diff --git a/src-ui/components/MultiScreen.h b/src-ui/components/MultiScreen.h index 47f9a141..8a73fe6c 100644 --- a/src-ui/components/MultiScreen.h +++ b/src-ui/components/MultiScreen.h @@ -131,6 +131,11 @@ struct MultiScreen : juce::Component, HasEditor } selectionMode{SelectionMode::NONE}; void setSelectionMode(SelectionMode m); + void onOtherTabSelection(); + // This allows us, in the future, to make this return s + selected part to have + // part differentiated selection + std::string tabKey(const std::string &s) { return s; } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MultiScreen); }; } // namespace scxt::ui diff --git a/src-ui/components/SCXTEditor.cpp b/src-ui/components/SCXTEditor.cpp index 0c76083f..65a76201 100644 --- a/src-ui/components/SCXTEditor.cpp +++ b/src-ui/components/SCXTEditor.cpp @@ -129,12 +129,14 @@ void SCXTEditor::setActiveScreen(ActiveScreen s) activeScreen = s; aboutScreen->setVisible(false); logScreen->setVisible(false); + std::string val{}; switch (s) { case MULTI: multiScreen->setVisible(true); mixerScreen->setVisible(false); playScreen->setVisible(false); + val = "multi"; resized(); break; @@ -142,6 +144,7 @@ void SCXTEditor::setActiveScreen(ActiveScreen s) multiScreen->setVisible(false); mixerScreen->setVisible(true); playScreen->setVisible(false); + val = "mixer"; resized(); break; @@ -149,9 +152,12 @@ void SCXTEditor::setActiveScreen(ActiveScreen s) multiScreen->setVisible(false); mixerScreen->setVisible(false); playScreen->setVisible(true); + val = "play"; resized(); break; } + + setTabSelection("main_screen", val); repaint(); } @@ -415,4 +421,16 @@ void SCXTEditor::resetColorsFromUserPreferences() themeApplier.recolorStylesheetWith(std::move(cm), style()); } +std::string SCXTEditor::queryTabSelection(const std::string &k) +{ + auto p = otherTabSelection.find(k); + if (p != otherTabSelection.end()) + return p->second; + return {}; +} + +void SCXTEditor::setTabSelection(const std::string &k, const std::string &t) +{ + sendToSerialization(messaging::client::UpdateOtherTabSelection({k, t})); +} } // namespace scxt::ui diff --git a/src-ui/components/SCXTEditor.h b/src-ui/components/SCXTEditor.h index efc5e6b0..98c959f8 100644 --- a/src-ui/components/SCXTEditor.h +++ b/src-ui/components/SCXTEditor.h @@ -201,6 +201,11 @@ struct SCXTEditor : sst::jucegui::components::WindowPanel, juce::DragAndDropCont void onSelectedPart(const int16_t); int16_t getSelectedPart() const; + selection::SelectionManager::otherTabSelection_t otherTabSelection; + void onOtherTabSelection(const scxt::selection::SelectionManager::otherTabSelection_t &p); + std::string queryTabSelection(const std::string &k); + void setTabSelection(const std::string &k, const std::string &t); + void onMixerBusEffectFullData(const scxt::messaging::client::busEffectFullData_t &); void onMixerBusSendData(const scxt::messaging::client::busSendData_t &); diff --git a/src-ui/components/SCXTEditorResponseHandlers.cpp b/src-ui/components/SCXTEditorResponseHandlers.cpp index 8ae21a8f..06029707 100644 --- a/src-ui/components/SCXTEditorResponseHandlers.cpp +++ b/src-ui/components/SCXTEditorResponseHandlers.cpp @@ -288,6 +288,8 @@ void SCXTEditor::onSelectedPart(const int16_t p) multiScreen->parts->selectedPartChanged(); if (multiScreen && multiScreen->sample) multiScreen->sample->selectedPartChanged(); + if (multiScreen) + multiScreen->onOtherTabSelection(); repaint(); } @@ -350,4 +352,41 @@ void SCXTEditor::onMacroValue(const scxt::messaging::client::macroValue_t &s) multiScreen->sample->repaint(); playScreen->repaint(); } + +void SCXTEditor::onOtherTabSelection( + const scxt::selection::SelectionManager::otherTabSelection_t &p) +{ + otherTabSelection = p; + + auto mainScreen = queryTabSelection("main_screen"); + if (mainScreen.empty()) + { + } + else if (mainScreen == "mixer") + { + setActiveScreen(MIXER); + } + else if (mainScreen == "multi") + { + setActiveScreen(MULTI); + } + else if (mainScreen == "play") + { + setActiveScreen(PLAY); + } + else + { + SCLOG("Unknown main screen " << mainScreen); + } + + if (mixerScreen) + { + mixerScreen->onOtherTabSelection(); + } + + if (multiScreen) + { + multiScreen->onOtherTabSelection(); + } +} } // namespace scxt::ui \ No newline at end of file diff --git a/src-ui/components/multi/LFOPane.cpp b/src-ui/components/multi/LFOPane.cpp index 0ea0f8e0..f0be7670 100644 --- a/src-ui/components/multi/LFOPane.cpp +++ b/src-ui/components/multi/LFOPane.cpp @@ -40,6 +40,7 @@ // Included so we can have UI-thread exceution for curve rendering #include "modulation/modulators/steplfo.h" +#include "components/MultiScreen.h" namespace scxt::ui::multi { @@ -712,6 +713,8 @@ void LfoPane::tabChanged(int i) { getContentAreaComponent()->removeAllChildren(); rebuildPanelComponents(); + auto kn = std::string("multi") + (forZone ? ".zone.lfo" : ".group.lfo"); + editor->setTabSelection(editor->multiScreen->tabKey(kn), std::to_string(i)); } void LfoPane::setActive(int i, bool b) diff --git a/src-ui/components/multi/MappingPane.cpp b/src-ui/components/multi/MappingPane.cpp index 41893348..79c33d6e 100644 --- a/src-ui/components/multi/MappingPane.cpp +++ b/src-ui/components/multi/MappingPane.cpp @@ -27,6 +27,7 @@ #include "MappingPane.h" #include "components/SCXTEditor.h" +#include "components/MultiScreen.h" #include "components/multi/SingleMacroEditor.h" #include "datamodel/metadata.h" #include "selection/selection_manager.h" @@ -2414,7 +2415,7 @@ struct SampleDisplay : juce::Component, HasEditor for (const auto &entry : fs::directory_iterator(parent_path)) { if (entry.is_regular_file() && - browser::Browser::isLoadableSingleSample(entry.path())) + scxt::browser::Browser::isLoadableSingleSample(entry.path())) { v.push_back(entry.path().u8string()); } @@ -2455,7 +2456,7 @@ struct SampleDisplay : juce::Component, HasEditor void showFileBrowser() { std::string filePattern; - for (auto s : browser::Browser::LoadableFile::singleSample) + for (auto s : scxt::browser::Browser::LoadableFile::singleSample) { filePattern += "*" + s + ","; } @@ -3030,15 +3031,26 @@ MappingPane::MappingPane(SCXTEditor *e) : sst::jucegui::components::NamedPanel(" onTabSelected = [wt = juce::Component::SafePointer(this)](int i) { if (wt) { - wt->sampleDisplay->setVisible(i == 2); - wt->mappingDisplay->setVisible(i == 1); - wt->macroDisplay->setVisible(i == 0); + wt->setSelectedTab(i); } }; } MappingPane::~MappingPane() {} +void MappingPane::setSelectedTab(int i) +{ + if (i < 0 || i > 2) + return; + selectedTab = i; + sampleDisplay->setVisible(i == 2); + mappingDisplay->setVisible(i == 1); + macroDisplay->setVisible(i == 0); + + repaint(); + editor->setTabSelection(editor->multiScreen->tabKey("multi.mapping"), std::to_string(i)); +} + void MappingPane::resized() { mappingDisplay->setBounds(getContentArea()); diff --git a/src-ui/components/multi/MappingPane.h b/src-ui/components/multi/MappingPane.h index 557b5096..65014632 100644 --- a/src-ui/components/multi/MappingPane.h +++ b/src-ui/components/multi/MappingPane.h @@ -55,6 +55,7 @@ struct MappingPane : sst::jucegui::components::NamedPanel, HasEditor void macroDataChanged(int part, int index); void editorSelectionChanged(); void setActive(bool b); + void setSelectedTab(int i); std::unique_ptr mappingDisplay; std::unique_ptr sampleDisplay; diff --git a/src-ui/components/multi/PartGroupSidebar.cpp b/src-ui/components/multi/PartGroupSidebar.cpp index c6b03460..5a660de8 100644 --- a/src-ui/components/multi/PartGroupSidebar.cpp +++ b/src-ui/components/multi/PartGroupSidebar.cpp @@ -364,18 +364,7 @@ PartGroupSidebar::PartGroupSidebar(SCXTEditor *e) if (!w->partSidebar || !w->groupSidebar || !w->zoneSidebar) return; - bool p = (t == 0), g = (t == 1), z = (t == 2); - w->partSidebar->setVisible(p); - w->groupSidebar->setVisible(g); - w->zoneSidebar->setVisible(z); - - w->editor->multiScreen->setSelectionMode(g ? MultiScreen::SelectionMode::GROUP - : MultiScreen::SelectionMode::ZONE); - - if (g) - w->editor->themeApplier.applyGroupMultiScreenTheme(w); - else - w->editor->themeApplier.applyZoneMultiScreenTheme(w); + w->setSelectedTab(t); }; resetTabState(); @@ -455,4 +444,25 @@ void PartGroupSidebar::editorSelectionChanged() } repaint(); } + +void PartGroupSidebar::setSelectedTab(int t) +{ + selectedTab = t; + bool p = (t == 0), g = (t == 1), z = (t == 2); + partSidebar->setVisible(p); + groupSidebar->setVisible(g); + zoneSidebar->setVisible(z); + + editor->multiScreen->setSelectionMode(g ? MultiScreen::SelectionMode::GROUP + : MultiScreen::SelectionMode::ZONE); + + if (g) + editor->themeApplier.applyGroupMultiScreenTheme(this); + else + editor->themeApplier.applyZoneMultiScreenTheme(this); + + editor->setTabSelection(editor->multiScreen->tabKey("multi.pgz"), + (t == 0 ? "part" : (t == 1 ? "group" : "zone"))); + repaint(); +} } // 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 750c28b9..a89c8d8a 100644 --- a/src-ui/components/multi/PartGroupSidebar.h +++ b/src-ui/components/multi/PartGroupSidebar.h @@ -52,6 +52,8 @@ struct PartGroupSidebar : sst::jucegui::components::NamedPanel, HasEditor void selectParts() {} void selectGroups() {} + void setSelectedTab(int t); // part / group / zone + std::unique_ptr zoneSidebar; std::unique_ptr groupSidebar; std::unique_ptr partSidebar; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f7b6ab2f..a09d2d7c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -979,6 +979,7 @@ void Engine::sendFullRefreshToClient() const getSelectionManager()->sendClientDataForLeadSelectionState(); getSelectionManager()->sendSelectedZonesToClient(); getSelectionManager()->sendSelectedPartMacrosToClient(); + getSelectionManager()->sendOtherTabsSelectionToClient(); } void Engine::clearAll() diff --git a/src/messaging/client/client_serial.h b/src/messaging/client/client_serial.h index 0e1afc8d..32bd8be9 100644 --- a/src/messaging/client/client_serial.h +++ b/src/messaging/client/client_serial.h @@ -124,6 +124,8 @@ enum ClientToSerializationMessagesIds c2s_request_host_callback, c2s_macro_begin_end_edit, + c2s_set_othertab_selection, + num_clientToSerializationMessages }; @@ -152,6 +154,7 @@ enum SerializationToClientMessageIds s2c_send_selected_part, s2c_send_selected_group_zone_mapping_summary, s2c_send_selection_state, + s2c_send_othertab_selection, s2c_bus_effect_full_data, s2c_bus_send_data, diff --git a/src/messaging/client/selection_messages.h b/src/messaging/client/selection_messages.h index 59464295..9a5ee140 100644 --- a/src/messaging/client/selection_messages.h +++ b/src/messaging/client/selection_messages.h @@ -65,6 +65,17 @@ typedef std::tuple, SERIAL_TO_CLIENT(SetSelectionState, s2c_send_selection_state, selectedStateMessage_t, onSelectionState); +SERIAL_TO_CLIENT(SetOtherTabSelection, s2c_send_othertab_selection, + scxt::selection::SelectionManager::otherTabSelection_t, onOtherTabSelection); + +using updateOther_t = std::pair; +inline void doUpdateOtherTabSelection(const updateOther_t &pl, const engine::Engine &engine) +{ + engine.getSelectionManager()->otherTabSelection[pl.first] = pl.second; +} +CLIENT_TO_SERIAL(UpdateOtherTabSelection, c2s_set_othertab_selection, updateOther_t, + doUpdateOtherTabSelection(payload, engine)); + // Begin and End Edit messages. These are a mess. See #775 using editGestureFor_t = bool; inline void doBeginEndEdit(bool isBegin, const editGestureFor_t &payload, diff --git a/src/selection/selection_manager.cpp b/src/selection/selection_manager.cpp index 521c06c8..bf6ff78b 100644 --- a/src/selection/selection_manager.cpp +++ b/src/selection/selection_manager.cpp @@ -748,6 +748,12 @@ void SelectionManager::sendSelectedPartMacrosToClient() *(engine.getMessageController())); } +void SelectionManager::sendOtherTabsSelectionToClient() +{ + serializationSendToClient(cms::s2c_send_othertab_selection, otherTabSelection, + *(engine.getMessageController())); +} + void SelectionManager::clearAllSelections() { for (auto &s : allSelectedZones) diff --git a/src/selection/selection_manager.h b/src/selection/selection_manager.h index 30f4f673..8f1ef9e9 100644 --- a/src/selection/selection_manager.h +++ b/src/selection/selection_manager.h @@ -188,6 +188,7 @@ struct SelectionManager void sendDisplayDataForSingleGroup(int part, int group); void sendDisplayDataForNoGroupSelected(); void sendSelectedPartMacrosToClient(); + void sendOtherTabsSelectionToClient(); void configureAndSendZoneModMatrixMetadata(int p, int g, int z); // To ponder. Does this belong on this object or the engine? @@ -196,7 +197,8 @@ struct SelectionManager std::set processorTypesForSelectedZones(int pidx); public: - std::unordered_map otherTabSelection; + using otherTabSelection_t = std::unordered_map; + otherTabSelection_t otherTabSelection; std::array allSelectedZones, allSelectedGroups; std::array leadZone, leadGroup; };