Extended Stylesheet theming functionality
This commit is contained in:
parent
f33b6de559
commit
39c9798760
14 changed files with 398 additions and 227 deletions
1
.clangd
1
.clangd
|
|
@ -2,7 +2,6 @@ CompileFlags:
|
||||||
CompilationDatabase: bin
|
CompilationDatabase: bin
|
||||||
Add:
|
Add:
|
||||||
[
|
[
|
||||||
"--target=x86_64-w64-mingw32",
|
|
||||||
"-IC:/msys64/ucrt64/include",
|
"-IC:/msys64/ucrt64/include",
|
||||||
"-IC:/msys64/ucrt64/include/ucrt",
|
"-IC:/msys64/ucrt64/include/ucrt",
|
||||||
"-IC:/msys64/ucrt64/include/crt",
|
"-IC:/msys64/ucrt64/include/crt",
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ list(APPEND OSTD_SOURCE_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/OutputHandlers.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/OutputHandlers.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/Serial.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/Serial.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/Memory.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/Memory.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/io/Stylesheet.cpp
|
||||||
|
|
||||||
# math
|
# math
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/math/Geometry.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/math/Geometry.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,28 @@
|
||||||
|
% ================== testTheme.txt ==================
|
||||||
|
|
||||||
|
$accentColor = Color(rgba(255, 0, 255, 255))
|
||||||
|
|
||||||
% Window
|
% Window
|
||||||
window.backgroundColor = Color(rgba(40, 0, 0, 255))
|
window.backgroundColor = Color(rgba(40, 0, 0, 255))
|
||||||
|
|
||||||
% Label
|
% Label
|
||||||
label.textColor = Color(#FFFFFFFF)
|
label.textColor = $accentColor
|
||||||
label.backgroundColor = Color(#FFFF80FF)
|
label.backgroundColor = Color(#000000FF)
|
||||||
label.borderColor = Color(#FFFFFFFF)
|
label.borderColor = $accentColor
|
||||||
label.fontSize = 30
|
label.fontSize = 30
|
||||||
label.borderRadius = 20
|
label.borderRadius = 20
|
||||||
label.borderWidth = 2
|
label.borderWidth = 2
|
||||||
label.showBackground = false
|
label.showBackground = true
|
||||||
label.showBorder = false
|
label.showBorder = true
|
||||||
label.padding = Rect(5, 5, 5, 5)
|
label.padding = Rect(15, 15, 15, 15)
|
||||||
|
|
||||||
|
% Format:
|
||||||
|
% @ID:QUALIFIER PROPERTY = VALUE
|
||||||
|
|
||||||
|
|
||||||
|
@testLabel label.textColor = Color(#004000FF)
|
||||||
|
@testLabel:hover label.textColor = Color(#000040FF)
|
||||||
|
@testLabel:hover label.backgroundColor = Color(#400040FF)
|
||||||
|
@testWindow window.backgroundColor = Color(#004000FF)
|
||||||
|
|
||||||
|
% ===================================================
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
***Add Color constants to ostd::Color
|
***Add Color constants to ostd::Color
|
||||||
***Add MouseEntered/MouseExited callbacks
|
***Add MouseEntered/MouseExited callbacks
|
||||||
***Fix mouse detection currently using local coordinates
|
***Fix mouse detection currently using local coordinates
|
||||||
implement JSON theme loading
|
***implement file theme loading
|
||||||
***Implemenmt MouseDragged callback
|
***Implemenmt MouseDragged callback
|
||||||
|
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
/*
|
|
||||||
OmniaFramework - A collection of useful functionality
|
|
||||||
Copyright (C) 2025 OmniaX-Dev
|
|
||||||
|
|
||||||
This file is part of OmniaFramework.
|
|
||||||
|
|
||||||
OmniaFramework is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
OmniaFramework is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace ogfx
|
|
||||||
{
|
|
||||||
namespace gui
|
|
||||||
{
|
|
||||||
struct Theme
|
|
||||||
{
|
|
||||||
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::Red);
|
|
||||||
set("label.borderColor", ostd::Colors::White);
|
|
||||||
set("label.fontSize", 20);
|
|
||||||
set("label.borderRadius", 20);
|
|
||||||
set("label.borderWidth", 2);
|
|
||||||
set("label.showBackground", true);
|
|
||||||
set("label.showBorder", false);
|
|
||||||
set("label.padding", ostd::Rectangle { 5, 5, 5, 5 });
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Theme& blank(void)
|
|
||||||
{
|
|
||||||
m_values.clear();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Theme& loadFromFile(const ostd::String& filePath)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
m_values[key] = std::move(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const ThemeValue* __get(const ostd::String& key) const
|
|
||||||
{
|
|
||||||
auto it = m_values.find(key);
|
|
||||||
return it != m_values.end() ? &it->second : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline T get(const ostd::String& key, const T& fallback) const
|
|
||||||
{
|
|
||||||
if (auto v = __get(key))
|
|
||||||
{
|
|
||||||
if (auto p = std::get_if<T>(v))
|
|
||||||
return *p;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -118,7 +118,7 @@ namespace ogfx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WidgetManager::onThemeApplied(const Theme& theme)
|
void WidgetManager::onThemeApplied(const ostd::Stylesheet& theme)
|
||||||
{
|
{
|
||||||
for (auto& w : m_widgetList)
|
for (auto& w : m_widgetList)
|
||||||
{
|
{
|
||||||
|
|
@ -304,13 +304,33 @@ namespace ogfx
|
||||||
return ostd::Rectangle(getGlobalPosition(), getSize()).contains(p, includeBounds);
|
return ostd::Rectangle(getGlobalPosition(), getSize()).contains(p, includeBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::applyThemeValue(Theme& theme, const ostd::String& key, Theme::ThemeValue value, bool propagate)
|
void Widget::addThemeOverride(const ostd::String& key, ostd::Stylesheet::TypeVariant value, const ostd::String& themeID, const ostd::String& qualifier, bool propagate)
|
||||||
{
|
{
|
||||||
auto currentValue = theme.__get(key);
|
ostd::String fullKey = "@" + themeID;
|
||||||
if (currentValue == nullptr) return;
|
if (qualifier.new_trim() != "")
|
||||||
theme.set(key, value);
|
fullKey += ":" + qualifier;
|
||||||
__applyTheme(theme, propagate);
|
fullKey += "." + key;
|
||||||
theme.set(key, *currentValue);
|
m_themeOverrides.push_back({ fullKey, value, propagate });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::reloadTheme(void)
|
||||||
|
{
|
||||||
|
if (getWindow().theme() == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& theme = *getWindow().theme();
|
||||||
|
applyTheme(theme);
|
||||||
|
ostd::Stylesheet& const_cast_theme = const_cast<ostd::Stylesheet&>(theme);
|
||||||
|
|
||||||
|
for (auto& rule : m_themeOverrides)
|
||||||
|
{
|
||||||
|
auto currentValue = theme.getFull(rule.fullKey);
|
||||||
|
if (currentValue == nullptr)
|
||||||
|
return;
|
||||||
|
const_cast_theme.setFull(rule.fullKey, rule.value);
|
||||||
|
__applyTheme(const_cast_theme, rule.propagate);
|
||||||
|
const_cast_theme.setFull(rule.fullKey, *currentValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::__draw(ogfx::BasicRenderer2D& gfx)
|
void Widget::__draw(ogfx::BasicRenderer2D& gfx)
|
||||||
|
|
@ -369,6 +389,8 @@ namespace ogfx
|
||||||
|
|
||||||
void Widget::__onMouseEntered(const Event& event)
|
void Widget::__onMouseEntered(const Event& event)
|
||||||
{
|
{
|
||||||
|
m_qualifier = "hover";
|
||||||
|
reloadTheme();
|
||||||
if (callback_onMouseEntered)
|
if (callback_onMouseEntered)
|
||||||
callback_onMouseEntered(event);
|
callback_onMouseEntered(event);
|
||||||
onMouseEntered(event);
|
onMouseEntered(event);
|
||||||
|
|
@ -376,6 +398,8 @@ namespace ogfx
|
||||||
|
|
||||||
void Widget::__onMouseExited(const Event& event)
|
void Widget::__onMouseExited(const Event& event)
|
||||||
{
|
{
|
||||||
|
m_qualifier = "";
|
||||||
|
reloadTheme();
|
||||||
if (callback_onMouseExited)
|
if (callback_onMouseExited)
|
||||||
callback_onMouseExited(event);
|
callback_onMouseExited(event);
|
||||||
onMouseExited(event);
|
onMouseExited(event);
|
||||||
|
|
@ -478,7 +502,7 @@ namespace ogfx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::__applyTheme(const Theme& theme, bool propagate)
|
void Widget::__applyTheme(const ostd::Stylesheet& theme, bool propagate)
|
||||||
{
|
{
|
||||||
if (propagate && hasChildren())
|
if (propagate && hasChildren())
|
||||||
m_widgets.onThemeApplied(theme);
|
m_widgets.onThemeApplied(theme);
|
||||||
|
|
@ -502,9 +526,9 @@ namespace ogfx
|
||||||
setSize(static_cast<float>(event.windowResized->new_width), static_cast<float>(event.windowResized->new_height));
|
setSize(static_cast<float>(event.windowResized->new_width), static_cast<float>(event.windowResized->new_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::applyTheme(const Theme& theme)
|
void RootWidget::applyTheme(const ostd::Stylesheet& theme)
|
||||||
{
|
{
|
||||||
m_color = theme.get<ostd::Color>("window.backgroundColor", getWindow().getClearColor());
|
m_color = theme.get<ostd::Color>("window.backgroundColor", getWindow().getClearColor(), getThemeID(), getThemeQualifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWidget::onDraw(ogfx::BasicRenderer2D& gfx)
|
void RootWidget::onDraw(ogfx::BasicRenderer2D& gfx)
|
||||||
|
|
@ -526,17 +550,17 @@ namespace ogfx
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::applyTheme(const Theme& theme)
|
void Label::applyTheme(const ostd::Stylesheet& theme)
|
||||||
{
|
{
|
||||||
setColor(theme.get<ostd::Color>("label.textColor", ostd::Colors::White));
|
setColor(theme.get<ostd::Color>("label.textColor", ostd::Colors::White, getThemeID(), getThemeQualifier()));
|
||||||
setBackGroundColor(theme.get<ostd::Color>("label.backgroundColor", ostd::Colors::Transparent));
|
setBackGroundColor(theme.get<ostd::Color>("label.backgroundColor", ostd::Colors::Transparent, getThemeID(), getThemeQualifier()));
|
||||||
setFontSize(theme.get<int32_t>("label.fontSize", 20));
|
setFontSize(theme.get<int32_t>("label.fontSize", 20, getThemeID(), getThemeQualifier()));
|
||||||
m_borderRadius = theme.get<int32_t>("label.borderRadius", 10);
|
m_borderRadius = theme.get<int32_t>("label.borderRadius", 10, getThemeID(), getThemeQualifier());
|
||||||
m_borderWidth = theme.get<int32_t>("label.borderWidth", 2);
|
m_borderWidth = theme.get<int32_t>("label.borderWidth", 2, getThemeID(), getThemeQualifier());
|
||||||
m_showBorder = theme.get<bool>("label.showBorder", false);
|
m_showBorder = theme.get<bool>("label.showBorder", false, getThemeID(), getThemeQualifier());
|
||||||
m_borderColor = theme.get<ostd::Color>("label.borderColor", ostd::Colors::White);
|
m_borderColor = theme.get<ostd::Color>("label.borderColor", ostd::Colors::White, getThemeID(), getThemeQualifier());
|
||||||
enableBackground(theme.get<bool>("label.showBackground", false));
|
enableBackground(theme.get<bool>("label.showBackground", false, getThemeID(), getThemeQualifier()));
|
||||||
setPadding(theme.get<ostd::Rectangle>("label.padding", { 5, 5, 5, 5 }));
|
setPadding(theme.get<ostd::Rectangle>("label.padding", { 5, 5, 5, 5 }, getThemeID(), getThemeQualifier()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::onDraw(ogfx::BasicRenderer2D& gfx)
|
void Label::onDraw(ogfx::BasicRenderer2D& gfx)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
#include <ostd/data/BaseObject.hpp>
|
#include <ostd/data/BaseObject.hpp>
|
||||||
#include <ostd/math/Geometry.hpp>
|
#include <ostd/math/Geometry.hpp>
|
||||||
#include <ostd/data/Color.hpp>
|
#include <ostd/data/Color.hpp>
|
||||||
#include <ogfx/gui/Themes.hpp>
|
#include <ostd/io/Stylesheet.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace ogfx
|
namespace ogfx
|
||||||
|
|
@ -46,7 +46,7 @@ namespace ogfx
|
||||||
|
|
||||||
void draw(ogfx::BasicRenderer2D& gfx);
|
void draw(ogfx::BasicRenderer2D& gfx);
|
||||||
void update(void);
|
void update(void);
|
||||||
void onThemeApplied(const Theme& theme);
|
void onThemeApplied(const ostd::Stylesheet& theme);
|
||||||
void onMousePressed(const Event& event);
|
void onMousePressed(const Event& event);
|
||||||
void onMouseReleased(const Event& event);
|
void onMouseReleased(const Event& event);
|
||||||
void onMouseMoved(const Event& event);
|
void onMouseMoved(const Event& event);
|
||||||
|
|
@ -72,6 +72,12 @@ namespace ogfx
|
||||||
};
|
};
|
||||||
class Widget : public ostd::BaseObject, public ostd::Rectangle
|
class Widget : public ostd::BaseObject, public ostd::Rectangle
|
||||||
{
|
{
|
||||||
|
private: struct ThemeOverride
|
||||||
|
{
|
||||||
|
ostd::String fullKey;
|
||||||
|
ostd::Stylesheet::TypeVariant value;
|
||||||
|
bool propagate;
|
||||||
|
};
|
||||||
public: using EventCallback = std::function<void(const Event&)>;
|
public: using EventCallback = std::function<void(const Event&)>;
|
||||||
public:
|
public:
|
||||||
Widget(const ostd::Rectangle& bounds, WindowCore& window);
|
Widget(const ostd::Rectangle& bounds, WindowCore& window);
|
||||||
|
|
@ -79,8 +85,9 @@ namespace ogfx
|
||||||
ostd::Vec2 getGlobalPosition(void) const;
|
ostd::Vec2 getGlobalPosition(void) const;
|
||||||
using ostd::Rectangle::contains;
|
using ostd::Rectangle::contains;
|
||||||
bool contains(ostd::Vec2 p, bool includeBounds = false) const override;
|
bool contains(ostd::Vec2 p, bool includeBounds = false) const override;
|
||||||
virtual void applyTheme(const Theme& theme) = 0;
|
virtual void applyTheme(const ostd::Stylesheet& theme) = 0;
|
||||||
void applyThemeValue(Theme& theme, const ostd::String& key, Theme::ThemeValue value, bool propagate);
|
void addThemeOverride(const ostd::String& key, ostd::Stylesheet::TypeVariant value, const ostd::String& themeID = "", const ostd::String& qualifier = "", bool propagate = true);
|
||||||
|
void reloadTheme(void);
|
||||||
|
|
||||||
inline virtual void onDraw(ogfx::BasicRenderer2D& gfx) { }
|
inline virtual void onDraw(ogfx::BasicRenderer2D& gfx) { }
|
||||||
inline virtual void onUpdate(void) { }
|
inline virtual void onUpdate(void) { }
|
||||||
|
|
@ -116,7 +123,7 @@ namespace ogfx
|
||||||
void __onWindowResized(const Event& event);
|
void __onWindowResized(const Event& event);
|
||||||
void __onWIndowFocused(const Event& event);
|
void __onWIndowFocused(const Event& event);
|
||||||
void __onWindowFocusLost(const Event& event);
|
void __onWindowFocusLost(const Event& event);
|
||||||
void __applyTheme(const Theme& theme, bool propagate);
|
void __applyTheme(const ostd::Stylesheet& theme, bool propagate);
|
||||||
|
|
||||||
inline virtual void setMousePressedCallback(EventCallback callback) { callback_onMousePressed = callback; }
|
inline virtual void setMousePressedCallback(EventCallback callback) { callback_onMousePressed = callback; }
|
||||||
inline virtual void setMouseReleasedCallback(EventCallback callback) { callback_onMouseReleased = callback; }
|
inline virtual void setMouseReleasedCallback(EventCallback callback) { callback_onMouseReleased = callback; }
|
||||||
|
|
@ -145,6 +152,9 @@ namespace ogfx
|
||||||
inline Rectangle getPadding(void) { return m_padding; }
|
inline Rectangle getPadding(void) { return m_padding; }
|
||||||
inline bool isMouseInside(void) const { return m_mouseInside; }
|
inline bool isMouseInside(void) const { return m_mouseInside; }
|
||||||
inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; }
|
inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; }
|
||||||
|
inline ostd::String getThemeID(void) const { return m_themeID; }
|
||||||
|
inline void setThemeID(const ostd::String& id) { m_themeID = id; }
|
||||||
|
inline ostd::String getThemeQualifier(void) const { return m_qualifier; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void disableChildren(void) { m_allowChildren = false; }
|
inline void disableChildren(void) { m_allowChildren = false; }
|
||||||
|
|
@ -180,7 +190,11 @@ namespace ogfx
|
||||||
WidgetManager m_widgets;
|
WidgetManager m_widgets;
|
||||||
bool m_allowChildren { true };
|
bool m_allowChildren { true };
|
||||||
bool m_mouseInside { false };
|
bool m_mouseInside { false };
|
||||||
ogfx::MouseEventData::eButton m_pressedButton { ogfx::MouseEventData::eButton::None };
|
MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None };
|
||||||
|
|
||||||
|
ostd::String m_themeID { "" };
|
||||||
|
ostd::String m_qualifier { "" };
|
||||||
|
std::vector<ThemeOverride> m_themeOverrides;
|
||||||
|
|
||||||
bool m_drawBox { true };
|
bool m_drawBox { true };
|
||||||
ostd::Color m_drawBoxColor { 255, 0, 0 };
|
ostd::Color m_drawBoxColor { 255, 0, 0 };
|
||||||
|
|
@ -196,7 +210,7 @@ namespace ogfx
|
||||||
public:
|
public:
|
||||||
RootWidget(WindowCore& window);
|
RootWidget(WindowCore& window);
|
||||||
void onWindowResized(const Event& event) override;
|
void onWindowResized(const Event& event) override;
|
||||||
void applyTheme(const Theme& theme) override;
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
void onDraw(ogfx::BasicRenderer2D& gfx) override;
|
void onDraw(ogfx::BasicRenderer2D& gfx) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -208,7 +222,7 @@ namespace ogfx
|
||||||
inline Label(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); }
|
inline Label(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window) { create(""); }
|
||||||
inline Label(WindowCore& window, const ostd::String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
|
inline Label(WindowCore& window, const ostd::String& text) : Widget({ 0, 0, 0, 0 }, window) { create(text); }
|
||||||
Label& create(const ostd::String& text);
|
Label& create(const ostd::String& text);
|
||||||
void applyTheme(const Theme& theme) override;
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
void onDraw(ogfx::BasicRenderer2D& gfx) override;
|
void onDraw(ogfx::BasicRenderer2D& gfx) override;
|
||||||
void setText(const ostd::String& text);
|
void setText(const ostd::String& text);
|
||||||
inline ostd::String getText(void) const { return m_text; }
|
inline ostd::String getText(void) const { return m_text; }
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,18 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
const Uint32 REDRAW_EVENT = SDL_RegisterEvents(1);
|
const Uint32 REDRAW_EVENT = SDL_RegisterEvents(1);
|
||||||
|
|
||||||
|
inline static const ostd::String DefaultThemeStyle = \
|
||||||
|
"window.backgroundColor = Color(#000000FF)\n" \
|
||||||
|
"label.textColor = Color(#FFFFFFFF)\n" \
|
||||||
|
"label.backgroundColor = Color(#00000000)\n" \
|
||||||
|
"label.borderColor = Color(#FFFFFFFF)\n" \
|
||||||
|
"label.fontSize = 20\n" \
|
||||||
|
"label.borderRadius = 20\n" \
|
||||||
|
"label.borderWidth = 2\n" \
|
||||||
|
"label.showBackground = false\n" \
|
||||||
|
"label.showBorder = false\n" \
|
||||||
|
"label.padding = Rect(5, 5, 5, 5)\n";
|
||||||
|
|
||||||
WindowCore::~WindowCore(void)
|
WindowCore::~WindowCore(void)
|
||||||
{
|
{
|
||||||
__on_window_destroy();
|
__on_window_destroy();
|
||||||
|
|
@ -60,6 +72,9 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
if (m_initialized) return;
|
if (m_initialized) return;
|
||||||
SDLSysten::acquire();
|
SDLSysten::acquire();
|
||||||
|
|
||||||
|
DefaultTheme.loadFromString(DefaultThemeStyle);
|
||||||
|
|
||||||
m_windowWidth = width;
|
m_windowWidth = width;
|
||||||
m_windowHeight = height;
|
m_windowHeight = height;
|
||||||
m_title = title;
|
m_title = title;
|
||||||
|
|
@ -445,7 +460,7 @@ namespace ogfx
|
||||||
setTheme(*m_guiTheme);
|
setTheme(*m_guiTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::setTheme(const gui::Theme& theme)
|
void Window::setTheme(const ostd::Stylesheet& theme)
|
||||||
{
|
{
|
||||||
m_guiTheme = &theme;
|
m_guiTheme = &theme;
|
||||||
m_rootWidget.__applyTheme(theme, true);
|
m_rootWidget.__applyTheme(theme, true);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
#include <ogfx/gui/Widgets.hpp>
|
#include <ogfx/gui/Widgets.hpp>
|
||||||
#include <ogfx/render/BasicRenderer.hpp>
|
#include <ogfx/render/BasicRenderer.hpp>
|
||||||
#include <ogfx/gui/WindowOutputHandler.hpp>
|
#include <ogfx/gui/WindowOutputHandler.hpp>
|
||||||
#include <ogfx/gui/Themes.hpp>
|
|
||||||
|
|
||||||
namespace ogfx
|
namespace ogfx
|
||||||
{
|
{
|
||||||
|
|
@ -75,6 +74,10 @@ namespace ogfx
|
||||||
void requestRedraw(void);
|
void requestRedraw(void);
|
||||||
void handleSignal(ostd::Signal& signal) override;
|
void handleSignal(ostd::Signal& signal) override;
|
||||||
|
|
||||||
|
inline const ostd::Stylesheet* theme(void) const { return m_guiTheme; }
|
||||||
|
inline void loadDefaultTHeme(void) { setTheme(DefaultTheme); }
|
||||||
|
inline virtual void setTheme(const ostd::Stylesheet& theme) { }
|
||||||
|
|
||||||
inline bool isInitialized(void) const { return m_initialized; }
|
inline bool isInitialized(void) const { return m_initialized; }
|
||||||
inline bool isRunning(void) const { return m_running; }
|
inline bool isRunning(void) const { return m_running; }
|
||||||
inline bool isVisible(void) const { return m_visible; }
|
inline bool isVisible(void) const { return m_visible; }
|
||||||
|
|
@ -112,6 +115,7 @@ namespace ogfx
|
||||||
SDL_Renderer* m_renderer { nullptr };
|
SDL_Renderer* m_renderer { nullptr };
|
||||||
ostd::ConsoleOutputHandler m_out;
|
ostd::ConsoleOutputHandler m_out;
|
||||||
GraphicsWindowOutputHandler m_wout;
|
GraphicsWindowOutputHandler m_wout;
|
||||||
|
const ostd::Stylesheet* m_guiTheme { nullptr };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ostd::Color m_clearColor { 10, 10, 10, 255 };
|
ostd::Color m_clearColor { 10, 10, 10, 255 };
|
||||||
|
|
@ -152,7 +156,9 @@ namespace ogfx
|
||||||
public:
|
public:
|
||||||
inline static constexpr int32_t MaxBlockingEventsFPS { 240 };
|
inline static constexpr int32_t MaxBlockingEventsFPS { 240 };
|
||||||
inline static constexpr int32_t DefaultBlockingEventsFPS { 30 };
|
inline static constexpr int32_t DefaultBlockingEventsFPS { 30 };
|
||||||
inline static const gui::Theme DefaultTheme;
|
|
||||||
|
private:
|
||||||
|
inline static ostd::Stylesheet DefaultTheme;
|
||||||
};
|
};
|
||||||
class GraphicsWindow : public WindowCore
|
class GraphicsWindow : public WindowCore
|
||||||
{
|
{
|
||||||
|
|
@ -196,6 +202,8 @@ namespace ogfx
|
||||||
inline Window(int32_t width, int32_t height, const ostd::String& title) { initialize(width, height, title); }
|
inline Window(int32_t width, int32_t height, const ostd::String& title) { initialize(width, height, title); }
|
||||||
void addWidget(Widget& widget);
|
void addWidget(Widget& widget);
|
||||||
|
|
||||||
|
void setTheme(const ostd::Stylesheet& theme) override;
|
||||||
|
|
||||||
inline virtual void onInitialize(void) { }
|
inline virtual void onInitialize(void) { }
|
||||||
inline virtual void onDestroy(void) { }
|
inline virtual void onDestroy(void) { }
|
||||||
inline virtual void onClose(void) { }
|
inline virtual void onClose(void) { }
|
||||||
|
|
@ -203,10 +211,6 @@ namespace ogfx
|
||||||
inline virtual void onRedraw(BasicRenderer2D& gfx) { }
|
inline virtual void onRedraw(BasicRenderer2D& gfx) { }
|
||||||
inline virtual void onSignal(ostd::Signal& signal) { }
|
inline virtual void onSignal(ostd::Signal& signal) { }
|
||||||
|
|
||||||
inline const gui::Theme& theme(void) const { return *m_guiTheme; }
|
|
||||||
void setTheme(const gui::Theme& theme);
|
|
||||||
inline void loadDefaultTHeme(void) { setTheme(DefaultTheme); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void __on_window_init(int32_t width, int32_t height, const ostd::String& title) override;
|
void __on_window_init(int32_t width, int32_t height, const ostd::String& title) override;
|
||||||
void __on_event(SDL_Event& event) override;
|
void __on_event(SDL_Event& event) override;
|
||||||
|
|
@ -218,9 +222,6 @@ namespace ogfx
|
||||||
protected:
|
protected:
|
||||||
BasicRenderer2D m_gfx;
|
BasicRenderer2D m_gfx;
|
||||||
widgets::RootWidget m_rootWidget { *this };
|
widgets::RootWidget m_rootWidget { *this };
|
||||||
const gui::Theme* m_guiTheme { nullptr };
|
|
||||||
|
|
||||||
inline static const gui::Theme DefaultTheme;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
194
src/ostd/io/Stylesheet.cpp
Normal file
194
src/ostd/io/Stylesheet.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
OmniaFramework - A collection of useful functionality
|
||||||
|
Copyright (C) 2025 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of OmniaFramework.
|
||||||
|
|
||||||
|
OmniaFramework is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
OmniaFramework is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Stylesheet.hpp"
|
||||||
|
#include "FileSystem.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace ostd
|
||||||
|
{
|
||||||
|
Stylesheet::Stylesheet(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Stylesheet& Stylesheet::clear(void)
|
||||||
|
{
|
||||||
|
m_values.clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stylesheet& Stylesheet::loadFromFile(const ostd::String& filePath)
|
||||||
|
{
|
||||||
|
ostd::String outContent = "";
|
||||||
|
ostd::FileSystem::readTextFileRaw(filePath, outContent);
|
||||||
|
return loadFromString(outContent, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stylesheet& Stylesheet::loadFromString(const ostd::String& content, const ostd::String& filePath)
|
||||||
|
{
|
||||||
|
std::vector<ostd::String> lines = content.tokenize("\n", false, true).getRawData();
|
||||||
|
uint32_t lineNumber = 0;
|
||||||
|
ostd::String lineCopy = "";
|
||||||
|
std::unordered_map<ostd::String, ostd::String> variables;
|
||||||
|
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 (line.startsWith("$"))
|
||||||
|
{
|
||||||
|
line.substr(1);
|
||||||
|
if (line.count("=") != 1 || line.endsWith("="))
|
||||||
|
{
|
||||||
|
OX_WARN("Invalid variable in theme line. File: <%s:%d>:\n\t%s", filePath.c_str(), lineNumber, lineCopy.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ostd::String varName = line.new_substr(0, line.indexOf("=")).trim();
|
||||||
|
ostd::String varValue = line.new_substr(line.indexOf("=") + 1).trim();
|
||||||
|
if (!varName.regexMatches(m_validNameRegex))
|
||||||
|
{
|
||||||
|
OX_WARN("Invalid variable name in theme line. File: <%s:%d>:\n\t%s", filePath.c_str(), lineNumber, lineCopy.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
variables["$" + varName] = varValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const auto&[var, val] : variables)
|
||||||
|
line.replaceAll(var, val);
|
||||||
|
if (!parseThemeFileLine(line))
|
||||||
|
OX_WARN("Invalid theme line: File <%s:%d>:\n\t%s", filePath.c_str(), lineNumber, lineCopy.c_str());
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stylesheet::set(const std::string& key, TypeVariant value, const ostd::String& themeID)
|
||||||
|
{
|
||||||
|
ostd::String fullKey = "@" + themeID + "." + key;
|
||||||
|
// std::cout << "SET: " << fullKey << "\n";
|
||||||
|
setFull(fullKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stylesheet::setFull(const ostd::String& fullKey, TypeVariant value)
|
||||||
|
{
|
||||||
|
m_values[fullKey] = std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Stylesheet::TypeVariant* Stylesheet::getVariant(const ostd::String& key, const ostd::String& themeID, const ostd::String& qualifier) const
|
||||||
|
{
|
||||||
|
if (auto v = getFull("@" + themeID + ":" + qualifier + "." + key))
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
else if (auto v = getFull("@:" + qualifier + "." + key))
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
else if (auto v = getFull("@" + themeID + "." + key))
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
else if (auto v = getFull("@." + key))
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Stylesheet::TypeVariant* Stylesheet::getFull(const ostd::String& fullKey) const
|
||||||
|
{
|
||||||
|
// std::cout << " GET: " << key << "\n";
|
||||||
|
auto it = m_values.find(fullKey);
|
||||||
|
return it != m_values.end() ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stylesheet::parseThemeFileLine(const ostd::String& line)
|
||||||
|
{
|
||||||
|
if (!line.contains("="))
|
||||||
|
return false;
|
||||||
|
ostd::String key = line.new_substr(0, line.indexOf("=")).trim();
|
||||||
|
ostd::String valuePreserveCase = line.new_substr(line.indexOf("=") + 1).trim();
|
||||||
|
ostd::String value = line.new_substr(line.indexOf("=") + 1).trim().toLower();
|
||||||
|
if (key == "")
|
||||||
|
return false;
|
||||||
|
ostd::String themeID = "";
|
||||||
|
if (key.startsWith("@"))
|
||||||
|
{
|
||||||
|
if (key.indexOf(" ") < 2)
|
||||||
|
return false;
|
||||||
|
themeID = key.new_substr(1, key.indexOf(" ")).trim();
|
||||||
|
key.substr(key.indexOf(" ") + 1).trim();
|
||||||
|
}
|
||||||
|
if (value.isInt())
|
||||||
|
set(key, static_cast<int32_t>(value.toInt()), themeID);
|
||||||
|
else if (value.isNumeric(true))
|
||||||
|
set(key, value.toFloat(), themeID);
|
||||||
|
else if (value == "true" || value == "false")
|
||||||
|
set(key, value == "true", themeID);
|
||||||
|
else if (value.startsWith("\"") && value.endsWith("\""))
|
||||||
|
{
|
||||||
|
valuePreserveCase.substr(1, value.len() - 1);
|
||||||
|
set(key, valuePreserveCase, themeID);
|
||||||
|
}
|
||||||
|
else if (value.startsWith("color(") && value.endsWith(")"))
|
||||||
|
{
|
||||||
|
value.substr(6, value.len() - 1).trim();
|
||||||
|
set(key, ostd::Color(value), themeID);
|
||||||
|
}
|
||||||
|
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]), themeID);
|
||||||
|
}
|
||||||
|
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]), themeID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/ostd/io/Stylesheet.hpp
Normal file
61
src/ostd/io/Stylesheet.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
OmniaFramework - A collection of useful functionality
|
||||||
|
Copyright (C) 2025 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of OmniaFramework.
|
||||||
|
|
||||||
|
OmniaFramework is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
OmniaFramework is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostd/data/Color.hpp>
|
||||||
|
#include <ostd/math/Geometry.hpp>
|
||||||
|
#include <variant>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ostd
|
||||||
|
{
|
||||||
|
class Stylesheet
|
||||||
|
{
|
||||||
|
public: using TypeVariant = std::variant<int32_t, float, bool, ostd::String, ostd::Color, ostd::Rectangle, ostd::Vec2>;
|
||||||
|
public:
|
||||||
|
Stylesheet(void);
|
||||||
|
Stylesheet& clear(void);
|
||||||
|
Stylesheet& loadFromFile(const ostd::String& filePath);
|
||||||
|
Stylesheet& loadFromString(const ostd::String& content, const ostd::String& filePath = "memory://");
|
||||||
|
void set(const std::string& key, TypeVariant value, const ostd::String& themeID);
|
||||||
|
void setFull(const ostd::String& fullKey, TypeVariant value);
|
||||||
|
const TypeVariant* getVariant(const ostd::String& key, const ostd::String& themeID, const ostd::String& qualifier) const;
|
||||||
|
const TypeVariant* getFull(const ostd::String& fullKey) const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T get(const ostd::String& key, const T& fallback, const ostd::String& themeID, const ostd::String& qualifier) const
|
||||||
|
{
|
||||||
|
if (auto v = getVariant(key, themeID, qualifier))
|
||||||
|
{
|
||||||
|
if (auto p = std::get_if<T>(v))
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parseThemeFileLine(const ostd::String& line);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<ostd::String, TypeVariant> m_values;
|
||||||
|
const ostd::String m_validNameRegex { "^[A-Za-z_][A-Za-z0-9_]*$" };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -511,6 +511,20 @@ namespace ostd
|
||||||
return m_data.ends_with(str);
|
return m_data.ends_with(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool String::regexMatches(const String& regex_pattern, bool case_insensitive) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
boost::regex rgx(regex_pattern.cpp_str(), case_insensitive ? boost::regex_constants::icase : boost::regex_constants::normal);
|
||||||
|
return boost::regex_search(m_data, rgx);
|
||||||
|
}
|
||||||
|
catch (const boost::regex_error& err)
|
||||||
|
{
|
||||||
|
std::cerr << err.what() << '\n'; //TODO: Better error handling
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t String::count(const String& str) const
|
uint32_t String::count(const String& str) const
|
||||||
{
|
{
|
||||||
Tokens tok = tokenize(str, false, true);
|
Tokens tok = tokenize(str, false, true);
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ namespace ostd
|
||||||
bool contains(const String& str) const;
|
bool contains(const String& str) const;
|
||||||
bool startsWith(const String& str) const;
|
bool startsWith(const String& str) const;
|
||||||
bool endsWith(const String& str) const;
|
bool endsWith(const String& str) const;
|
||||||
|
bool regexMatches(const String& regex_pattern, bool case_insensitive = false) const;
|
||||||
uint32_t count(const String& str) const;
|
uint32_t count(const String& str) const;
|
||||||
int32_t indexOf(char c, uint32_t start = 0) const;
|
int32_t indexOf(char c, uint32_t start = 0) const;
|
||||||
int32_t indexOf(const String& str, uint32_t start = 0) const;
|
int32_t indexOf(const String& str, uint32_t start = 0) const;
|
||||||
|
|
|
||||||
|
|
@ -51,21 +51,22 @@ class Window : public ogfx::gui::Window
|
||||||
{
|
{
|
||||||
m_label1.setPosition(100, 200);
|
m_label1.setPosition(100, 200);
|
||||||
m_label1.setText("Hello World!");
|
m_label1.setText("Hello World!");
|
||||||
m_label1.setMouseEnteredCallback([&](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);
|
// m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkBlue, false);
|
||||||
this->setCursor(eCursor::Move);
|
// this->setCursor(eCursor::Move);
|
||||||
});
|
// });
|
||||||
m_label1.setMouseExitedCallback([&](const ogfx::gui::Event& event) -> void {
|
// m_label1.setMouseExitedCallback([&](const ogfx::gui::Event& event) -> void {
|
||||||
m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkRed, false);
|
// m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkRed, false);
|
||||||
this->setCursor(eCursor::Default);
|
// this->setCursor(eCursor::Default);
|
||||||
});
|
// });
|
||||||
m_label1.setMouseDraggedCallback([&](const ogfx::gui::Event& event) -> void {
|
// m_label1.setMouseDraggedCallback([&](const ogfx::gui::Event& event) -> void {
|
||||||
m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkGreen, false);
|
// m_label1.applyThemeValue(m_theme, "label.backgroundColor", ostd::Colors::DarkGreen, false);
|
||||||
});
|
// });
|
||||||
addWidget(m_label1);
|
addWidget(m_label1);
|
||||||
|
|
||||||
m_label2.setPosition(100, 400);
|
m_label2.setPosition(100, 400);
|
||||||
m_label2.setText("Ciccia Bella!");
|
m_label2.setText("Ciccia Bella!");
|
||||||
|
m_label2.setThemeID("testLabel");
|
||||||
addWidget(m_label2);
|
addWidget(m_label2);
|
||||||
|
|
||||||
m_theme.loadFromFile("./testTheme.txt");
|
m_theme.loadFromFile("./testTheme.txt");
|
||||||
|
|
@ -90,7 +91,7 @@ class Window : public ogfx::gui::Window
|
||||||
private:
|
private:
|
||||||
ogfx::gui::widgets::Label m_label1 { *this };
|
ogfx::gui::widgets::Label m_label1 { *this };
|
||||||
ogfx::gui::widgets::Label m_label2 { *this };
|
ogfx::gui::widgets::Label m_label2 { *this };
|
||||||
ogfx::gui::Theme m_theme = ogfx::gui::Theme();
|
ostd::Stylesheet m_theme;
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue