gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 62d05be594151bb97a00684cb37d74d203069376
parent ead1ea3f9688ede41a4a2219e977ce8d9ae5d4c7
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Sat, 14 May 2022 01:01:03 +0200

add support for generic tab groups

Diffstat:
Msource/jucePlugin/ui3/Tabs.cpp | 35++++++-----------------------------
Msource/jucePlugin/ui3/Tabs.h | 13++++---------
Msource/jucePlugin/ui3/VirusEditor.cpp | 6+++++-
Msource/juceUiLib/CMakeLists.txt | 1+
Msource/juceUiLib/editor.cpp | 11+++++++++++
Msource/juceUiLib/editor.h | 19+++++++++++++++++++
Asource/juceUiLib/tabgroup.cpp | 36++++++++++++++++++++++++++++++++++++
Asource/juceUiLib/tabgroup.h | 42++++++++++++++++++++++++++++++++++++++++++
Msource/juceUiLib/uiObject.cpp | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msource/juceUiLib/uiObject.h | 5+++++
10 files changed, 180 insertions(+), 39 deletions(-)

diff --git a/source/jucePlugin/ui3/Tabs.cpp b/source/jucePlugin/ui3/Tabs.cpp @@ -4,35 +4,12 @@ namespace genericVirusUI { - Tabs::Tabs(VirusEditor& _editor): m_editor(_editor) + Tabs::Tabs(const VirusEditor& _editor) + : genericUI::TabGroup("pages" + , {"page_osc", "page_lfo", "page_fx", "page_arp", "page_presets"} + , {"TabOsc", "TabLfo", "TabEffects", "TabArp", "Presets"} + ) { - m_tabs.push_back(m_editor.findComponent("page_osc")); - m_tabs.push_back(m_editor.findComponent("page_lfo")); - m_tabs.push_back(m_editor.findComponent("page_fx")); - m_tabs.push_back(m_editor.findComponent("page_arp")); - m_tabs.push_back(m_editor.findComponent("page_presets")); - - m_tabButtons.push_back(m_editor.findComponentT<juce::Button>("TabOsc")); - m_tabButtons.push_back(m_editor.findComponentT<juce::Button>("TabLfo")); - m_tabButtons.push_back(m_editor.findComponentT<juce::Button>("TabEffects")); - m_tabButtons.push_back(m_editor.findComponentT<juce::Button>("TabArp")); - m_tabButtons.push_back(m_editor.findComponentT<juce::Button>("Presets")); - - if(m_tabs.size() != m_tabButtons.size()) - throw std::runtime_error("Number of tabs does not match number of tab buttons, not all requested objects have been found"); - - for(size_t i=0; i<m_tabButtons.size(); ++i) - m_tabButtons[i]->onClick = [this, i] { setPage(i); }; - - setPage(0); - } - - void Tabs::setPage(const size_t _page) const - { - for(size_t i=0; i<m_tabs.size(); ++i) - { - m_tabs[i]->setVisible(_page == i); - m_tabButtons[i]->setToggleState(_page == i, juce::dontSendNotification); - } + create(_editor); } } diff --git a/source/jucePlugin/ui3/Tabs.h b/source/jucePlugin/ui3/Tabs.h @@ -2,6 +2,8 @@ #include <vector> +#include "../../juceUiLib/tabgroup.h" + namespace juce { class Button; @@ -12,16 +14,9 @@ namespace genericVirusUI { class VirusEditor; - class Tabs + class Tabs : genericUI::TabGroup { public: - explicit Tabs(VirusEditor& _editor); - private: - void setPage(size_t _page) const; - - VirusEditor& m_editor; - - std::vector<juce::Component*> m_tabs; - std::vector<juce::Button*> m_tabButtons; + explicit Tabs(const VirusEditor& _editor); }; } diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp @@ -21,7 +21,11 @@ namespace genericVirusUI create(_jsonFilename); m_parts.reset(new Parts(*this)); - m_tabs.reset(new Tabs(*this)); + + // be backwards compatible with old skins + if(getTabGroupCount() == 0) + m_tabs.reset(new Tabs(*this)); + m_midiPorts.reset(new MidiPorts(*this)); m_fxPage.reset(new FxPage(*this)); m_patchBrowser.reset(new PatchBrowser(*this)); diff --git a/source/juceUiLib/CMakeLists.txt b/source/juceUiLib/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES labelStyle.cpp labelStyle.h textbuttonStyle.cpp textbuttonStyle.h hyperlinkbuttonStyle.cpp hyperlinkbuttonStyle.h + tabgroup.cpp tabgroup.h uiObject.cpp uiObject.h uiObjectStyle.cpp uiObjectStyle.h ) diff --git a/source/juceUiLib/editor.cpp b/source/juceUiLib/editor.cpp @@ -56,6 +56,7 @@ namespace genericUI } m_rootObject->createJuceTree(*this); + m_rootObject->createTabGroups(*this); m_scale = m_rootObject->getPropertyFloat("scale", 1.0f); } @@ -156,6 +157,16 @@ namespace genericUI } } + void Editor::registerTabGroup(TabGroup* _group) + { + const auto& n = _group->getName(); + + if(m_tabGroupsByName.find(n) != m_tabGroupsByName.end()) + throw std::runtime_error("tab group " + n + " is already defined"); + + m_tabGroupsByName.insert(std::make_pair(n, _group)); + } + const std::vector<juce::Component*>& Editor::findComponents(const std::string& _name, uint32_t _expectedCount/* = 0*/) const { const auto it = m_componentsByName.find(_name); diff --git a/source/juceUiLib/editor.h b/source/juceUiLib/editor.h @@ -26,6 +26,7 @@ namespace genericUI const juce::Font& getFont(const std::string& _fontFile); void registerComponent(const std::string& _name, juce::Component* _component); + void registerTabGroup(TabGroup* _group); EditorInterface& getInterface() const { return m_interface; } @@ -54,6 +55,23 @@ namespace genericUI float getScale() const { return m_scale; } + TabGroup* findTabGroup(const std::string& _name, bool _mustExist = true) + { + const auto it = m_tabGroupsByName.find(_name); + if(it == m_tabGroupsByName.end()) + { + if(_mustExist) + throw std::runtime_error("tab group with name " + _name + " not found"); + return nullptr; + } + return it->second; + } + + size_t getTabGroupCount() const + { + return m_tabGroupsByName.size(); + } + private: EditorInterface& m_interface; @@ -65,6 +83,7 @@ namespace genericUI std::unique_ptr<UiObject> m_rootObject; std::map<std::string, std::vector<juce::Component*>> m_componentsByName; + std::map<std::string, TabGroup*> m_tabGroupsByName; float m_scale = 1.0f; }; diff --git a/source/juceUiLib/tabgroup.cpp b/source/juceUiLib/tabgroup.cpp @@ -0,0 +1,36 @@ +#include "tabgroup.h" + +#include "editor.h" + +namespace genericUI +{ + TabGroup::TabGroup(std::string _name, std::vector<std::string> _pageNames, std::vector<std::string> _buttonNames) + : m_name(std::move(_name)) + , m_pageNames(std::move(_pageNames)) + , m_buttonNames(std::move(_buttonNames)) + { + } + + void TabGroup::create(const Editor& _editor) + { + for (const auto& pageName : m_pageNames) + m_tabs.push_back(_editor.findComponent(pageName)); + + for (const auto& buttonName : m_buttonNames) + m_tabButtons.push_back(_editor.findComponentT<juce::Button>(buttonName)); + + for(size_t i=0; i<m_tabButtons.size(); ++i) + m_tabButtons[i]->onClick = [this, i] { setPage(i); }; + + setPage(0); + } + + void TabGroup::setPage(const size_t _page) const + { + for(size_t i=0; i<m_tabs.size(); ++i) + { + m_tabs[i]->setVisible(_page == i); + m_tabButtons[i]->setToggleState(_page == i, juce::dontSendNotification); + } + } +} diff --git a/source/juceUiLib/tabgroup.h b/source/juceUiLib/tabgroup.h @@ -0,0 +1,42 @@ +#pragma once + +#include <string> +#include <vector> + +namespace juce +{ + class Button; + class Component; +} + +namespace genericUI +{ + class Editor; + + class TabGroup + { + public: + TabGroup() = default; + TabGroup(std::string _name, std::vector<std::string> _pageNames, std::vector<std::string> _buttonNames); + + bool isValid() const + { + return !m_name.empty() && !m_buttonNames.empty() && m_buttonNames.size() == m_pageNames.size(); + } + + void create(const Editor& _editor); + + const std::string& getName() const { return m_name; } + + private: + void setPage(const size_t _page) const; + + std::string m_name; + + std::vector<std::string> m_pageNames; + std::vector<std::string> m_buttonNames; + + std::vector<juce::Component*> m_tabs; + std::vector<juce::Button*> m_tabButtons; + }; +} diff --git a/source/juceUiLib/uiObject.cpp b/source/juceUiLib/uiObject.cpp @@ -50,6 +50,20 @@ namespace genericUI } } + void UiObject::createTabGroups(Editor& _editor) + { + if(m_tabGroup.isValid()) + { + m_tabGroup.create(_editor); + _editor.registerTabGroup(&m_tabGroup); + } + + for (auto& ch : m_children) + { + ch->createTabGroups(_editor); + } + } + void UiObject::apply(Editor& _editor, juce::Component& _target) const { const auto x = getPropertyInt("x"); @@ -240,6 +254,43 @@ namespace genericUI } } } + else if(key == "tabgroup") + { + auto buttons = value["buttons"].getArray(); + auto pages = value["pages"].getArray(); + auto name = value["name"].toString().toStdString(); + + if(name.empty()) + throw std::runtime_error("tab group needs to have a name"); + if(buttons == nullptr) + throw std::runtime_error("tab group needs to define at least one button but 'buttons' array not found"); + if(pages == nullptr) + throw std::runtime_error("tab group needs to define at least one page but 'pages' array not found"); + + std::vector<std::string> buttonVec; + std::vector<std::string> pagesVec; + + for (const auto& button : *buttons) + { + const auto b = button.toString().toStdString(); + if(b.empty()) + throw std::runtime_error("tab group button name must not be empty"); + buttonVec.push_back(b); + } + + for (const auto& page : *pages) + { + const auto p = page.toString().toStdString(); + if(p.empty()) + throw std::runtime_error("tab group page name must not be empty"); + pagesVec.push_back(p); + } + + if(buttonVec.size() != pagesVec.size()) + throw std::runtime_error("tab group page count must match tap group button count"); + + m_tabGroup = TabGroup(name, pagesVec, buttonVec); + } else { auto* componentDesc = value.getDynamicObject(); diff --git a/source/juceUiLib/uiObject.h b/source/juceUiLib/uiObject.h @@ -6,6 +6,8 @@ #include <string> #include <vector> +#include "tabgroup.h" + namespace juce { class HyperlinkButton; @@ -34,6 +36,7 @@ namespace genericUI void createJuceTree(Editor& _editor) const; void createChildObjects(Editor& _editor, juce::Component& _parent) const; + void createTabGroups(Editor& _editor); void apply(Editor& _editor, juce::Component& _target) const; void apply(Editor& _editor, juce::Slider& _target); @@ -71,6 +74,8 @@ namespace genericUI std::vector<std::unique_ptr<juce::Component>> m_juceObjects; std::unique_ptr<UiObjectStyle> m_style; + + TabGroup m_tabGroup; }; inline bool UiObject::hasComponent(const std::string& _component) const