Added macros to stylesheet

This commit is contained in:
OmniaX-Dev 2026-05-03 13:49:30 +02:00
parent 203a305633
commit fa638d2556
7 changed files with 333 additions and 15 deletions

View file

@ -9,9 +9,17 @@ const $darkColor = #223322FF
const $accentColorLight = #87FF5CFF const $accentColorLight = #87FF5CFF
const $accentColorDark = #3B7500FF const $accentColorDark = #3B7500FF
const $backgroundColor = rgba(30, 30, 30, 255) const $backgroundColor = rgba(30, 30, 30, 255)
const $crimsonAccent = rgba(220, 20, 60, 150)
% =================== % ===================
macro set_focus_border(color, test = "ciccio,ciao", width = 1, width2 = false) {
focusBorderColor = $color;
showFocusBorder = $width2;
focusBorderWidth = $width;
}
% ====== Root Widget ====== % ====== Root Widget ======
(window) { (window) {
backgroundColor = $backgroundColor backgroundColor = $backgroundColor
@ -383,5 +391,6 @@ const $backgroundColor = rgba(30, 30, 30, 255)
branchLineColor = $textColor branchLineColor = $textColor
showBranchLines = true showBranchLines = true
branchLineThickness = 1.0 branchLineThickness = 1.0
set_focus_border(#FF0000.lighten(0.5), "bella balla", 5)
} }
% ====================== % ======================

View file

@ -56,6 +56,7 @@ Add ToolBar::addButton overload that takes a ogfx::Icon wrapper
Redesign theme overrides to be more robust Redesign theme overrides to be more robust
FIX: Animated icons not working in TreeView FIX: Animated icons not working in TreeView
Add modifiers to Event.keyboard path Add modifiers to Event.keyboard path
Check if possible, then change all WindowCore& refs to Window&

View file

@ -304,6 +304,7 @@ namespace ogfx
push_panel(m_data.entries, pos, false); push_panel(m_data.entries, pos, false);
m_visible = true; m_visible = true;
m_animClock.startCount(ostd::eTimeUnits::Milliseconds); m_animClock.startCount(ostd::eTimeUnits::Milliseconds);
cast<Window&>(m_window).getFocusManager().clearFocus();
} }
void ContextMenu::hide(void) void ContextMenu::hide(void)

View file

@ -39,10 +39,10 @@ namespace ogfx
Widget::~Widget(void) Widget::~Widget(void)
{ {
if (m_window && m_tabIndex >= 0) if (m_window && m_tabIndex >= 0)
cast<Window&>(*m_window).getFocusManager().unregisterTabIndex(*this); cast<Window&>(*m_window).getFocusManager().unregisterTabIndex(*this);
auto& fm = cast<Window&>(*m_window).getFocusManager(); auto& fm = cast<Window&>(*m_window).getFocusManager();
if (fm.getFocused() == this) if (fm.getFocused() == this)
fm.clearFocus(); // you'd add this method fm.clearFocus(); // you'd add this method
} }
bool Widget::addWidget(Widget& child, const Vec2& position, bool __skip_callback) bool Widget::addWidget(Widget& child, const Vec2& position, bool __skip_callback)
@ -316,9 +316,8 @@ namespace ogfx
if (m_showBorder) if (m_showBorder)
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth); gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth);
afterDraw(gfx); afterDraw(gfx);
if (isFocused() && isFocusBorderEnabled())
if (isFocused()) gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_focusBorderColor, m_borderRadius, m_focusBorderWidth);
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, Colors::Red, m_borderRadius, 3);
} }
void Widget::__update(void) void Widget::__update(void)
@ -527,6 +526,9 @@ namespace ogfx
setBorderWidth(getThemeValue<i32>(theme, "borderWidth", m_borderWidth)); setBorderWidth(getThemeValue<i32>(theme, "borderWidth", m_borderWidth));
enableBorder(getThemeValue<bool>(theme, "showBorder", m_showBorder)); enableBorder(getThemeValue<bool>(theme, "showBorder", m_showBorder));
setBorderColor(getThemeValue<Color>(theme, "borderColor", m_borderColor)); setBorderColor(getThemeValue<Color>(theme, "borderColor", m_borderColor));
enableFocusBorder(getThemeValue<bool>(theme, "showFocusBorder", m_showFocusBorder));
setFocusBorderColor(getThemeValue<Color>(theme, "focusBorderColor", m_focusBorderColor));
setFocusBorderWidth(getThemeValue<i32>(theme, "focusBorderWidth", m_focusBorderWidth));
enableBackground(getThemeValue<bool>(theme, "showBackground", m_showBackground)); enableBackground(getThemeValue<bool>(theme, "showBackground", m_showBackground));
setPadding(getThemeValue<Rectangle>(theme, "padding", m_padding)); setPadding(getThemeValue<Rectangle>(theme, "padding", m_padding));
setMargin(getThemeValue<Rectangle>(theme, "margin", m_margin)); setMargin(getThemeValue<Rectangle>(theme, "margin", m_margin));

