Added the ability to load Gui themes from a file
This commit is contained in:
parent
45c36339ec
commit
f33b6de559
9 changed files with 172 additions and 19 deletions
13
extra/testTheme.txt
Normal file
13
extra/testTheme.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
% Window
|
||||
window.backgroundColor = Color(rgba(40, 0, 0, 255))
|
||||
|
||||
% Label
|
||||
label.textColor = Color(#FFFFFFFF)
|
||||
label.backgroundColor = Color(#FFFF80FF)
|
||||
label.borderColor = Color(#FFFFFFFF)
|
||||
label.fontSize = 30
|
||||
label.borderRadius = 20
|
||||
label.borderWidth = 2
|
||||
label.showBackground = false
|
||||
label.showBorder = false
|
||||
label.padding = Rect(5, 5, 5, 5)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
***Add Color constants to ostd::Color
|
||||
Add MouseEntered/MouseExited callbacks
|
||||
Fix mouse detection currently using local coordinates
|
||||
***Add MouseEntered/MouseExited callbacks
|
||||
***Fix mouse detection currently using local coordinates
|
||||
implement JSON theme loading
|
||||
Implemenmt MouseDragged callback
|
||||
***Implemenmt MouseDragged callback
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "io/FileSystem.hpp"
|
||||
#include "io/Logger.hpp"
|
||||
#include "string/String.hpp"
|
||||
#include <ostd/data/Color.hpp>
|
||||
#include <ostd/math/Geometry.hpp>
|
||||
#include <variant>
|
||||
|
|
@ -31,21 +34,22 @@ namespace ogfx
|
|||
{
|
||||
struct Theme
|
||||
{
|
||||
public: using ThemeValue = std::variant<int, float, bool, ostd::String, ostd::Color, ostd::Rectangle, ostd::Vec2>;
|
||||
public: using ThemeValue = std::variant<int32_t, float, bool, ostd::String, ostd::Color, ostd::Rectangle, ostd::Vec2>;
|
||||
public:
|
||||
inline Theme(bool blank = false)
|
||||
{
|
||||
if (blank) return;
|
||||
|
||||
// Loading default theme
|
||||
set("window.backgroundColor", ostd::Colors::Black);
|
||||
|
||||
set("label.textColor", ostd::Colors::White);
|
||||
set("label.backgroundColor", ostd::Colors::Transparent);
|
||||
set("label.backgroundColor", ostd::Colors::Red);
|
||||
set("label.borderColor", ostd::Colors::White);
|
||||
set("label.fontSize", 20);
|
||||
set("label.borderRadius", 20);
|
||||
set("label.borderWidth", 2);
|
||||
set("label.showBackground", false);
|
||||
set("label.showBackground", true);
|
||||
set("label.showBorder", false);
|
||||
set("label.padding", ostd::Rectangle { 5, 5, 5, 5 });
|
||||
}
|
||||
|
|
@ -56,9 +60,27 @@ namespace ogfx
|
|||
return *this;
|
||||
}
|
||||
|
||||
inline Theme& loadFromJsonFile(const ostd::String& jsonFilePath)
|
||||
inline Theme& loadFromFile(const ostd::String& filePath)
|
||||
{
|
||||
return *this; //TODO: Implement
|
||||
std::vector<ostd::String> lines;
|
||||
ostd::FileSystem::readTextFile(filePath, lines);
|
||||
uint32_t lineNumber = 0;
|
||||
ostd::String lineCopy = "";
|
||||
for (auto& line : lines)
|
||||
{
|
||||
lineNumber++;
|
||||
lineCopy = line;
|
||||
line.trim();
|
||||
if (line.startsWith("%"))
|
||||
continue;
|
||||
if (line.contains("%"))
|
||||
line.substr(0, line.indexOf("%")).trim();
|
||||
if (line == "")
|
||||
continue;
|
||||
if (!parseThemeFileLine(line))
|
||||
OX_WARN("Invalid theme line in file <%s:%d>:\n\t%s", filePath.c_str(), lineNumber, lineCopy.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void set(const std::string& key, ThemeValue value)
|
||||
|
|
@ -83,6 +105,63 @@ namespace ogfx
|
|||
return fallback;
|
||||
}
|
||||
|
||||
private:
|
||||
inline bool parseThemeFileLine(const ostd::String& line)
|
||||
{
|
||||
if (!line.contains("="))
|
||||
return false;
|
||||
ostd::String key = line.new_substr(0, line.indexOf("=")).trim();
|
||||
ostd::String value = line.new_substr(line.indexOf("=") + 1).trim().toLower();
|
||||
if (key == "")
|
||||
return false;
|
||||
if (value.isInt())
|
||||
set(key, static_cast<int32_t>(value.toInt()));
|
||||
else if (value.isNumeric(true))
|
||||
set(key, value.toFloat());
|
||||
else if (value == "true" || value == "false")
|
||||
set(key, value == "true");
|
||||
else if (value.startsWith("\"") && value.endsWith("\""))
|
||||
set(key, value);
|
||||
else if (value.startsWith("color(") && value.endsWith(")"))
|
||||
{
|
||||
value.substr(6, value.len() - 1).trim();
|
||||
set(key, ostd::Color(value));
|
||||
}
|
||||
else if (value.startsWith("vec2(") && value.endsWith(")"))
|
||||
{
|
||||
value.substr(4, value.len() - 1).trim();
|
||||
auto tokens = value.tokenize(",");
|
||||
if (tokens.count() != 2)
|
||||
return false;
|
||||
std::vector<float> vec;
|
||||
for (const auto& tok : tokens)
|
||||
{
|
||||
if (!tok.isNumeric(true))
|
||||
return false;
|
||||
vec.push_back(tok.toFloat());
|
||||
}
|
||||
set(key, ostd::Vec2(vec[0], vec[1]));
|
||||
}
|
||||
else if (value.startsWith("rect(") && value.endsWith(")"))
|
||||
{
|
||||
value.substr(5, value.len() - 1).trim();
|
||||
auto tokens = value.tokenize(",");
|
||||
if (tokens.count() != 4)
|
||||
return false;
|
||||
std::vector<float> vec;
|
||||
for (const auto& tok : tokens)
|
||||
{
|
||||
if (!tok.isNumeric(true))
|
||||
return false;
|
||||
vec.push_back(tok.toFloat());
|
||||
}
|
||||
set(key, ostd::Rectangle(vec[0], vec[1], vec[2], vec[3]));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<ostd::String, ThemeValue> m_values;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
|
||||
#include "Widgets.hpp"
|
||||
#include "gui/Events.hpp"
|
||||
#include "utils/Keycodes.hpp"
|
||||
#include <ogfx/render/BasicRenderer.hpp>
|
||||
#include <ogfx/gui/Window.hpp>
|
||||
|
|
@ -167,8 +168,26 @@ namespace ogfx
|
|||
if (w == nullptr) continue;
|
||||
if (w->isInvalid()) continue;
|
||||
if (!w->contains(event.mouse->position_x, event.mouse->position_y, true))
|
||||
{
|
||||
if (w->m_mouseInside)
|
||||
{
|
||||
w->m_mouseInside = false;
|
||||
w->__onMouseExited(event);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!w->m_mouseInside)
|
||||
{
|
||||
w->m_mouseInside = true;
|
||||
w->__onMouseEntered(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (w->m_pressedButton != ogfx::MouseEventData::eButton::None)
|
||||
w->__onMouseDragged(event);
|
||||
else
|
||||
w->__onMouseMoved(event);
|
||||
}
|
||||
if (event.isHandled())
|
||||
break;
|
||||
}
|
||||
|
|
@ -319,11 +338,13 @@ namespace ogfx
|
|||
if (callback_onMousePressed)
|
||||
callback_onMousePressed(event);
|
||||
onMousePressed(event);
|
||||
m_pressedButton = event.mouse->button;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::__onMouseReleased(const Event& event)
|
||||
{
|
||||
m_pressedButton = ogfx::MouseEventData::eButton::None;
|
||||
if (hasChildren())
|
||||
m_widgets.onMouseReleased(event);
|
||||
if (!event.isHandled())
|
||||
|
|
@ -346,6 +367,20 @@ namespace ogfx
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::__onMouseEntered(const Event& event)
|
||||
{
|
||||
if (callback_onMouseEntered)
|
||||
callback_onMouseEntered(event);
|
||||
onMouseEntered(event);
|
||||
}
|
||||
|
||||
void Widget::__onMouseExited(const Event& event)
|
||||
{
|
||||
if (callback_onMouseExited)
|
||||
callback_onMouseExited(event);
|
||||
onMouseExited(event);
|
||||
}
|
||||
|
||||
void Widget::__onMouseDragged(const Event& event)
|
||||
{
|
||||
if (hasChildren())
|
||||
|
|
@ -372,6 +407,7 @@ namespace ogfx
|
|||
|
||||
void Widget::__onKeyReleased(const Event& event)
|
||||
{
|
||||
m_pressedButton = ogfx::MouseEventData::eButton::None;
|
||||
if (hasChildren())
|
||||
m_widgets.onKeyReleased(event);
|
||||
if (!event.isHandled())
|
||||
|
|
@ -455,10 +491,9 @@ namespace ogfx
|
|||
{
|
||||
RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window)
|
||||
{
|
||||
disableDrawBox();
|
||||
m_rootChild = true;
|
||||
setSize(static_cast<float>(window.getWindowWidth()), static_cast<float>(window.getWindowHeight()));
|
||||
enableDrawBox();
|
||||
setDrawBoxColor(window.getClearColor());
|
||||
setTypeName("ogfx::gui::widgets::RootWidget");
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +504,12 @@ namespace ogfx
|
|||
|
||||
void RootWidget::applyTheme(const Theme& theme)
|
||||
{
|
||||
setDrawBoxColor(theme.get<ostd::Color>("window.backgroundColor", getWindow().getClearColor()));
|
||||
m_color = theme.get<ostd::Color>("window.backgroundColor", getWindow().getClearColor());
|
||||
}
|
||||
|
||||
void RootWidget::onDraw(ogfx::BasicRenderer2D& gfx)
|
||||
{
|
||||
gfx.fillRect({ 0, 0, static_cast<float>(getWindow().getWindowWidth()), static_cast<float>(getWindow().getWindowHeight()) }, m_color);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "gui/Events.hpp"
|
||||
#include <ostd/data/BaseObject.hpp>
|
||||
#include <ostd/math/Geometry.hpp>
|
||||
#include <ostd/data/Color.hpp>
|
||||
|
|
@ -87,6 +88,8 @@ namespace ogfx
|
|||
inline virtual void onMousePressed(const Event& event) { }
|
||||
inline virtual void onMouseReleased(const Event& event) { }
|
||||
inline virtual void onMouseMoved(const Event& event) { }
|
||||
inline virtual void onMouseEntered(const Event& event) { }
|
||||
inline virtual void onMouseExited(const Event& event) { }
|
||||
inline virtual void onMouseDragged(const Event& event) { }
|
||||
inline virtual void onKeyPressed(const Event& event) { }
|
||||
inline virtual void onKeyReleased(const Event& event) { }
|
||||
|
|
@ -103,6 +106,8 @@ namespace ogfx
|
|||
void __onMousePressed(const Event& event);
|
||||
void __onMouseReleased(const Event& event);
|
||||
void __onMouseMoved(const Event& event);
|
||||
void __onMouseEntered(const Event& event);
|
||||
void __onMouseExited(const Event& event);
|
||||
void __onMouseDragged(const Event& event);
|
||||
void __onKeyPressed(const Event& event);
|
||||
void __onKeyReleased(const Event& event);
|
||||
|
|
@ -116,6 +121,8 @@ namespace ogfx
|
|||
inline virtual void setMousePressedCallback(EventCallback callback) { callback_onMousePressed = callback; }
|
||||
inline virtual void setMouseReleasedCallback(EventCallback callback) { callback_onMouseReleased = callback; }
|
||||
inline virtual void setMouseMovedCallback(EventCallback callback) { callback_onMouseMoved = callback; }
|
||||
inline virtual void setMouseEnteredCallback(EventCallback callback) { callback_onMouseEntered = callback; }
|
||||
inline virtual void setMouseExitedCallback(EventCallback callback) { callback_onMouseExited = callback; }
|
||||
inline virtual void setMouseDraggedCallback(EventCallback callback) { callback_onMouseDragged = callback; }
|
||||
inline virtual void setKeyPressedCallback(EventCallback callback) { callback_onKeyPressed = callback; }
|
||||
inline virtual void setKeyReleasedCallback(EventCallback callback) { callback_onKeyReleased = callback; }
|
||||
|
|
@ -136,6 +143,8 @@ namespace ogfx
|
|||
inline bool isChildrenEnabled(void) const { return m_allowChildren; }
|
||||
inline void setPadding(const ostd::Rectangle& pad) { m_padding = pad; }
|
||||
inline Rectangle getPadding(void) { return m_padding; }
|
||||
inline bool isMouseInside(void) const { return m_mouseInside; }
|
||||
inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; }
|
||||
|
||||
protected:
|
||||
inline void disableChildren(void) { m_allowChildren = false; }
|
||||
|
|
@ -151,6 +160,8 @@ namespace ogfx
|
|||
EventCallback callback_onMousePressed { nullptr };
|
||||
EventCallback callback_onMouseReleased { nullptr };
|
||||
EventCallback callback_onMouseMoved { nullptr };
|
||||
EventCallback callback_onMouseEntered { nullptr };
|
||||
EventCallback callback_onMouseExited { nullptr };
|
||||
EventCallback callback_onMouseDragged { nullptr };
|
||||
EventCallback callback_onKeyPressed { nullptr };
|
||||
EventCallback callback_onKeyReleased { nullptr };
|
||||
|
|
@ -168,6 +179,8 @@ namespace ogfx
|
|||
int32_t m_zIndex { -1 };
|
||||
WidgetManager m_widgets;
|
||||
bool m_allowChildren { true };
|
||||
bool m_mouseInside { false };
|
||||
ogfx::MouseEventData::eButton m_pressedButton { ogfx::MouseEventData::eButton::None };
|
||||
|
||||
bool m_drawBox { true };
|
||||
ostd::Color m_drawBoxColor { 255, 0, 0 };
|
||||
|
|
@ -184,8 +197,10 @@ namespace ogfx
|
|||
RootWidget(WindowCore& window);
|
||||
void onWindowResized(const Event& event) override;
|
||||
void applyTheme(const Theme& theme) override;
|
||||
void onDraw(ogfx::BasicRenderer2D& gfx) override;
|
||||
|
||||
private:
|
||||
ostd::Color m_color { ostd::Colors::Transparent };
|
||||
};
|
||||
class Label : public Widget
|
||||
{
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ namespace ogfx
|
|||
connectSignal(ostd::BuiltinSignals::WindowLostFocus);
|
||||
|
||||
__on_window_init(width, height, title);
|
||||
setSize(m_windowWidth, m_windowHeight);
|
||||
}
|
||||
|
||||
void WindowCore::mainLoop(void)
|
||||
|
|
@ -457,6 +458,7 @@ namespace ogfx
|
|||
m_gfx.init(*this);
|
||||
loadDefaultTHeme();
|
||||
onInitialize();
|
||||
m_rootWidget.setSize(static_cast<float>(width), static_cast<float>(height));
|
||||
}
|
||||
|
||||
void Window::__on_window_close(void)
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ namespace ogfx
|
|||
void __main_loop(void) override;
|
||||
void __on_signal(ostd::Signal& signal) override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
BasicRenderer2D m_gfx;
|
||||
widgets::RootWidget m_rootWidget { *this };
|
||||
const gui::Theme* m_guiTheme { nullptr };
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ namespace ostd
|
|||
str = str.substr(2);
|
||||
base = 2;
|
||||
}
|
||||
return strtol(str.c_str(), NULL, base);
|
||||
return strtoul(str.c_str(), NULL, base);
|
||||
}
|
||||
|
||||
float String::toFloat(void) const
|
||||
|
|
|
|||
|
|
@ -51,20 +51,24 @@ class Window : public ogfx::gui::Window
|
|||
{
|
||||
m_label1.setPosition(100, 200);
|
||||
m_label1.setText("Hello World!");
|
||||
m_label1.setMouseMovedCallback([&](const ogfx::gui::Event& event) -> void {
|
||||
m_label1.setMouseEnteredCallback([&](const ogfx::gui::Event& event) -> void {
|
||||
m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkBlue, false);
|
||||
this->setCursor(eCursor::Move);
|
||||
});
|
||||
m_label1.setMouseExitedCallback([&](const ogfx::gui::Event& event) -> void {
|
||||
m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkRed, false);
|
||||
this->setCursor(eCursor::Default);
|
||||
});
|
||||
m_label1.setMouseDraggedCallback([&](const ogfx::gui::Event& event) -> void {
|
||||
m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkGreen, false);
|
||||
});
|
||||
addWidget(m_label1);
|
||||
|
||||
m_label2.setPosition(100, 400);
|
||||
m_label2.setText("Ciccia Bella!");
|
||||
addWidget(m_label2);
|
||||
|
||||
m_theme.set("label.textColor", ostd::Colors::White);
|
||||
m_theme.set("label.backgroundColor", ostd::Colors::DarkRed);
|
||||
m_theme.set("label.showBackground", true);
|
||||
m_theme.set("label.borderRadius", 0);
|
||||
m_theme.loadFromFile("./testTheme.txt");
|
||||
setTheme(m_theme);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue