Skip to content

Commit

Permalink
[SUTK] Added Theme Inheritance support using [Base("<theme name>")] a…
Browse files Browse the repository at this point in the history
…ttribute on Theme block
  • Loading branch information
ravi688 committed Dec 16, 2024
1 parent 807a072 commit f9b512c
Show file tree
Hide file tree
Showing 12 changed files with 474 additions and 52 deletions.
2 changes: 1 addition & 1 deletion dependencies/Common
146 changes: 117 additions & 29 deletions sutk/include/sutk/ThemeManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace SUTK
BindID bind(const KeyViewType& key, ValueChangeCallback<ValueType> callback) noexcept;
void unbind(const KeyViewType& key, BindID id) noexcept;
template<typename ValueType>
const ValueType& getValue(Type type, const KeyViewType& key) noexcept;
const com::OptionalReference<ValueType> getValue(Type type, const KeyViewType& key) noexcept;
void applyTheme(Theme<KeyType, KeyViewType>* theme) noexcept;
Theme<KeyType, KeyViewType>* getCurrentTheme() noexcept { return m_currentTheme; }
com::Bool verifyKey(const KeyViewType& key, Type type) noexcept
Expand All @@ -65,32 +65,74 @@ namespace SUTK
void dump() const noexcept;
};

template<typename KeyType, com::ViewType<KeyType> KeyViewType>
class SUTK_API ThemeManager;