View file

@ -172,8 +172,10 @@ namespace ogfx
OSTD_PARAM_GETSET(Color, TextColor, m_textColor); OSTD_PARAM_GETSET(Color, TextColor, m_textColor);
OSTD_PARAM_GETSET(Color, BackgroundColor, m_backgroundColor); OSTD_PARAM_GETSET(Color, BackgroundColor, m_backgroundColor);
OSTD_PARAM_GETSET(Color, BorderColor, m_borderColor); OSTD_PARAM_GETSET(Color, BorderColor, m_borderColor);
OSTD_PARAM_GETSET(Color, FocusBorderColor, m_focusBorderColor);
OSTD_PARAM_GETSET(i32, BorderRadius, m_borderRadius); OSTD_PARAM_GETSET(i32, BorderRadius, m_borderRadius);
OSTD_PARAM_GETSET(i32, BorderWidth, m_borderWidth); OSTD_PARAM_GETSET(i32, BorderWidth, m_borderWidth);
OSTD_PARAM_GETSET(i32, FocusBorderWidth, m_focusBorderWidth);
OSTD_PARAM_GETSET(i32, FontSize, m_fontSize); OSTD_PARAM_GETSET(i32, FontSize, m_fontSize);
OSTD_PARAM_GETSET(ostd::ColorGradient, BackgroundGradient, m_backgroundGradient); OSTD_PARAM_GETSET(ostd::ColorGradient, BackgroundGradient, m_backgroundGradient);
OSTD_PARAM_GETSET(Rectangle, Padding, m_padding); OSTD_PARAM_GETSET(Rectangle, Padding, m_padding);
@ -199,6 +201,7 @@ namespace ogfx
OSTD_BOOL_PARAM_GETSET_E(DragAndDrop, m_acceptDragAndDrop); OSTD_BOOL_PARAM_GETSET_E(DragAndDrop, m_acceptDragAndDrop);
OSTD_BOOL_PARAM_GETSET_E(Background, m_showBackground); OSTD_BOOL_PARAM_GETSET_E(Background, m_showBackground);
OSTD_BOOL_PARAM_GETSET_E(Border, m_showBorder); OSTD_BOOL_PARAM_GETSET_E(Border, m_showBorder);
OSTD_BOOL_PARAM_GETSET_E(FocusBorder, m_showFocusBorder);
OSTD_BOOL_PARAM_GETSET_E(ManualDraw, m_manualDraw); OSTD_BOOL_PARAM_GETSET_E(ManualDraw, m_manualDraw);
OSTD_BOOL_PARAM_GETSET_E(TopMost, m_topMost); OSTD_BOOL_PARAM_GETSET_E(TopMost, m_topMost);
OSTD_BOOL_PARAM_GETSET_E(IgnoreScroll, m_ignoreScroll); OSTD_BOOL_PARAM_GETSET_E(IgnoreScroll, m_ignoreScroll);
@ -300,6 +303,9 @@ namespace ogfx
i32 m_borderWidth { 2 }; i32 m_borderWidth { 2 };
Color m_borderColor { 255, 255, 255 }; Color m_borderColor { 255, 255, 255 };
bool m_showBorder { false }; bool m_showBorder { false };
i32 m_focusBorderWidth { 2 };
Color m_focusBorderColor { 0, 150, 20 };
bool m_showFocusBorder { false };
Color m_backgroundColor { Colors::Transparent }; Color m_backgroundColor { Colors::Transparent };
bool m_showBackground { false }; bool m_showBackground { false };
i32 m_fontSize { 20 }; i32 m_fontSize { 20 };

