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:
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