Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,15 @@ if (DEFINED SOL_INCLUDE_DIR)
endif()

# RmlUI
find_package(RmlUi CONFIG REQUIRED)
if(NOT TARGET RmlUi::Core)
find_package(RmlUi CONFIG REQUIRED)
endif()
target_link_libraries(RmlSolLua PUBLIC RmlUi::Core)

# Sol2
find_package(sol2 CONFIG REQUIRED)
if(NOT TARGET sol2)
find_package(sol2 CONFIG REQUIRED)
endif()
target_link_libraries(RmlSolLua PUBLIC sol2)

# Try to find a Lua.
Expand Down
89 changes: 26 additions & 63 deletions src/bind/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <RmlSolLua_private.h>
#include <RmlUi/Core.h>
Expand Down Expand Up @@ -49,44 +48,6 @@ namespace Rml::SolLua

namespace datamodel
{
/// <summary>
/// Bind a sol::table into the data model.
/// </summary>
/// <param name="data">The data model container.</param>
/// <param name="table">The table to bind.</param>
static void bindTable(SolLuaDataModel* data, sol::table& table)
{
for (auto& [key, value] : table)
{
auto skey = key.as<std::string>();
auto it = data->ObjectList.insert_or_assign(skey, value);

if (value.get_type() == sol::type::function)
{
data->Constructor.BindEventCallback(
skey,
[skey, cb = sol::protected_function{value}, state = sol::state_view{table.lua_state()}](Rml::DataModelHandle, Rml::Event& event, const Rml::VariantList& varlist)
{
if (cb.valid())
{
std::vector<sol::object> args;
for (const auto& variant : varlist)
{
args.push_back(makeObjectFromVariant(&variant, state));
}
auto pfr = cb(event, sol::as_args(args));
if (!pfr.valid())
ErrorHandler(cb.lua_state(), std::move(pfr));
}
}
);
}
else
{
data->Constructor.BindCustomDataVariable(skey, Rml::DataVariable(data->ObjectDef.get(), &(it.first->second)));
}
}
}

/// <summary>
/// Opens a Lua data model.
Expand All @@ -96,34 +57,35 @@ namespace Rml::SolLua
/// <param name="model">The table to bind as the data model.</param>
/// <param name="s">Lua state.</param>
/// <returns>A unique pointer to a Sol Lua Data Model.</returns>
static std::unique_ptr<SolLuaDataModel> openDataModel(Rml::Context& self, const Rml::String& name, sol::object model, sol::this_state s)
static sol::object
openDataModel(Rml::Context& self, const Rml::String& name, sol::object model, sol::this_state s)
{
sol::state_view lua{s};
if (model.get_type() != sol::type::table)
{
Rml::Log::Message(Log::LT_ERROR, "Data model must be a table.");
return sol::make_object(s, sol::lua_nil);
}

// Create data model.
auto constructor = self.CreateDataModel(name);
auto data = std::make_unique<SolLuaDataModel>(lua);

// Already created? Get existing.
if (!constructor)
{
constructor = self.GetDataModel(name);
if (!constructor)
return data;
{
return sol::make_object(s, sol::lua_nil);
}
}

data->Constructor = constructor;
data->Handle = constructor.GetModelHandle();
data->ObjectDef = std::make_unique<SolLuaObjectDef>(data.get());

// Only bind table.
if (model.get_type() == sol::type::table)
{
data->Table = model.as<sol::table>();
datamodel::bindTable(data.get(), data->Table);
}
auto dataModel = std::make_shared<SolLuaDataModel>(model.as<sol::table>(), constructor);
auto& proxy = dataModel->topLevelProxy();

return data;
// Alias data model to its top level proxy and push as shared_ptr userdata.
sol::object obj = sol::make_object(s, std::shared_ptr<SolLuaDataModelProxy>(dataModel, &proxy));
proxy.attachUservalueTo(obj);
return obj;
}
} // namespace datamodel

Expand Down Expand Up @@ -179,18 +141,19 @@ namespace Rml::SolLua
"PullDocumentToFront", &Rml::Context::PullDocumentToFront,
"PushDocumentToBack", &Rml::Context::PushDocumentToBack,
"UnfocusDocument", &Rml::Context::UnfocusDocument,
"RemoveDataModel", &Rml::Context::RemoveDataModel,
// RemoveEventListener

// G+S
"dimensions", sol::property(&Rml::Context::GetDimensions, &Rml::Context::SetDimensions),
"dp_ratio", sol::property(&Rml::Context::GetDensityIndependentPixelRatio, &Rml::Context::SetDensityIndependentPixelRatio),
// G+S
"dimensions", sol::property(&Rml::Context::GetDimensions, &Rml::Context::SetDimensions),
"dp_ratio", sol::property(&Rml::Context::GetDensityIndependentPixelRatio, &Rml::Context::SetDensityIndependentPixelRatio),

