diff --git a/data/styles/0-vars.otui b/data/styles/0-vars.otui new file mode 100644 index 0000000..83c687d --- /dev/null +++ b/data/styles/0-vars.otui @@ -0,0 +1,4 @@ +// Labels +$var-font: verdana-11px-antialised +$var-text-color: #dfdfdf +$var-text-color-disabled: #dfdfdf88 \ No newline at end of file diff --git a/data/styles/10-labels.otui b/data/styles/10-labels.otui index 8edfbb1..e0bdb7d 100644 --- a/data/styles/10-labels.otui +++ b/data/styles/10-labels.otui @@ -1,23 +1,23 @@ Label < UILabel - font: verdana-11px-antialised - color: #dfdfdf + font: $var-font + color: $var-text-color $disabled: - color: #dfdfdf88 + color: $var-text-color-disabled FlatLabel < UILabel - font: verdana-11px-antialised - color: #dfdfdf + font: $var-font + color: $var-text-color size: 86 20 text-offset: 3 3 image-source: /images/ui/panel_flat image-border: 1 $disabled: - color: #dfdfdf88 + color: $var-text-color-disabled MenuLabel < Label GameLabel < UILabel - font: verdana-11px-antialised - color: #dfdfdf + font: $var-font + color: $var-text-color diff --git a/src/framework/luaengine/luavaluecasts.cpp b/src/framework/luaengine/luavaluecasts.cpp index 7627d90..659a4cf 100644 --- a/src/framework/luaengine/luavaluecasts.cpp +++ b/src/framework/luaengine/luavaluecasts.cpp @@ -82,7 +82,7 @@ int push_luavalue(const std::string& str) bool luavalue_cast(int index, std::string& str) { - str = g_lua.toString(index); + str = g_ui.getOTUIVarSafe(g_lua.toString(index)); return true; } @@ -121,7 +121,7 @@ bool luavalue_cast(int index, Color& color) color.setAlpha((int)g_lua.popInteger()); return true; } else if(g_lua.isString()) { - return stdext::cast(g_lua.toString(index), color); + return stdext::cast(g_ui.getOTUIVarSafe(g_lua.toString(index)), color); } else if(g_lua.isNil()) { color = Color::white; return true; @@ -157,7 +157,7 @@ bool luavalue_cast(int index, Rect& rect) rect.setHeight(g_lua.popInteger()); return true; } else if(g_lua.isString()) { - return stdext::cast(g_lua.toString(index), rect); + return stdext::cast(g_ui.getOTUIVarSafe(g_lua.toString(index)), rect); } else if(g_lua.isNil()) { rect = Rect(); return true; @@ -185,7 +185,7 @@ bool luavalue_cast(int index, Point& point) point.y = g_lua.popInteger(); return true; } else if(g_lua.isString()) { - return stdext::cast(g_lua.toString(index), point); + return stdext::cast(g_ui.getOTUIVarSafe(g_lua.toString(index)), point); } else if(g_lua.isNil()) { point = Point(); return true; @@ -213,7 +213,7 @@ bool luavalue_cast(int index, Size& size) size.setHeight(g_lua.popInteger()); return true; } else if(g_lua.isString()) { - return stdext::cast(g_lua.toString(index), size); + return stdext::cast(g_ui.getOTUIVarSafe(g_lua.toString(index)), size); } else if(g_lua.isNil()) { size = Size(); return true; diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 4ea85c5..4c6741b 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -422,6 +422,10 @@ void Application::registerLuaFunctions() g_lua.bindSingletonFunction("g_ui", "isDrawingDebugBoxes", &UIManager::isDrawingDebugBoxes, &g_ui); g_lua.bindSingletonFunction("g_ui", "isMouseGrabbed", &UIManager::isMouseGrabbed, &g_ui); g_lua.bindSingletonFunction("g_ui", "isKeyboardGrabbed", &UIManager::isKeyboardGrabbed, &g_ui); + g_lua.bindSingletonFunction("g_ui", "hasOTUIVar", &UIManager::hasOTUIVar, &g_ui); + g_lua.bindSingletonFunction("g_ui", "getOTUIVar", &UIManager::getOTUIVar, &g_ui); + g_lua.bindSingletonFunction("g_ui", "getOTUIVarSafe", &UIManager::getOTUIVarSafe, &g_ui); + g_lua.bindSingletonFunction("g_ui", "addOTUIVar", &UIManager::addOTUIVar, &g_ui); // FontManager g_lua.registerSingletonClass("g_fonts"); diff --git a/src/framework/otml/declarations.h b/src/framework/otml/declarations.h index 64c01af..c030fb3 100644 --- a/src/framework/otml/declarations.h +++ b/src/framework/otml/declarations.h @@ -33,5 +33,6 @@ class OTMLEmitter; typedef stdext::shared_object_ptr OTMLNodePtr; typedef stdext::shared_object_ptr OTMLDocumentPtr; typedef std::vector OTMLNodeList; +typedef std::unordered_map OTUIVars; #endif diff --git a/src/framework/otml/otmlnode.h b/src/framework/otml/otmlnode.h index 8d52146..251d54e 100644 --- a/src/framework/otml/otmlnode.h +++ b/src/framework/otml/otmlnode.h @@ -24,6 +24,7 @@ #define OTMLNODE_H #include "declarations.h" +#include class OTMLNode : public stdext::shared_object { @@ -73,7 +74,7 @@ class OTMLNode : public stdext::shared_object OTMLNodePtr clone(); template - T value(); + T value(bool raw = false); template T valueAt(const std::string& childTag); template @@ -110,7 +111,7 @@ class OTMLNode : public stdext::shared_object #include "otmlexception.h" template<> -inline std::string OTMLNode::value() { +inline std::string OTMLNode::value(bool raw) { std::string value = m_value; if(stdext::starts_with(value, "\"") && stdext::ends_with(value, "\"")) { value = value.substr(1, value.length()-2); @@ -120,13 +121,23 @@ inline std::string OTMLNode::value() { stdext::replace_all(value, "\\n", "\n"); stdext::replace_all(value, "\\'", "\'"); } + if (!raw) { + if (stdext::starts_with(value, "$var-")) { + return g_ui.getOTUIVar(value); + } + } return value; } template -T OTMLNode::value() { +T OTMLNode::value(bool raw) { T ret; - if(!stdext::cast(m_value, ret)) + if (stdext::starts_with(m_value, "$var-")) { + if (!stdext::cast(g_ui.getOTUIVar(m_value), ret)) + throw OTMLException(asOTMLNode(), stdext::format("failed to cast var value '%s' (var value '%s') to type '%s'", m_value, g_ui.getOTUIVar(m_value), stdext::demangle_type())); + return ret; + } + if (!stdext::cast(m_value, ret)) throw OTMLException(asOTMLNode(), stdext::format("failed to cast node value '%s' to type '%s'", m_value, stdext::demangle_type())); return ret; } diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index 44a593f..eed7019 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -22,6 +22,7 @@ #include "uimanager.h" #include "ui.h" +#include "uiwidget.h" #include #include @@ -40,6 +41,7 @@ void UIManager::init() m_rootWidget->setId("root"); m_mouseReceiver = m_rootWidget; m_keyboardReceiver = m_rootWidget; + m_vars.reserve(100); } void UIManager::terminate() @@ -56,6 +58,7 @@ void UIManager::terminate() m_styles.clear(); m_destroyedWidgets.clear(); m_checkEvent = nullptr; + m_vars.clear(); } void UIManager::render(Fw::DrawPane drawPane) @@ -371,6 +374,15 @@ bool UIManager::importStyleFromString(std::string data) void UIManager::importStyleFromOTML(const OTMLNodePtr& styleNode) { std::string tag = styleNode->tag(); + + // parse otui variable + if (stdext::starts_with(tag, "$var-")) { + std::string var = tag.substr(6); + if (!hasOTUIVar(var)) + addOTUIVar(var, styleNode->rawValue()); + return; + } + std::vector split = stdext::split(tag, "<"); if(split.size() != 2) throw OTMLException(styleNode, "not a valid style declaration"); diff --git a/src/framework/ui/uimanager.h b/src/framework/ui/uimanager.h index b89193d..7fb15f2 100644 --- a/src/framework/ui/uimanager.h +++ b/src/framework/ui/uimanager.h @@ -24,10 +24,11 @@ #define UIMANAGER_H #include "declarations.h" -#include "uiwidget.h" #include #include +class UIWidget; + //@bindsingleton g_ui class UIManager { @@ -72,6 +73,36 @@ class UIManager bool isDrawingDebugBoxes() { return m_drawDebugBoxes; } + const OTUIVars& getOTUIVars() { + return m_vars; + } + bool hasOTUIVar(const std::string& key) { + if (stdext::starts_with(key, "$var-")) { + return m_vars.find(key.substr(6)) != m_vars.end(); + } + return m_vars.find(key) != m_vars.end(); + } + std::string getOTUIVar(const std::string& key) { + if (stdext::starts_with(key, "$var-")) { + return m_vars[key.substr(6)]; + } + return m_vars[key]; + } + std::string getOTUIVarSafe(const std::string& key) { + if (!hasOTUIVar(key)) { + return key; + } + if (stdext::starts_with(key, "$var-")) { + return m_vars[key.substr(6)]; + } + return m_vars[key]; + } + void addOTUIVar(const std::string& key, const std::string& value) { + if (m_vars.find(key) != m_vars.end()) return; + + m_vars.insert(std::make_pair(key, value)); + } + protected: void onWidgetAppear(const UIWidgetPtr& widget); void onWidgetDisappear(const UIWidgetPtr& widget); @@ -89,6 +120,7 @@ class UIManager stdext::boolean m_hoverUpdateScheduled; stdext::boolean m_drawDebugBoxes; std::unordered_map m_styles; + OTUIVars m_vars; UIWidgetList m_destroyedWidgets; ScheduledEventPtr m_checkEvent; stdext::timer m_moveTimer; diff --git a/src/framework/ui/uiwidgetbasestyle.cpp b/src/framework/ui/uiwidgetbasestyle.cpp index 18e6b84..51a829d 100644 --- a/src/framework/ui/uiwidgetbasestyle.cpp +++ b/src/framework/ui/uiwidgetbasestyle.cpp @@ -30,6 +30,7 @@ #include #include #include +#include void UIWidget::initBaseStyle() { @@ -155,10 +156,10 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode) else if(node->tag() == "clipping") setClipping(node->value()); else if(node->tag() == "border") { - auto split = stdext::split(node->value(), " "); + auto split = stdext::split(node->value(true), " "); if(split.size() == 2) { - setBorderWidth(stdext::safe_cast(split[0])); - setBorderColor(stdext::safe_cast(split[1])); + setBorderWidth(stdext::safe_cast(g_ui.getOTUIVarSafe(split[0]))); + setBorderColor(stdext::safe_cast(g_ui.getOTUIVarSafe(split[1]))); } else throw OTMLException(node, "border param must have its width followed by its color"); } @@ -191,30 +192,30 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode) else if(node->tag() == "margin-left") setMarginLeft(node->value()); else if(node->tag() == "margin") { - std::string marginDesc = node->value(); + std::string marginDesc = node->value(true); std::vector split = stdext::split(marginDesc, " "); if(split.size() == 4) { - setMarginTop(stdext::safe_cast(split[0])); - setMarginRight(stdext::safe_cast(split[1])); - setMarginBottom(stdext::safe_cast(split[2])); - setMarginLeft(stdext::safe_cast(split[3])); + setMarginTop(stdext::safe_cast(g_ui.getOTUIVarSafe(split[0]))); + setMarginRight(stdext::safe_cast(g_ui.getOTUIVarSafe(split[1]))); + setMarginBottom(stdext::safe_cast(g_ui.getOTUIVarSafe(split[2]))); + setMarginLeft(stdext::safe_cast(g_ui.getOTUIVarSafe(split[3]))); } else if(split.size() == 3) { - int marginTop = stdext::safe_cast(split[0]); - int marginHorizontal = stdext::safe_cast(split[1]); - int marginBottom = stdext::safe_cast(split[2]); + int marginTop = stdext::safe_cast(g_ui.getOTUIVarSafe(split[0])); + int marginHorizontal = stdext::safe_cast(g_ui.getOTUIVarSafe(split[1])); + int marginBottom = stdext::safe_cast(g_ui.getOTUIVarSafe(split[2])); setMarginTop(marginTop); setMarginRight(marginHorizontal); setMarginBottom(marginBottom); setMarginLeft(marginHorizontal); } else if(split.size() == 2) { - int marginVertical = stdext::safe_cast(split[0]); - int marginHorizontal = stdext::safe_cast(split[1]); + int marginVertical = stdext::safe_cast(g_ui.getOTUIVarSafe(split[0])); + int marginHorizontal = stdext::safe_cast(g_ui.getOTUIVarSafe(split[1])); setMarginTop(marginVertical); setMarginRight(marginHorizontal); setMarginBottom(marginVertical); setMarginLeft(marginHorizontal); } else if(split.size() == 1) { - int margin = stdext::safe_cast(split[0]); + int margin = stdext::safe_cast(g_ui.getOTUIVarSafe(split[0])); setMarginTop(margin); setMarginRight(margin); setMarginBottom(margin); @@ -230,30 +231,30 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode) else if(node->tag() == "padding-left") setPaddingLeft(node->value()); else if(node->tag() == "padding") { - std::string paddingDesc = node->value(); + std::string paddingDesc = node->value(true); std::vector split = stdext::split(paddingDesc, " "); if(split.size() == 4) { - setPaddingTop(stdext::safe_cast(split[0])); - setPaddingRight(stdext::safe_cast(split[1])); - setPaddingBottom(stdext::safe_cast(split[2])); - setPaddingLeft(stdext::safe_cast(split[3])); + setPaddingTop(stdext::safe_cast(g_ui.getOTUIVarSafe(split[0]))); + setPaddingRight(stdext::safe_cast(g_ui.getOTUIVarSafe(split[1]))); + setPaddingBottom(stdext::safe_cast(g_ui.getOTUIVarSafe(split[2]))); + setPaddingLeft(stdext::safe_cast(g_ui.getOTUIVarSafe(split[3]))); } else if(split.size() == 3) { - int paddingTop = stdext::safe_cast(split[0]); - int paddingHorizontal = stdext::safe_cast(split[1]); - int paddingBottom = stdext::safe_cast(split[2]); + int paddingTop = stdext::safe_cast(g_ui.getOTUIVarSafe(split[0])); + int paddingHorizontal = stdext::safe_cast(g_ui.getOTUIVarSafe(split[1])); + int paddingBottom = stdext::safe_cast(g_ui.getOTUIVarSafe(split[2])); setPaddingTop(paddingTop); setPaddingRight(paddingHorizontal); setPaddingBottom(paddingBottom); setPaddingLeft(paddingHorizontal); } else if(split.size() == 2) { - int paddingVertical = stdext::safe_cast(split[0]); - int paddingHorizontal = stdext::safe_cast(split[1]); + int paddingVertical = stdext::safe_cast(g_ui.getOTUIVarSafe(split[0])); + int paddingHorizontal = stdext::safe_cast(g_ui.getOTUIVarSafe(split[1])); setPaddingTop(paddingVertical); setPaddingRight(paddingHorizontal); setPaddingBottom(paddingVertical); setPaddingLeft(paddingHorizontal); } else if(split.size() == 1) { - int padding = stdext::safe_cast(split[0]); + int padding = stdext::safe_cast(g_ui.getOTUIVarSafe(split[0])); setPaddingTop(padding); setPaddingRight(padding); setPaddingBottom(padding); @@ -315,12 +316,12 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode) if(node->value() == "none") { removeAnchor(anchoredEdge); } else { - std::vector split = stdext::split(node->value(), "."); + std::vector split = stdext::split(node->value(true), "."); if(split.size() != 2) throw OTMLException(node, "invalid anchor description"); - std::string hookedWidgetId = split[0]; - Fw::AnchorEdge hookedEdge = Fw::translateAnchorEdge(split[1]); + std::string hookedWidgetId = g_ui.getOTUIVarSafe(split[0]); + Fw::AnchorEdge hookedEdge = Fw::translateAnchorEdge(g_ui.getOTUIVarSafe(split[1])); if(anchoredEdge == Fw::AnchorNone) throw OTMLException(node, "invalid anchor edge");