template<typename KeyType, com::ViewType<KeyType> KeyViewType>
class SUTK_API Theme : public UIDriverObject
{
public:
using ThemeInterfaceType = ThemeInterface<KeyType, KeyViewType>;
using ThemeInterfaceList = std::vector<ThemeInterfaceType*>;
using Colors = com::unordered_map<KeyType, Color4, KeyViewType>;
using Images = com::unordered_map<KeyType, UIDriver::ImageReference, KeyViewType>;
using Fonts = com::unordered_map<KeyType, UIDriver::FontReference, KeyViewType>;
using Floats = com::unordered_map<KeyType, f32>;
using Strings = com::unordered_map<KeyType, std::string>;
private:
com::unordered_map<KeyType, Color4, KeyViewType> m_colors;
com::unordered_map<KeyType, UIDriver::ImageReference, KeyViewType> m_images;
com::unordered_map<KeyType, UIDriver::FontReference, KeyViewType> m_fonts;
com::unordered_map<KeyType, f32> m_floats;
com::unordered_map<KeyType, std::string> m_strings;
Colors m_colors;
Images m_images;
Fonts m_fonts;
Floats m_floats;
Strings m_strings;
ThemeInterfaceList m_interfaces;

friend class ThemeManager<KeyType, KeyViewType>;

const Colors& getColors() const noexcept { return m_colors; }
const Images& getImages() const noexcept { return m_images; }
const Fonts& getFonts() const noexcept { return m_fonts; }
const Floats& getFloats() const noexcept { return m_floats; }
const Strings& getStrings() const noexcept { return m_strings; }

public:
Theme(UIDriver& driver, std::vector<ThemeInterfaceType*>& interfaces) noexcept : UIDriverObject(driver), m_interfaces(interfaces) { }
Theme(UIDriver& driver, std::vector<ThemeInterfaceType*>&& interfaces) noexcept : UIDriverObject(driver), m_interfaces(std::move(interfaces)) { }
Theme(UIDriver& driver, ThemeInterfaceType* interface) noexcept : Theme(driver, std::vector { interface }) { }

ThemeInterfaceList& getInterfaces() noexcept { return m_interfaces; }

void dump() noexcept
{
std::cout << "theme dump: \n";
std::cout << "\tcolors: \n";
for(auto& pair : m_colors)
std::cout << "\t\t" << pair.first << "\n";
std::cout << "\timages: \n";
for(auto& pair : m_images)
std::cout << "\t\t" << pair.first << "\n";
std::cout << "\tfonts: \n";
for(auto& pair : m_fonts)
std::cout << "\t\t" << pair.first << "\n";
std::cout << "\tfloats: \n";
for(auto& pair : m_floats)
std::cout << "\t\t" << pair.first << "\n";
std::cout << "\tstrings: \n";
for(auto& pair : m_strings)
std::cout << "\t\t" << pair.first << "\n";
}

void apply() noexcept
{
for(auto& interface : m_interfaces)
interface->applyTheme(this);
}

com::Bool hasKey(const KeyViewType& key) noexcept
{
return com::Bool { m_colors.contains(key) || m_images.contains(key) || m_fonts.contains(key) || m_floats.contains(key) || m_strings.contains(key) };
}

com::Bool verifyKey(const KeyViewType& key, ThemeInterfaceType::Type type) noexcept
{
for(auto& interface : m_interfaces)
Expand Down Expand Up @@ -126,23 +168,44 @@ namespace SUTK
}

template<typename ValueType>
void add(const KeyViewType& key, ValueType&& value) noexcept
void add(const KeyViewType& key, ValueType value) noexcept
{
if(!verifyKey(key, getTypeEnum<ValueType>()))
{
DEBUG_LOG_ERROR("Failed to add value into the theme");
return;
}
if constexpr(std::is_same_v<ValueType, Color4>)
m_colors.insert({ KeyType { key }, std::move(value) });
else if constexpr(std::is_same_v<ValueType, UIDriver::ImageReference>)
m_images.insert({ KeyType { key }, std::move(value) });
else if constexpr(std::is_same_v<ValueType, UIDriver::FontReference>)
m_fonts.insert({ KeyType { key }, std::move(value) });
else if constexpr(std::is_same_v<ValueType, f32>)
m_floats.insert({ KeyType { key }, std::move(value) });
else if constexpr(std::is_same_v<ValueType, std::string>)
m_strings.insert({ KeyType { key }, std::move(value) });
else
static_assert(false, "Type not supported");
}
template<typename ValueType>
void addOrSet(const KeyViewType& key, ValueType value) noexcept
{
if(!verifyKey(key, getTypeEnum<ValueType>()))
{
DEBUG_LOG_ERROR("Failed to add value into the theme");
return;
}
if constexpr(std::is_same_v<ValueType, Color4>)
m_colors.insert({ KeyType { key }, std::forward<Color4&&>(value) });
com::insert_or_set(m_colors, KeyType { key }, std::move(value));
else if constexpr(std::is_same_v<ValueType, UIDriver::ImageReference>)
m_images.insert({ KeyType { key }, std::forward<UIDriver::ImageReference&&>(value) });
com::insert_or_set(m_images, KeyType { key }, std::move(value));
else if constexpr(std::is_same_v<ValueType, UIDriver::FontReference>)
m_fonts.insert({ KeyType { key }, std::forward<UIDriver::FontReference&&>(value) });
com::insert_or_set(m_fonts, KeyType { key }, std::move(value));
else if constexpr(std::is_same_v<ValueType, f32>)
m_floats.insert({ KeyType { key }, std::forward<f32&&>(value) });
com::insert_or_set(m_floats, KeyType { key }, std::move(value));
else if constexpr(std::is_same_v<ValueType, std::string>)
m_strings.insert({ KeyType { key }, std::forward<std::string&&>(value) });
com::insert_or_set(m_strings, KeyType { key }, std::move(value));
else
static_assert(false, "Type not supported");
}
Expand All @@ -161,6 +224,21 @@ namespace SUTK
else
DEBUG_LOG_ERROR("Unrecognized image file extension in path: %.*s", view.length(), view.data());
}
template<typename ValueType> requires(com::SameAsAny<ValueType, UIDriver::ImageReference, UIDriver::FontReference>)
void addOrSet(const KeyViewType& key, std::string_view view) noexcept
{
if(view.ends_with(".png") || view.ends_with(".jpg") || view.ends_with(".bmp"))
{
UIDriver::ImageReference image = getUIDriver().loadImage(view);
com::insert_or_set(m_images, KeyType { key }, image);
} else if(view.ends_with(".ttf"))
{
UIDriver::FontReference font = getUIDriver().loadFont(view);
com::insert_or_set(m_fonts, KeyType { key }, font);
}
else
DEBUG_LOG_ERROR("Unrecognized image file extension in path: %.*s", view.length(), view.data());
}
template<typename ValueType>
void remove(const KeyViewType& key) noexcept
{
Expand All @@ -180,20 +258,21 @@ namespace SUTK
_com_assert(result);
}
template<typename ValueType>
ValueType& getValue(const KeyViewType& key) noexcept
const com::OptionalReference<ValueType> getValue(const KeyViewType& key) noexcept
{
if constexpr(std::is_same_v<ValueType, Color4>)
return com::find_value(m_colors, key);
return com::try_find_value(m_colors, key);
else if constexpr(std::is_same_v<ValueType, UIDriver::ImageReference>)
return com::find_value(m_images, key);
return com::try_find_value(m_images, key);
else if constexpr(std::is_same_v<ValueType, UIDriver::FontReference>)
return com::find_value(m_fonts, key);
return com::try_find_value(m_fonts, key);
else if constexpr(std::is_same_v<ValueType, f32>)
return com::find_value(m_floats, key);
return com::try_find_value(m_floats, key);
else if constexpr(std::is_same_v<ValueType, std::string>)
return com::find_value(m_strings, key);
return com::try_find_value(m_strings, key);
else
static_assert(false, "Type not supported");
return { };
}
};

