Added macros to stylesheet
This commit is contained in:
parent
203a305633
commit
fa638d2556
7 changed files with 333 additions and 15 deletions
|
|
@ -9,9 +9,17 @@ const $darkColor = #223322FF
|
|||
const $accentColorLight = #87FF5CFF
|
||||
const $accentColorDark = #3B7500FF
|
||||
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 ======
|
||||
(window) {
|
||||
backgroundColor = $backgroundColor
|
||||
|
|
@ -383,5 +391,6 @@ const $backgroundColor = rgba(30, 30, 30, 255)
|
|||
branchLineColor = $textColor
|
||||
showBranchLines = true
|
||||
branchLineThickness = 1.0
|
||||
set_focus_border(#FF0000.lighten(0.5), "bella balla", 5)
|
||||
}
|
||||
% ======================
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ Add ToolBar::addButton overload that takes a ogfx::Icon wrapper
|
|||
Redesign theme overrides to be more robust
|
||||
FIX: Animated icons not working in TreeView
|
||||
Add modifiers to Event.keyboard path
|
||||
Check if possible, then change all WindowCore& refs to Window&
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -304,6 +304,7 @@ namespace ogfx
|
|||
push_panel(m_data.entries, pos, false);
|
||||
m_visible = true;
|
||||
m_animClock.startCount(ostd::eTimeUnits::Milliseconds);
|
||||
cast<Window&>(m_window).getFocusManager().clearFocus();
|
||||
}
|
||||
|
||||
void ContextMenu::hide(void)
|
||||
|
|
|
|||
|
|
@ -316,9 +316,8 @@ namespace ogfx
|
|||
if (m_showBorder)
|
||||
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth);
|
||||
afterDraw(gfx);
|
||||
|
||||
if (isFocused())
|
||||
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, Colors::Red, m_borderRadius, 3);
|
||||
if (isFocused() && isFocusBorderEnabled())
|
||||
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_focusBorderColor, m_borderRadius, m_focusBorderWidth);
|
||||
}
|
||||
|
||||
void Widget::__update(void)
|
||||
|
|
@ -527,6 +526,9 @@ namespace ogfx
|
|||
setBorderWidth(getThemeValue<i32>(theme, "borderWidth", m_borderWidth));
|
||||
enableBorder(getThemeValue<bool>(theme, "showBorder", m_showBorder));
|
||||
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));
|
||||
setPadding(getThemeValue<Rectangle>(theme, "padding", m_padding));
|
||||
setMargin(getThemeValue<Rectangle>(theme, "margin", m_margin));
|
||||
|
|
|
|||
|
|
@ -172,8 +172,10 @@ namespace ogfx
|
|||
OSTD_PARAM_GETSET(Color, TextColor, m_textColor);
|
||||
OSTD_PARAM_GETSET(Color, BackgroundColor, m_backgroundColor);
|
||||
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, BorderWidth, m_borderWidth);
|
||||
OSTD_PARAM_GETSET(i32, FocusBorderWidth, m_focusBorderWidth);
|
||||
OSTD_PARAM_GETSET(i32, FontSize, m_fontSize);
|
||||
OSTD_PARAM_GETSET(ostd::ColorGradient, BackgroundGradient, m_backgroundGradient);
|
||||
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(Background, m_showBackground);
|
||||
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(TopMost, m_topMost);
|
||||
OSTD_BOOL_PARAM_GETSET_E(IgnoreScroll, m_ignoreScroll);
|
||||
|
|
@ -300,6 +303,9 @@ namespace ogfx
|
|||
i32 m_borderWidth { 2 };
|
||||
Color m_borderColor { 255, 255, 255 };
|
||||
bool m_showBorder { false };
|
||||
i32 m_focusBorderWidth { 2 };
|
||||
Color m_focusBorderColor { 0, 150, 20 };
|
||||
bool m_showFocusBorder { false };
|
||||
Color m_backgroundColor { Colors::Transparent };
|
||||
bool m_showBackground { false };
|
||||
i32 m_fontSize { 20 };
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "FileSystem.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
namespace ostd
|
||||
{
|
||||
|
|
@ -65,6 +66,8 @@ namespace ostd
|
|||
bool debug_print = false;
|
||||
bool brokenLine = false;
|
||||
String brokenLineBuffer = "";
|
||||
String macroCode = "";
|
||||
bool inMacro = false;
|
||||
for (auto& line : lines)
|
||||
{
|
||||
lineNumber++;
|
||||
|
|
@ -87,6 +90,30 @@ namespace ostd
|
|||
brokenLineBuffer = line.new_trim().substr(0, line.len() - 1).trim();
|
||||
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("$"))
|
||||
{
|
||||
bool is_const = false;
|
||||
|
|
@ -249,14 +276,6 @@ namespace ostd
|
|||
|
||||
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 {
|
||||
value.trim();
|
||||
if (value.new_toLower().startsWith("vec2(") && value.new_toLower().endsWith(")"))
|
||||
|
|
@ -324,6 +343,55 @@ namespace ostd
|
|||
}
|
||||
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("@"))
|
||||
{
|
||||
|
|
@ -708,6 +776,224 @@ namespace ostd
|
|||
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)
|
||||
{
|
||||
for (const auto&[key, value] : m_values)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <ostd/data/Color.hpp>
|
||||
#include <ostd/math/Geometry.hpp>
|
||||
#include <ostd/data/AnimationData.hpp>
|
||||
#include <ostd/io/StaticHashMap.hpp>
|
||||
#include <variant>
|
||||
|
||||
|
||||
|
|
@ -34,6 +35,13 @@ namespace ostd
|
|||
public: using QualifierList = stdvec<std::pair<const 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>;
|
||||
private: struct Macro
|
||||
{
|
||||
String body { "" };
|
||||
stdvec<std::pair<String, String>> params;
|
||||
|
||||
inline i32 argCount(void) const { return params.size(); }
|
||||
};
|
||||
public:
|
||||
Stylesheet(void);
|
||||
Stylesheet& clear(void);
|
||||
|
|
@ -82,9 +90,14 @@ namespace ostd
|
|||
ColorGradient parseColorGradient(const String& _value, 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);
|
||||
bool parseMacro(const String& macroCode);
|
||||
stdvec<String> parseMacroCall(const String& call, const Macro& macro);
|
||||
|
||||
private:
|
||||
stdumap<String, TypeVariant> m_values;
|
||||
stdumap<String, Macro> m_macros;
|
||||
const String m_validNameRegex { "^[A-Za-z_][A-Za-z0-9_]*$" };
|
||||
|
||||
inline static const String MacroParamDefault { "NO_VAL" };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue