/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "Element.h" #include "ElementStyleProxy.h" #include "LuaEventListener.h" #include "ElementAttributesProxy.h" #include "ElementChildNodesProxy.h" #include namespace Rml { namespace Lua { typedef ElementDocument Document; template<> void ExtraInit(lua_State* L, int metatable_index) { int top = lua_gettop(L); //guarantee the "Element.As" table exists lua_getfield(L,metatable_index-1,"As"); if(lua_isnoneornil(L,-1)) //if it doesn't exist, create it { lua_newtable(L); lua_setfield(L,metatable_index-1,"As"); } lua_pop(L,1); //pop the result of lua_getfield lua_pushcfunction(L,Elementnew); lua_setfield(L,metatable_index-1,"new"); lua_settop(L,top); } int Elementnew(lua_State* L) { const char* tag = luaL_checkstring(L,1); Element* ele = new Element(tag); LuaType::push(L,ele,true); return 1; } //methods int ElementAddEventListener(lua_State* L, Element* obj) { int top = lua_gettop(L); bool capture = false; //default false if they didn't pass it in if (top > 2) capture = RMLUI_CHECK_BOOL(L,3); const char* event = luaL_checkstring(L,1); LuaEventListener* listener = nullptr; int type = lua_type(L,2); if(type == LUA_TFUNCTION) { listener = new LuaEventListener(L,2,obj); } else if(type == LUA_TSTRING) { const char* code = luaL_checkstring(L,2); listener = new LuaEventListener(code,obj); } else { Log::Message(Log::LT_WARNING, "Lua Context:AddEventLisener's 2nd argument can only be a Lua function or a string, you passed in a %s", lua_typename(L,type)); } if(listener != nullptr) { obj->AddEventListener(event,listener,capture); } return 0; } int ElementAppendChild(lua_State* L, Element* obj) { ElementPtr* element = LuaType::check(L, 1); if (*element) obj->AppendChild(std::move(*element)); else Log::Message(Log::LT_WARNING, "Could not append child to element '%s', as the child was null. Was it already moved from?", obj->GetAddress().c_str()); return 0; } int ElementBlur(lua_State* /*L*/, Element* obj) { obj->Blur(); return 0; } int ElementClick(lua_State* /*L*/, Element* obj) { obj->Click(); return 0; } int ElementDispatchEvent(lua_State* L, Element* obj) { const char* event = luaL_checkstring(L,1); Dictionary params; lua_pushnil(L); //becauase lua_next pops a key from the stack first, we don't want to pop the table while(lua_next(L,2) != 0) { //[-1] is value, [-2] is key int type = lua_type(L,-1); const char* key = luaL_checkstring(L,-2); //key HAS to be a string, or things will go bad switch(type) { case LUA_TNUMBER: params[key] = (float)lua_tonumber(L,-1); break; case LUA_TBOOLEAN: params[key] = RMLUI_CHECK_BOOL(L,-1); break; case LUA_TSTRING: params[key] = luaL_checkstring(L,-1); break; case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: params[key] = lua_touserdata(L,-1); break; default: break; } lua_pop(L, 1); //pops value, leaves key for next iteration } obj->DispatchEvent(event, params); return 0; } int ElementFocus(lua_State* /*L*/, Element* obj) { obj->Focus(); return 0; } int ElementGetAttribute(lua_State* L, Element* obj) { const char* name = luaL_checkstring(L,1); Variant* var = obj->GetAttribute(name); PushVariant(L,var); return 1; } int ElementGetElementById(lua_State* L, Element* obj) { const char* id = luaL_checkstring(L,1); Element* ele = obj->GetElementById(id); LuaType::push(L,ele,false); return 1; } int ElementGetElementsByTagName(lua_State* L, Element* obj) { const char* tag = luaL_checkstring(L,1); ElementList list; obj->GetElementsByTagName(list,tag); lua_newtable(L); for(unsigned int i = 0; i < list.size(); i++) { PushIndex(L,i); LuaType::push(L,list[i],false); lua_settable(L,-3); //-3 is the table } return 1; } int ElementQuerySelector(lua_State* L, Element* obj) { const char* sel = luaL_checkstring(L,1); Element* ele = obj->QuerySelector(sel); LuaType::push(L,ele,false); return 1; } int ElementQuerySelectorAll(lua_State* L, Element* obj) { const char* tag = luaL_checkstring(L,1); ElementList list; obj->QuerySelectorAll(list,tag); lua_newtable(L); for(unsigned int i = 0; i < list.size(); i++) { PushIndex(L,i); LuaType::push(L,list[i],false); lua_settable(L,-3); //-3 is the table } return 1; } int ElementHasAttribute(lua_State* L, Element* obj) { const char* name = luaL_checkstring(L,1); lua_pushboolean(L,obj->HasAttribute(name)); return 1; } int ElementHasChildNodes(lua_State* L, Element* obj) { lua_pushboolean(L,obj->HasChildNodes()); return 1; } int ElementInsertBefore(lua_State* L, Element* obj) { ElementPtr* element = LuaType::check(L,1); Element* adjacent = LuaType::check(L,2); if(*element) obj->InsertBefore(std::move(*element), adjacent); else Log::Message(Log::LT_WARNING, "Could not insert child to element '%s', as the child was null. Was it already moved from?", obj->GetAddress().c_str()); return 0; } int ElementIsClassSet(lua_State* L, Element* obj) { const char* name = luaL_checkstring(L,1); lua_pushboolean(L,obj->IsClassSet(name)); return 1; } int ElementRemoveAttribute(lua_State* L, Element* obj) { const char* name = luaL_checkstring(L,1); obj->RemoveAttribute(name); return 0; } int ElementRemoveChild(lua_State* L, Element* obj) { Element* element = LuaType::check(L,1); lua_pushboolean(L,static_cast(obj->RemoveChild(element))); return 1; } int ElementReplaceChild(lua_State* L, Element* obj) { ElementPtr* inserted = LuaType::check(L,1); Element* replaced = LuaType::check(L,2); if(*inserted) lua_pushboolean(L, static_cast(obj->ReplaceChild(std::move(*inserted),replaced))); else Log::Message(Log::LT_WARNING, "Could not replace child in element '%s', as the child was null. Was it already moved from?", obj->GetAddress().c_str()); return 1; } int ElementScrollIntoView(lua_State* L, Element* obj) { bool align = RMLUI_CHECK_BOOL(L,1); obj->ScrollIntoView(align); return 0; } int ElementSetAttribute(lua_State* L, Element* obj) { const char* name = luaL_checkstring(L,1); const char* value = luaL_checkstring(L,2); obj->SetAttribute(name,String(value)); return 0; } int ElementSetClass(lua_State* L, Element* obj) { const char* name = luaL_checkstring(L,1); bool value = RMLUI_CHECK_BOOL(L,2); obj->SetClass(name,value); return 0; } //getters int ElementGetAttrattributes(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); ElementAttributesProxy* proxy = new ElementAttributesProxy(); proxy->owner = ele; LuaType::push(L,proxy,true); return 1; } int ElementGetAttrchild_nodes(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); ElementChildNodesProxy* ecnp = new ElementChildNodesProxy(); ecnp->owner = ele; LuaType::push(L,ecnp,true); return 1; } int ElementGetAttrclass_name(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); String classnames = ele->GetClassNames(); lua_pushstring(L,classnames.c_str()); return 1; } int ElementGetAttrclient_left(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetClientLeft()); return 1; } int ElementGetAttrclient_height(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetClientHeight()); return 1; } int ElementGetAttrclient_top(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetClientTop()); return 1; } int ElementGetAttrclient_width(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetClientWidth()); return 1; } int ElementGetAttrfirst_child(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Element* child = ele->GetFirstChild(); if(child == nullptr) lua_pushnil(L); else LuaType::push(L,child,false); return 1; } int ElementGetAttrid(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushstring(L,ele->GetId().c_str()); return 1; } int ElementGetAttrinner_rml(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushstring(L,ele->GetInnerRML().c_str()); return 1; } int ElementGetAttrlast_child(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Element* child = ele->GetLastChild(); if(child == nullptr) lua_pushnil(L); else LuaType::push(L,child,false); return 1; } int ElementGetAttrnext_sibling(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Element* sibling = ele->GetNextSibling(); if(sibling == nullptr) lua_pushnil(L); else LuaType::push(L,sibling,false); return 1; } int ElementGetAttroffset_height(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetOffsetHeight()); return 1; } int ElementGetAttroffset_left(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetOffsetLeft()); return 1; } int ElementGetAttroffset_parent(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Element* parent = ele->GetOffsetParent(); LuaType::push(L,parent,false); return 1; } int ElementGetAttroffset_top(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L, ele->GetOffsetTop()); return 1; } int ElementGetAttroffset_width(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetOffsetWidth()); return 1; } int ElementGetAttrowner_document(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Document* doc = ele->GetOwnerDocument(); LuaType::push(L,doc,false); return 1; } int ElementGetAttrparent_node(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Element* parent = ele->GetParentNode(); if(parent == nullptr) lua_pushnil(L); else LuaType::push(L,parent,false); return 1; } int ElementGetAttrprevious_sibling(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); Element* sibling = ele->GetPreviousSibling(); if(sibling == nullptr) lua_pushnil(L); else LuaType::push(L,sibling,false); return 1; } int ElementGetAttrscroll_height(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetScrollHeight()); return 1; } int ElementGetAttrscroll_left(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetScrollLeft()); return 1; } int ElementGetAttrscroll_top(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetScrollTop()); return 1; } int ElementGetAttrscroll_width(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushnumber(L,ele->GetScrollWidth()); return 1; } int ElementGetAttrstyle(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); ElementStyleProxy* prox = new ElementStyleProxy(); prox->owner = ele; LuaType::push(L,prox,true); return 1; } int ElementGetAttrtag_name(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); lua_pushstring(L,ele->GetTagName().c_str()); return 1; } //setters int ElementSetAttrclass_name(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); const char* name = luaL_checkstring(L,2); ele->SetClassNames(name); return 0; } int ElementSetAttrid(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); const char* id = luaL_checkstring(L,2); ele->SetId(id); return 0; } int ElementSetAttrinner_rml(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); const char* rml = luaL_checkstring(L,2); ele->SetInnerRML(rml); return 0; } int ElementSetAttrscroll_left(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); float scroll = (float)luaL_checknumber(L,2); ele->SetScrollLeft(scroll); return 0; } int ElementSetAttrscroll_top(lua_State* L) { Element* ele = LuaType::check(L,1); RMLUI_CHECK_OBJ(ele); float scroll = (float)luaL_checknumber(L,2); ele->SetScrollTop(scroll); return 0; } RegType ElementMethods[] = { RMLUI_LUAMETHOD(Element,AddEventListener) RMLUI_LUAMETHOD(Element,AppendChild) RMLUI_LUAMETHOD(Element,Blur) RMLUI_LUAMETHOD(Element,Click) RMLUI_LUAMETHOD(Element,DispatchEvent) RMLUI_LUAMETHOD(Element,Focus) RMLUI_LUAMETHOD(Element,GetAttribute) RMLUI_LUAMETHOD(Element,GetElementById) RMLUI_LUAMETHOD(Element,GetElementsByTagName) RMLUI_LUAMETHOD(Element,QuerySelector) RMLUI_LUAMETHOD(Element,QuerySelectorAll) RMLUI_LUAMETHOD(Element,HasAttribute) RMLUI_LUAMETHOD(Element,HasChildNodes) RMLUI_LUAMETHOD(Element,InsertBefore) RMLUI_LUAMETHOD(Element,IsClassSet) RMLUI_LUAMETHOD(Element,RemoveAttribute) RMLUI_LUAMETHOD(Element,RemoveChild) RMLUI_LUAMETHOD(Element,ReplaceChild) RMLUI_LUAMETHOD(Element,ScrollIntoView) RMLUI_LUAMETHOD(Element,SetAttribute) RMLUI_LUAMETHOD(Element,SetClass) { nullptr, nullptr }, }; luaL_Reg ElementGetters[] = { RMLUI_LUAGETTER(Element,attributes) RMLUI_LUAGETTER(Element,child_nodes) RMLUI_LUAGETTER(Element,class_name) RMLUI_LUAGETTER(Element,client_left) RMLUI_LUAGETTER(Element,client_height) RMLUI_LUAGETTER(Element,client_top) RMLUI_LUAGETTER(Element,client_width) RMLUI_LUAGETTER(Element,first_child) RMLUI_LUAGETTER(Element,id) RMLUI_LUAGETTER(Element,inner_rml) RMLUI_LUAGETTER(Element,last_child) RMLUI_LUAGETTER(Element,next_sibling) RMLUI_LUAGETTER(Element,offset_height) RMLUI_LUAGETTER(Element,offset_left) RMLUI_LUAGETTER(Element,offset_parent) RMLUI_LUAGETTER(Element,offset_top) RMLUI_LUAGETTER(Element,offset_width) RMLUI_LUAGETTER(Element,owner_document) RMLUI_LUAGETTER(Element,parent_node) RMLUI_LUAGETTER(Element,previous_sibling) RMLUI_LUAGETTER(Element,scroll_height) RMLUI_LUAGETTER(Element,scroll_left) RMLUI_LUAGETTER(Element,scroll_top) RMLUI_LUAGETTER(Element,scroll_width) RMLUI_LUAGETTER(Element,style) RMLUI_LUAGETTER(Element,tag_name) { nullptr, nullptr }, }; luaL_Reg ElementSetters[] = { RMLUI_LUASETTER(Element,class_name) RMLUI_LUASETTER(Element,id) RMLUI_LUASETTER(Element,inner_rml) RMLUI_LUASETTER(Element,scroll_left) RMLUI_LUASETTER(Element,scroll_top) { nullptr, nullptr }, }; RMLUI_LUATYPE_DEFINE(Element) template<> void ExtraInit(lua_State* /*L*/, int /*metatable_index*/) { return; } RegType ElementPtrMethods[] = { { nullptr, nullptr }, }; luaL_Reg ElementPtrGetters[] = { { nullptr, nullptr }, }; luaL_Reg ElementPtrSetters[] = { { nullptr, nullptr }, }; RMLUI_LUATYPE_DEFINE(ElementPtr) } // namespace Lua } // namespace Rml