Skip to content

Commit

Permalink
Added replace function to Theme to copy one theme into another one wh…
Browse files Browse the repository at this point in the history
…ile also changing all widgets connected to the theme
  • Loading branch information
texus committed Jan 10, 2024
1 parent e5e8975 commit eb2d154
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 1 deletion.
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
TGUI 1.2 (TBD)
---------------

- Added changeItem function to TreeView
- Added Theme::replace function
- Added TreeView::changeItem function
- Added ignoreMouseEvents function to canvas widgets
- Replaced getWidgetAtPosition with getWidgetAtPos
- getWidgetBelowMouseCursor was given a parameter for recursive search
Expand Down
13 changes: 13 additions & 0 deletions include/TGUI/Loading/Theme.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ TGUI_MODULE_EXPORT namespace tgui
void load(const String& primary);


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Replaced this theme with another one, while updating all connected widgets to the new renderers
///
/// @param otherTheme The theme to copy
///
/// The renderers are copied, meaning that all widgets connected to the other theme will remain connected to it.
/// Any widgets connected to this theme will however be updated with new renderers when the same name is encountered.
///
/// @since TGUI 1.2
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void replace(const Theme& otherTheme);


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Gets data for the renderers
///
Expand Down
67 changes: 67 additions & 0 deletions src/Loading/Theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,71 @@ namespace tgui

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Theme::replace(const Theme& otherTheme)
{
m_primary = otherTheme.m_primary;
m_globalProperties = otherTheme.m_globalProperties;

// Replace the existing renderers
auto existingRendererIt = m_renderers.begin();
while (existingRendererIt != m_renderers.end())
{
const auto& id = existingRendererIt->first;
auto& existingRenderer = existingRendererIt->second;

// If the renderer doesn't exist in the other theme then disconnect the old renderer from this theme.
// Widgets using those old renderers will remain unmodified, but they will no longer be connected to this theme.
auto rendererToCopyIt = otherTheme.m_renderers.find(id);
if ((rendererToCopyIt == otherTheme.m_renderers.end()) && !m_themeLoader->canLoad(m_primary, id))
{
if (existingRenderer->connectedTheme == this)
existingRenderer->connectedTheme = nullptr;

existingRendererIt = m_renderers.erase(existingRendererIt);
continue;
}

auto newRenderer = RendererData::create();
newRenderer->connectedTheme = this;
newRenderer->observers = std::move(existingRenderer->observers);

if (rendererToCopyIt != otherTheme.m_renderers.end())
newRenderer->propertyValuePairs = rendererToCopyIt->second->propertyValuePairs;
else
{
const auto& properties = m_themeLoader->load(m_primary, id);
for (const auto& property : properties)
newRenderer->propertyValuePairs[property.first] = ObjectConverter(property.second);
}

existingRenderer = newRenderer;

// Update the existing widgets that were using the previous renderer from this theme
for (auto& observer : newRenderer->observers)
observer->setRenderer(newRenderer);

++existingRendererIt;
}

// Add the renderers that only existed in the other renderers (e.g. added via the addRenderer function)
for (const auto& otherRendererPair : otherTheme.m_renderers)
{
const auto& id = otherRendererPair.first;
const auto& otherRenderer = otherRendererPair.second;

auto rendererIt = m_renderers.find(id);
if (rendererIt != m_renderers.end())
continue; // We already have the renderer, it would have been handled by the earlier loop

auto newRenderer = RendererData::create();
newRenderer->connectedTheme = this;
newRenderer->propertyValuePairs = otherRenderer->propertyValuePairs;
m_renderers[id] = newRenderer;
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::shared_ptr<RendererData> Theme::getRenderer(const String& id)
{
// If we already have this renderer in cache then just return it
Expand Down Expand Up @@ -717,6 +782,8 @@ namespace tgui
if (!renderer)
renderer = RendererData::create();

// If a renderer with the same id already existed then disconnect the old renderer from this theme.
// Widgets using the old renderer would thus remain unmodified.
auto existingRendererIt = m_renderers.find(id);
if (existingRendererIt != m_renderers.end())
{
Expand Down
34 changes: 34 additions & 0 deletions tests/Loading/Theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,40 @@ TEST_CASE("[Theme]")
REQUIRE(!theme.removeRenderer("nonexistent"));
}

SECTION("Replace")
{
auto data1 = std::make_shared<tgui::RendererData>();
data1->propertyValuePairs["TextColor"] = {tgui::Color(255, 0, 0)};

auto data2 = std::make_shared<tgui::RendererData>();
data2->propertyValuePairs["TextColor"] = {tgui::Color(0, 255, 0)};

auto data3 = std::make_shared<tgui::RendererData>();
data3->propertyValuePairs["TextColor"] = {tgui::Color(0, 0, 255)};

auto data4 = std::make_shared<tgui::RendererData>();
data4->propertyValuePairs["TextColor"] = {tgui::Color(255, 255, 0)};

tgui::Theme theme1;
theme1.addRenderer("A", data1);
theme1.addRenderer("B", data2);

tgui::Theme theme2;
theme2.addRenderer("B", data3);
theme2.addRenderer("C", data4);

REQUIRE(theme1.getRenderer("A")->propertyValuePairs["TextColor"].getColor() == tgui::Color(255, 0, 0));
REQUIRE(theme1.getRenderer("B")->propertyValuePairs["TextColor"].getColor() == tgui::Color(0, 255, 0));
REQUIRE(theme1.getRenderer("C")->propertyValuePairs.empty());

theme1.replace(theme2);
REQUIRE(theme1.getRenderer("B")->propertyValuePairs["TextColor"].getColor() == tgui::Color(0, 0, 255));
REQUIRE(theme1.getRenderer("C")->propertyValuePairs["TextColor"].getColor() == tgui::Color(255, 255, 0));

REQUIRE(theme1.getRenderer("B")->connectedTheme == &theme1);
REQUIRE(theme2.getRenderer("B")->connectedTheme == &theme2);
}

SECTION("Renderers are shared")
{
tgui::Theme theme{"resources/Black.txt"};
Expand Down

0 comments on commit eb2d154

Please sign in to comment.