Expand Down Expand Up @@ -241,7 +320,10 @@ namespace SUTK
{
auto fn = [_callback = std::move(callback), key, this]()
{
_callback(this->m_currentTheme->template getValue<ValueType>(key));
// Only invoke the callback if the theme sets value for the key to which this callback binds to.
auto value = this->m_currentTheme->template getValue<ValueType>(key);
if(value)
_callback(*value);
};
if(m_currentTheme)
fn();
Expand All @@ -263,22 +345,18 @@ namespace SUTK

template<typename KeyType, com::ViewType<KeyType> KeyViewType>
template<typename ValueType>
const ValueType& ThemeInterface<KeyType, KeyViewType>::getValue(Type type, const KeyViewType& key) noexcept
const com::OptionalReference<ValueType> ThemeInterface<KeyType, KeyViewType>::getValue(Type type, const KeyViewType& key) noexcept
{
return m_currentTheme->template getValue<ValueType>(key);
}

template<typename KeyType, com::ViewType<KeyType> KeyViewType>
void ThemeInterface<KeyType, KeyViewType>::applyTheme(Theme<KeyType, KeyViewType>* theme) noexcept
{
if(m_currentTheme == theme)
{
debug_log_warning("You're trying to apply the same theme again, ignored");
return;
}
m_currentTheme = theme;
for(auto& keyValuePair : m_defs)
keyValuePair.second.second.publish();
if(m_currentTheme->hasKey(keyValuePair.first))
keyValuePair.second.second.publish();
m_onThemeChange.publish();
}

Expand Down Expand Up @@ -359,7 +437,8 @@ namespace SUTK
}
ThemeInterfaceType* getThemeInterface(const KeyViewType& key) noexcept
{
return com::find_value(m_themeInterfaces, key);
auto value = com::try_find_value(m_themeInterfaces, key);
return value ? *value : com::null_pointer<ThemeInterfaceType>();
}
void destroyThemeInterface(const KeyViewType& key) noexcept
{
Expand All @@ -371,7 +450,7 @@ namespace SUTK
{
return createTheme(key, std::vector { interface });
}
template<typename InitVectorType> requires(std::same_as<InitVectorType, std::vector<ThemeInterfaceType*>>)
template<typename InitVectorType> requires(com::SameAsAny<InitVectorType, std::vector<ThemeInterfaceType*>, std::vector<ThemeInterfaceType*>&>)
ThemeType* createTheme(const KeyViewType& key, InitVectorType&& interfaces) noexcept
{
_com_assert(!m_themes.contains(key));
Expand All @@ -382,12 +461,21 @@ namespace SUTK
ThemeType* loadTheme(const std::string_view filePath) noexcept;
ThemeType* getTheme(const KeyViewType& key) noexcept
{
return com::find_value(m_themes, key);
auto value = com::try_find_value(m_themes, key);
return value ? *value : com::null_pointer<ThemeType>();
}
void destroyTheme(const KeyViewType& key) noexcept
{
bool result = com::find_erase(m_themes, key);
_com_assert(result);
}

void dumpThemes() noexcept
{
std::cout << "Dump Theme Start: \n";
for(auto& pair : m_themes)
std::cout << "Theme: " << pair.first << "\n";
std::cout << "Dump Theme End" << std::endl;
}
};
}
12 changes: 12 additions & 0 deletions sutk/include/sutk/defines.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,12 @@ namespace SUTK
static constexpr Color3 yellow() noexcept { return { 255, 255, 0 }; }
};