View file

@ -22,6 +22,7 @@
#include "FileSystem.hpp" #include "FileSystem.hpp"
#include "Logger.hpp" #include "Logger.hpp"
#include <algorithm> #include <algorithm>
#include <optional>
namespace ostd namespace ostd
{ {
@ -65,6 +66,8 @@ namespace ostd
bool debug_print = false; bool debug_print = false;
bool brokenLine = false; bool brokenLine = false;
String brokenLineBuffer = ""; String brokenLineBuffer = "";
String macroCode = "";
bool inMacro = false;
for (auto& line : lines) for (auto& line : lines)
{ {
lineNumber++; lineNumber++;
@ -87,6 +90,30 @@ namespace ostd
brokenLineBuffer = line.new_trim().substr(0, line.len() - 1).trim(); brokenLineBuffer = line.new_trim().substr(0, line.len() - 1).trim();
continue; continue;
} }
if (inMacro)
{
for (const auto& c : line)
{
macroCode += c;
if (c == '}')
{
inMacro = false;
macroCode.trim();
if (!parseMacro(macroCode))
l_warn("Invalid macro");
macroCode = "";
break;
}
}
continue;
}
if (line.startsWith("macro "))
{
line.substr(6).trim();
macroCode += line;
inMacro = true;
continue;
}
if (line.startsWith("const ") || line.startsWith("$")) if (line.startsWith("const ") || line.startsWith("$"))
{ {
bool is_const = false; bool is_const = false;
@ -249,14 +276,6 @@ namespace ostd
bool Stylesheet::parseThemeFileLine(const String& line, const VariableList& variables, bool exitCondition) bool Stylesheet::parseThemeFileLine(const String& line, const VariableList& variables, bool exitCondition)
{ {
if (!line.contains("="))
return false;
String key = line.new_substr(0, line.indexOf("=")).trim();
String valuePreserveCase = line.new_substr(line.indexOf("=") + 1).trim();
String value = valuePreserveCase.new_toLower();
if (key == "")
return false;
String themeID = "";
auto l_isVec2 = [this](String& value) -> bool { auto l_isVec2 = [this](String& value) -> bool {
value.trim(); value.trim();
if (value.new_toLower().startsWith("vec2(") && value.new_toLower().endsWith(")")) if (value.new_toLower().startsWith("vec2(") && value.new_toLower().endsWith(")"))
@ -324,6 +343,55 @@ namespace ostd
} }
return false; return false;
}; };
auto l_findMacroCall = [this](String& value, String& outKey) -> std::optional<Macro> {
for (const auto& m : m_macros)
{
stdvec<String> matches;
stdvec<u32> indices = value.regexFind("^(.+\\.)?" + m.first + "[ \\t]*\\(", false, &matches);
if (!indices.empty())
{
u32 paren_index = indices[0] + matches[0].len() - 1;
String from_paren = value.new_substr(paren_index);
String match_text = value.new_substr(indices[0], paren_index);
i32 dot_index = match_text.lastIndexOf('.');
if (dot_index >= 0)
outKey = value.new_substr(0, dot_index);
if (from_paren.startsWith("(") && from_paren.endsWith(")"))
{
from_paren.substr(1, from_paren.len() - 1).trim();
value = from_paren;
return m.second;
}
return std::nullopt;
}
}
return std::nullopt;
};
{
String tmpLine = line.new_trim(), key = "";
if (auto m = l_findMacroCall(tmpLine, key); m != std::nullopt)
{
auto& macro = m.value();
auto expanded = parseMacroCall(tmpLine, macro);
for (const auto& l : expanded)
{
if (!parseThemeFileLine(key + "." + l, variables, false))
return false;
}
return true;
}
}
if (!line.contains("="))
return false;
String key = line.new_substr(0, line.indexOf("=")).trim();
String valuePreserveCase = line.new_substr(line.indexOf("=") + 1).trim();
String value = valuePreserveCase.new_toLower();
if (key == "")
return false;
String themeID = "";
if (key.startsWith("@")) if (key.startsWith("@"))
{ {
@ -708,6 +776,224 @@ namespace ostd
return lineCopy.trim(); return lineCopy.trim();
} }
bool Stylesheet::parseMacro(const String& macroCode)
{
auto l_isWhiteSpace = [](char c) { return c == ' ' || c == '\n' || c == '\t'; };
String name = "";
String params = "";
String body = "";
i32 index = 0;
bool valid = false;
for ( ; index < macroCode.len(); index++)
{
char c = macroCode[index];
if (l_isWhiteSpace(c))
continue;
if (c == '(')
{
index++;
if (index == macroCode.len())
return false;
valid = true;
break;
}
name += c;
}
if (!valid) return false;
if (!name.regexMatches(m_validNameRegex))
return false;
valid = false;
bool inStr = false;
for ( ; index < macroCode.len(); index++)
{
char c = macroCode[index];
if (inStr)
{
params += c;
inStr = inStr && c != '"';
continue;
}
if (l_isWhiteSpace(c))
continue;
if (c == '"')
{
params += c;
inStr = true;
continue;
}
if (c == ',')
{
params += '\n';
continue;
}
if (c == ')')
{
index++;
if (index == macroCode.len())
return false;
valid = true;
break;
}
params += c;
}
if (!valid) return false;
valid = false;
inStr = false;
bool open = false;
for ( ; index < macroCode.len(); index++)
{
char c = macroCode[index];
if (!open)
{
if (c == '{')
open = true;
continue;
}
if (inStr)
{
body += c;
inStr = inStr && c != '"';
continue;
}
if (l_isWhiteSpace(c))
continue;
if (c == '"')
{
body += c;
inStr = true;
continue;
}
if (c == ';')
{
body += '\n';
continue;
}
if (c == '}')
{
index++;
if (index != macroCode.len())
return false;
valid = true;
break;
}
body += c;
}
if (!valid) return false;
Macro macro;
macro.body = body;
auto l_paramExists = [](const String& param, const stdvec<std::pair<String, String>>& list) -> bool {
for (const auto& p : list)
{
if (p.first == param)
return true;
}
return false;
};
auto tokens = params.tokenize("\n");
bool defaultVal = false;
for (auto& tok : tokens)
{
if (tok.contains("="))
{
if (tok.count("=") != 1)
return false;
i32 i = tok.indexOf("=");
if (i < 1 || i > tok.len() - 2)
return false;
String pname = tok.new_substr(0, i).trim();
String pval = tok.new_substr(i + 1).trim();
if (pname == "" || pval == "")
return false;
if (!pname.regexMatches(m_validNameRegex))
return false;
if (l_paramExists("$" + pname, macro.params))
return false;
macro.params.push_back({ "$" + pname, pval });
defaultVal = true;
}
else
{
if (defaultVal)
return false;
if (tok == "")
return false;
if (!tok.regexMatches(m_validNameRegex))
return false;
if (l_paramExists("$" + tok, macro.params))
return false;
macro.params.push_back({ "$" + tok, MacroParamDefault });
}
}
// std::sort(macro.params.begin(), macro.params.end(), [](const auto& a, const auto& b) {
// return a.first.len() > b.first.len();
// });
m_macros[name] = macro;
return true;
}
stdvec<String> Stylesheet::parseMacroCall(const String& call, const Macro& macro)
{
stdvec<String> lines;
auto l_splitByTopLevelComma = [](const String& call) -> stdvec<String> {
stdvec<String> callArgs;
i32 openP = 0;
String arg = "";
for (i32 i = 0; i < call.len(); i++)
{
char c = call[i];
if (c == '(')
openP++;
if (c == ')')
openP--;
if (openP < 0)
return {};
if (c == ',' && openP == 0)
{
if (arg.trim() == "")
return {};
callArgs.push_back(arg);
arg = "";
continue;
}
arg += c;
}
if (arg.trim() == "")
return {};
callArgs.push_back(arg);
return callArgs;
};
auto callArgs = l_splitByTopLevelComma(call);
auto tokens = macro.body.tokenize("\n");
for (auto& line : tokens)
{
i32 argIndex = 0;
for (const auto&[pname, pval] : macro.params)
{
if (argIndex < callArgs.size())
{
line.replaceAll(pname, callArgs[argIndex++]);
std::cout << pname << " " << callArgs[argIndex - 1] << "\n " << line << "\n";
continue;
}
if (pval == MacroParamDefault)
return {};
line.replaceAll(pname, pval);
}
std::cout << "\n";
lines.push_back(line);
}
return lines;
}
void Stylesheet::debugPrint(void) void Stylesheet::debugPrint(void)
{ {
for (const auto&[key, value] : m_values) for (const auto&[key, value] : m_values)

View file

@ -24,6 +24,7 @@
#include <ostd/data/Color.hpp> #include <ostd/data/Color.hpp>
#include <ostd/math/Geometry.hpp> #include <ostd/math/Geometry.hpp>
#include <ostd/data/AnimationData.hpp> #include <ostd/data/AnimationData.hpp>
#include <ostd/io/StaticHashMap.hpp>
#include <variant> #include <variant>
@ -34,6 +35,13 @@ namespace ostd
public: using QualifierList = stdvec<std::pair<const String, bool>>; public: using QualifierList = stdvec<std::pair<const String, bool>>;
public: using VariableList = stdumap<String, std::pair<String, bool>>; public: using VariableList = stdumap<String, std::pair<String, bool>>;
public: using TypeVariant = std::variant<i32, f32, bool, String, Color, Rectangle, Vec2, ColorGradient, AnimationData>; public: using TypeVariant = std::variant<i32, f32, bool, String, Color, Rectangle, Vec2, ColorGradient, AnimationData>;
private: struct Macro
{
String body { "" };
stdvec<std::pair<String, String>> params;
inline i32 argCount(void) const { return params.size(); }
};
public: public:
Stylesheet(void); Stylesheet(void);
Stylesheet& clear(void); Stylesheet& clear(void);
@ -82,9 +90,14 @@ namespace ostd
ColorGradient parseColorGradient(const String& _value, const VariableList& variables); ColorGradient parseColorGradient(const String& _value, const VariableList& variables);
AnimationData parseAnim(const String& _value, bool& outError, const VariableList& variables); AnimationData parseAnim(const String& _value, bool& outError, const VariableList& variables);
String replaceVariables(const String& line, const VariableList& variables, bool stop_at_first_match = true); String replaceVariables(const String& line, const VariableList& variables, bool stop_at_first_match = true);
bool parseMacro(const String& macroCode);
stdvec<String> parseMacroCall(const String& call, const Macro& macro);
private: private:
stdumap<String, TypeVariant> m_values; stdumap<String, TypeVariant> m_values;
stdumap<String, Macro> m_macros;
const String m_validNameRegex { "^[A-Za-z_][A-Za-z0-9_]*$" }; const String m_validNameRegex { "^[A-Za-z_][A-Za-z0-9_]*$" };
inline static const String MacroParamDefault { "NO_VAL" };
}; };
} }