// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "../luaengine.h" #include "inheritance.h" #include #include using namespace Layouting; using namespace Utils; namespace Lua::Internal { template static void processChildren(T *item, const sol::table &children) { for (size_t i = 1; i <= children.size(); ++i) { const auto &child = children[i]; if (child.is()) { if (Layout *layout = child.get()) item->addItem(*layout); else item->addItem("ERROR"); } else if (child.is()) { if (Widget *widget = child.get()) item->addItem(*widget); else item->addItem("ERROR"); } else if (child.is()) { child.get()->addToLayout(*item); } else if (child.is()) { item->addItem(child.get()); } else if (child.is()) { const sol::function f = child.get(); auto res = LuaEngine::void_safe_call(f, item); QTC_ASSERT_EXPECTED(res, continue); } else if (child.is()) { const Span &span = child.get(); item->addItem(span); } else if (child.is()) { const Space &space = child.get(); item->addItem(space); } else if (child.is()) { const Stretch &stretch = child.get(); item->addItem(stretch); } else { qWarning() << "Incompatible object added to layout item: " << (int) child.get_type() << " (expected LayoutItem, Aspect or function returning LayoutItem)"; } } } template static std::unique_ptr construct(const sol::table &children) { std::unique_ptr item(new T({})); processChildren(item.get(), children); return item; } template void constructWidget(std::unique_ptr &widget, const sol::table &children) { widget->setWindowTitle(children.get_or("windowTitle", "")); widget->setToolTip(children.get_or("toolTip", "")); for (size_t i = 1; i <= children.size(); ++i) { const auto &child = children[i]; if (child.is()) widget->setLayout(*child.get()); } } #define HAS_MEM_FUNC(func, name) \ template \ struct name \ { \ typedef char yes[1]; \ typedef char no[2]; \ template \ struct type_check; \ template \ static yes &chk(type_check *); \ template \ static no &chk(...); \ static bool const value = sizeof(chk(0)) == sizeof(yes); \ } HAS_MEM_FUNC(onTextChanged, hasOnTextChanged); HAS_MEM_FUNC(onClicked, hasOnClicked); HAS_MEM_FUNC(setText, hasSetText); HAS_MEM_FUNC(setTitle, hasSetTitle); HAS_MEM_FUNC(setValue, hasSetValue); template void setProperties(std::unique_ptr &item, const sol::table &children, QObject *guard) { if constexpr (hasOnTextChanged::value) { sol::optional onTextChanged = children.get>("onTextChanged"); if (onTextChanged) { item->onTextChanged( [f = *onTextChanged](const QString &text) { auto res = LuaEngine::void_safe_call(f, text); QTC_CHECK_EXPECTED(res); }, guard); } } if constexpr (hasOnClicked &, QObject *guard)>::value) { sol::optional onClicked = children.get>("onClicked"); if (onClicked) { item->onClicked( [f = *onClicked]() { auto res = LuaEngine::void_safe_call(f); QTC_CHECK_EXPECTED(res); }, guard); } } if constexpr (hasSetText::value) { item->setText(children.get_or("text", "")); } if constexpr (hasSetTitle::value) { item->setTitle(children.get_or("title", "")); } if constexpr (hasSetValue::value) { sol::optional value = children.get>("value"); if (value) item->setValue(*value); } } template std::unique_ptr constructWidgetType(const sol::table &children, QObject *guard) { std::unique_ptr item(new T({})); constructWidget(item, children); setProperties(item, children, guard); return item; } std::unique_ptr constructTabFromTable(const sol::table &children) { if (children.size() != 2) throw sol::error("Tab must have exactly two children"); auto tabName = children[1]; if (tabName.get_type() != sol::type::string) throw sol::error("Tab name (first argument) must be a string"); const auto &layout = children[2]; if (!layout.is()) throw sol::error("Tab child (second argument) must be a Layout"); std::unique_ptr item = std::make_unique(tabName, *layout.get()); return item; } std::unique_ptr constructTab(const QString &tabName, const Layout &layout) { std::unique_ptr item = std::make_unique(tabName, layout); return item; } std::unique_ptr constructSpanFromTable(const sol::table &children) { if (children.size() != 2) throw sol::error("Span must have exactly two children"); auto spanSize = children[1]; if (spanSize.get_type() != sol::type::number) throw sol::error("Span size (first argument) must be a number"); const auto &layout = children[2]; if (!layout.is()) throw sol::error("Span child (second argument) must be a Layout"); std::unique_ptr item = std::make_unique(spanSize, *layout.get()); return item; } std::unique_ptr constructSpan(int n, const Layout &layout) { std::unique_ptr item = std::make_unique(n, layout); return item; } std::unique_ptr constructTabWidget(const sol::table &children, QObject *guard) { std::unique_ptr item(new TabWidget({})); setProperties(item, children, guard); for (size_t i = 1; i <= children.size(); ++i) { const auto &child = children[i]; if (child.is()) addToTabWidget(item.get(), *child.get()); } return item; } std::unique_ptr constructSplitter(const sol::table &children) { std::unique_ptr item(new Splitter({})); constructWidget(item, children); for (size_t i = 1; i <= children.size(); ++i) { const auto &child = children[i]; if (child.is()) { addToSplitter(item.get(), *child.get()); } else if (child.is()) { addToSplitter(item.get(), *child.get()); } else { qWarning() << "Incompatible object added to Splitter: " << (int) child.get_type() << " (expected Layout or Widget)"; } } return item; } void addGuiModule() { LuaEngine::registerProvider("Gui", [](sol::state_view l) -> sol::object { const ScriptPluginSpec *pluginSpec = l.get("PluginSpec"); QObject *guard = pluginSpec->connectionGuard.get(); sol::table gui = l.create_table(); gui.new_usertype( "Span", sol::call_constructor, sol::factories(&constructSpan, &constructSpanFromTable)); gui.new_usertype("Space", sol::call_constructor, sol::constructors()); gui.new_usertype( "Stretch", sol::call_constructor, sol::constructors()); // Layouts gui.new_usertype( "Layout", sol::call_constructor, sol::factories(&construct), "show", &Layout::show, sol::base_classes, sol::bases()); gui.new_usertype
( "Form", sol::call_constructor, sol::factories(&construct), sol::base_classes, sol::bases()); gui.new_usertype( "Column", sol::call_constructor, sol::factories(&construct), sol::base_classes, sol::bases()); gui.new_usertype( "Row", sol::call_constructor, sol::factories(&construct), sol::base_classes, sol::bases()); gui.new_usertype( "Flow", sol::call_constructor, sol::factories(&construct), sol::base_classes, sol::bases()); gui.new_usertype( "Grid", sol::call_constructor, sol::factories(&construct), sol::base_classes, sol::bases()); // Widgets gui.new_usertype( "PushButton", sol::call_constructor, sol::factories([guard](const sol::table &children) { return constructWidgetType(children, guard); }), sol::base_classes, sol::bases()); gui.new_usertype