static CAN_BE_UNUSED_FUNCTION std::ostream& operator <<(std::ostream& stream, Color3 value) noexcept
{
stream << "{ " << static_cast<u32>(value.r) << ", " << static_cast<u32>(value.g) << ", " << static_cast<u32>(value.b) << " }";
return stream;
}


struct Color4
{
Expand Down Expand Up @@ -613,6 +619,12 @@ namespace SUTK
}
};

static CAN_BE_UNUSED_FUNCTION std::ostream& operator <<(std::ostream& stream, Color4 value) noexcept
{
stream << "{ " << static_cast<u32>(value.r) << ", " << static_cast<u32>(value.g) << ", " << static_cast<u32>(value.b) << ", " << static_cast<u32>(value.b) << " }";
return stream;
}

typedef Rect2D<f32> Rect2Df;
typedef Vec2D<f32> Vec2Df;
typedef Vec3D<f32> Vec3Df;
Expand Down
35 changes: 35 additions & 0 deletions sutk/include/sutk/tests/BaseThemeTest.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <sutk/ITest.hpp>

#include <sge-cpp/sge.hpp>

#include <sutk/UIDriver.hpp>

namespace SUTK
{
class IGfxDriver;

class BaseThemeTest : public ITest
{
private:
UIDriver* m_uiDriver;
IGfxDriver* m_gfxDriver;
IInputDriver* m_inputDriver;
RenderImage* m_renderRect;
RenderableContainer* m_renderRectContainer;

public:
BaseThemeTest() : m_uiDriver(NULL), m_gfxDriver(NULL), m_inputDriver(NULL) { }

TestInitializationData getInitializationData() override;

void initialize(SGE::Driver& driver) override;

void terminate(SGE::Driver& driver) override;

void render(SGE::Driver& driver) override;

void update(SGE::Driver& driver, float deltaTime) override;
};
}
4 changes: 3 additions & 1 deletion sutk/source/ITest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <sutk/tests/FontTest.hpp>
#include <sutk/tests/NotebookTest.hpp>
#include <sutk/tests/MultipleThemeModelTest.hpp>
#include <sutk/tests/BaseThemeTest.hpp>

namespace SUTK
{
Expand Down Expand Up @@ -55,7 +56,8 @@ namespace SUTK
{ "FONT", [] () { return std::unique_ptr<ITest>(new FontTest()); }},
{ "NOTEBOOK", [] () { return std::unique_ptr<ITest>(new NotebookTest()); }},
{ "LUNASVG", [] () { return std::unique_ptr<ITest>(new LunaSVGTest()); }},
{ "MULTIPLE_THEME_MODEL", [] () { return std::unique_ptr<ITest>(new MultipleThemeModelTest()); }}
{ "MULTIPLE_THEME_MODEL", [] () { return std::unique_ptr<ITest>(new MultipleThemeModelTest()); }},
{ "BASE_THEME", [] () { return std::unique_ptr<ITest>(new BaseThemeTest()); }}
};

std::unique_ptr<ITest> ITest::Create(const std::string& testName)
Expand Down
Loading

0 comments on commit f9b512c

Please sign in to comment.