Panel holder is a control, which manages multiple windows with associated tabs. This control also known either as Tab container or Notebook widget. It's popular widget so it would be good to have such control in TGUI.
I've made such simple implementation that I current use in own project:
class tgPanelHolder : public tgui::SubwidgetContainer
{
public:
typedef std::shared_ptr<tgPanelHolder> Ptr; //!< Shared widget pointer
typedef std::shared_ptr<const tgPanelHolder> ConstPtr; //!< Shared constant widget pointer
tgPanelHolder(const tgui::Layout2d& size = { "100%", "100%" }) : m_index(-1)
{
m_type = "PanelHolder";
setSize(size);
m_tabs = tgui::Tabs::create();
m_tabs->setSize({ size.x, m_tabs->getSize().y * 2 });
m_tabs->onTabSelect([this]()
{
auto cur = m_tabs->getSelectedIndex();
Select(cur);
if (m_tabs->getSelectedIndex() != m_index)
{
m_tabs->select(m_index);
}
});
m_container->add(m_tabs, "Tabs");
}
static tgPanelHolder::Ptr create(const tgui::Layout2d& size = { "100%", "100%" })
{
return std::make_shared<tgPanelHolder>(size);
}
static tgPanelHolder::Ptr copy(tgPanelHolder::ConstPtr pholder)
{
if (pholder)
return std::static_pointer_cast<tgPanelHolder>(pholder->clone());
else
return nullptr;
}
Widget::Ptr clone() const override
{
return std::make_shared<tgPanelHolder>(*this);
}
void addPanel(tgui::Panel::Ptr ptr, const tgui::String& name, bool select = true)
{
auto size = getSizeLayout();
ptr->setSize({ size.x , size.y - m_tabs->getSize().y });
ptr->setPosition({ tgui::bindLeft(m_tabs), tgui::bindBottom(m_tabs) });
m_panels.push_back(ptr);
m_tabs->add(name, select);
if (select)
{
Select(m_panels.size() - 1, false);
}
}
bool addPanelAt(tgui::Panel::Ptr ptr, const tgui::String& name, std::size_t index, bool select = true)
{
if (index > m_panels.size())
{
return false;
}
addPanel(ptr, name, select);
auto size = m_panels.size();
if (index != size)
{
std::swap(m_panels[index], m_panels[size - 1]);
}
}
void RemovePanel(tgui::Panel::Ptr ptr)
{
if (ptr != nullptr)
{
auto idx = getIndex(ptr);
if (idx != -1)
{
m_tabs->remove(idx);
m_container->remove(m_panels[idx]);
m_panels.erase(m_panels.begin() + idx);
if (idx == 0)
{
Select(m_panels.size() - 1);
}
else
{
Select(idx - 1);
}
}
}
}
void Select(std::size_t index, bool genEvents = true)
{
if (index >= m_panels.size() || index == static_cast<std::size_t>(m_index))
{
return;
}
if (genEvents)
{
bool isVetoed = false;
onSelectionChanging.emit(this, index, &isVetoed);
if (isVetoed)
{
return;
}
}
if (m_container->getWidgets().size() == 2)
{
m_container->remove(m_panels[m_index]);
}
m_container->add(m_panels[index]);
m_tabs->select(index);
m_index = index;
if (genEvents)
{
onSelectionChanged.emit(this, m_index);
}
}
std::size_t Count() const
{
return m_panels.size();
}
int getIndex(tgui::Panel::Ptr ptr)
{
for (std::size_t i = 0; i < m_panels.size(); i++)
{
if (m_panels[i] == ptr)
{
return static_cast<int>(i);
}
}
return -1;
}
tgui::Panel::Ptr getSelected()
{
return getPanel(m_index);
}
int getSelectedIndex() const
{
return m_index;
}
tgui::Panel::Ptr getPanel(int index)
{
if (index < 0 || index >= static_cast<int>(m_panels.size()))
{
return nullptr;
}
return m_panels[index];
}
tgui::Tabs::Ptr getTabs()
{
return m_tabs;
}
tgui::String getTabText(std::size_t index) const
{
return m_tabs->getText(index);
}
bool changeTabText(std::size_t index, const tgui::String& text)
{
return m_tabs->changeText(index, text);
}
public:
tgui::SignalInt onSelectionChanged = { "SelectionChanged" };
tgui::SignalTyped2<int, bool*> onSelectionChanging = { "SelectionChanging" }; //can be vetoed
private:
std::vector<tgui::Panel::Ptr> m_panels;
int m_index;
tgui::Tabs::Ptr m_tabs;
};
If it's OK - I'm going to open PR with proper changes (code style, tests, ...)