// G
"documents", sol::readonly_property(&getIndexedTable<SolLuaDocument, Rml::Context, &document::getDocument, &Rml::Context::GetNumDocuments>),
"focus_element", sol::readonly_property(&Rml::Context::GetFocusElement),
"hover_element", sol::readonly_property(&Rml::Context::GetHoverElement),
"name", sol::readonly_property(&Rml::Context::GetName),
"root_element", sol::readonly_property(&Rml::Context::GetRootElement)
// G
"documents", sol::readonly_property(&getIndexedTable<SolLuaDocument, Rml::Context, &document::getDocument, &Rml::Context::GetNumDocuments>),
"focus_element", sol::readonly_property(&Rml::Context::GetFocusElement),
"hover_element", sol::readonly_property(&Rml::Context::GetHoverElement),
"name", sol::readonly_property(&Rml::Context::GetName),
"root_element", sol::readonly_property(&Rml::Context::GetRootElement)
);
// clang-format on
}
Expand Down
38 changes: 27 additions & 11 deletions src/bind/DataModel.cpp
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
#include <string>

#include <RmlSolLua_private.h>
#include SOLHPP

#include "plugin/SolLuaDataModel.h"

namespace Rml::SolLua
{
/// The following functions use uservalue/fenv to query the underlying table,
/// rather than accessing the proxy itself, to reduce indirection level and improve cache locality.
namespace functions
{
static sol::object dataModelGet(SolLuaDataModel& self, const std::string& name, sol::this_state s)
static int dataModelPairs(lua_State* L) // [-0, +3]
{
return self.Table.get<sol::object>(name);
lua_getuservalue(L, 1); // [tbl]
lua_getglobal(L, "next"); // [tbl, next]
lua_pushvalue(L, -2); // [tbl, next, tbl]
lua_pushnil(L); // [tbl, next, tbl, nil]
return 3;
}

static void dataModelSet(SolLuaDataModel& self, const std::string& name, sol::object value, sol::this_state s)
static int dataModelIpairs(lua_State* L) // [-0, +3]
{
self.Handle.DirtyVariable(name);
self.Table.set(name, value);
lua_getuservalue(L, 1); // [tbl]
lua_getglobal(L, "ipairs"); // [tbl, ipairs]
lua_pushvalue(L, -2); // [tbl, ipairs, tbl]
lua_call(L, 1, 3); // [tbl, iter, tbl, 0]
return 3;
}

static int dataModelLen(lua_State* L) // [-0, +1]
{
lua_getuservalue(L, 1); // [tbl]
lua_pushinteger(L, static_cast<lua_Integer>(lua_rawlen(L, -1))); // [tbl, len]
return 1;
}
} // namespace functions

void bind_datamodel(sol::state_view& lua)
{
// clang-format off
lua.new_usertype<SolLuaDataModel>("SolLuaDataModel", sol::no_constructor,
sol::meta_function::index, &functions::dataModelGet,
sol::meta_function::new_index, &functions::dataModelSet
lua.new_usertype<SolLuaDataModelProxy>("SolLuaDataModelProxy", sol::no_constructor,
sol::meta_function::index, &SolLuaDataModelProxy::luaIndex,
sol::meta_function::new_index, &SolLuaDataModelProxy::luaNewIndex,
sol::meta_function::length, &functions::dataModelLen,
sol::meta_function::pairs, &functions::dataModelPairs,
sol::meta_function::ipairs, &functions::dataModelIpairs
);
// clang-format on
}

} // end namespace Rml::SolLua
10 changes: 7 additions & 3 deletions src/bind/bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include SOLHPP

#include "bind.h"
#include "plugin/SolLuaDataModel.h"

namespace Rml::SolLua
{
Expand Down Expand Up @@ -40,9 +41,12 @@ namespace Rml::SolLua
case Rml::Variant::VECTOR2:
return sol::make_object_userdata<Rml::Vector2f>(s, variant->Get<Rml::Vector2f>());
case Rml::Variant::VOIDPTR:
return sol::make_object(s, variant->Get<void*>());
default:
return sol::make_object(s, sol::nil);
{
// The only place where we pass void* to Lua is for the data model proxy.
auto* proxy = static_cast<SolLuaDataModelProxy*>(variant->Get<void*>());
return proxy->luaUserdata();
}
default:;
}

return sol::make_object(s, sol::nil);
Expand Down
Loading