Use LayoutBuilder V2

This puts the implementation introduced in acf1ecb47f into use, after
significant simplifications in the class hierarchy. CRTP is not used
anymore, and the new tag based dispatch is also used for Layout::addItem,
effectively reducing the number of different code paths.

The Lua based settings access is disabled for now.

Change-Id: Idb6d1a25675378757c5267bdb630bcd4c1f52d34
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
hjk
2024-05-14 10:33:01 +02:00
parent e9b002ff7e
commit 971938421c
102 changed files with 1599 additions and 1244 deletions

View File

@@ -72,43 +72,47 @@ SOL_DERIVED_CLASSES(
Utils::AspectList);
namespace Layouting {
class LayoutItem;
class Object;
class Layout;
class Column;
class Row;
class Flow;
class Grid;
class Form;
class Grid;
class Widget;
class Stack;
class Tab;
class Group;
class TextEdit;
class PushButton;
class Row;
class SpinBox;
class Splitter;
class ToolBar;
class Stack;
class Tab;
class TabWidget;
class Group;
class TextEdit;
class ToolBar;
} // namespace Layouting
SOL_BASE_CLASSES(Layouting::Column, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Row, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Flow, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Grid, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Form, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Widget, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Stack, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Tab, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Group, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::TextEdit, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::PushButton, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::SpinBox, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Splitter, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::ToolBar, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::TabWidget, Layouting::LayoutItem);
SOL_BASE_CLASSES(Layouting::Layout, Layouting::Object);
SOL_BASE_CLASSES(Layouting::Column, Layouting::Layout);
SOL_BASE_CLASSES(Layouting::Row, Layouting::Layout);
SOL_BASE_CLASSES(Layouting::Flow, Layouting::Layout);
SOL_BASE_CLASSES(Layouting::Grid, Layouting::Layout);
SOL_BASE_CLASSES(Layouting::Form, Layouting::Layout);
SOL_BASE_CLASSES(Layouting::Widget, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::Stack, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::Tab, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::Group, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::TextEdit, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::PushButton, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::SpinBox, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::Splitter, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::ToolBar, Layouting::Widget);
SOL_BASE_CLASSES(Layouting::TabWidget, Layouting::Widget);
SOL_DERIVED_CLASSES(
Layouting::LayoutItem,
Layouting::Object,
Layouting::Layout,
Layouting::Column,
Layouting::Row,
Layouting::Flow,
@@ -124,3 +128,4 @@ SOL_DERIVED_CLASSES(
Layouting::Splitter,
Layouting::ToolBar,
Layouting::TabWidget);

View File

@@ -13,36 +13,164 @@ using namespace Utils;
namespace Lua::Internal {
static void processChildren(LayoutItem *item, const sol::table &children)
template<class T>
static void processChildren(T *item, const sol::table &children)
{
for (size_t i = 1; i <= children.size(); ++i) {
const sol::object v = children[i];
if (v.is<LayoutItem *>()) {
item->addItem(*v.as<LayoutItem *>());
} else if (v.is<BaseAspect>()) {
v.as<BaseAspect *>()->addToLayout(*item);
} else if (v.is<QString>()) {
item->addItem(v.as<QString>());
} else if (v.is<sol::function>()) {
const sol::function f = v.as<sol::function>();
auto res = LuaEngine::safe_call<LayoutItem *>(f);
const auto &child = children[i];
if (child.is<Layout *>()) {
item->addItem(*child.get<Layout *>());
} else if (child.is<Widget *>()) {
item->addItem(*child.get<Widget *>());
} else if (child.is<BaseAspect>()) {
child.get<BaseAspect *>()->addToLayout(*item);
} else if (child.is<QString>()) {
item->addItem(child.get<QString>());
} else if (child.is<sol::function>()) {
const sol::function f = child.get<sol::function>();
auto res = LuaEngine::void_safe_call(f, item);
QTC_ASSERT_EXPECTED(res, continue);
item->addItem(**res);
} else if (child.is<Span>()) {
const Span &span = child.get<Span>();
item->addItem(span);
} else if (child.is<Space>()) {
const Space &space = child.get<Space>();
item->addItem(space);
} else if (child.is<Stretch>()) {
const Stretch &stretch = child.get<Stretch>();
item->addItem(stretch);
} else {
qWarning() << "Incompatible object added to layout item: " << (int) v.get_type()
qWarning() << "Incompatible object added to layout item: " << (int) child.get_type()
<< " (expected LayoutItem, Aspect or function returning LayoutItem)";
}
}
}
template<class T, typename... Args>
static std::unique_ptr<T> construct(Args &&...args, const sol::table &children)
template<class T>
static std::unique_ptr<T> construct(const sol::table &children)
{
std::unique_ptr<T> item(new T(std::forward<Args>(args)..., {}));
std::unique_ptr<T> item(new T({}));
processChildren(item.get(), children);
return item;
}
template<class T>
void constructWidget(std::unique_ptr<T> &widget, const sol::table &children)
{
widget->setWindowTitle(children.get_or<QString>("windowTitle", ""));
widget->setToolTip(children.get_or<QString>("toolTip", ""));
for (size_t i = 1; i < children.size(); ++i) {
const auto &child = children[i];
if (child.is<Layout>())
widget->setLayout(*child.get<Layout *>());
}
}
#define HAS_MEM_FUNC(func, name) \
template<typename T, typename Sign> \
struct name \
{ \
typedef char yes[1]; \
typedef char no[2]; \
template<typename U, U> \
struct type_check; \
template<typename _1> \
static yes &chk(type_check<Sign, &_1::func> *); \
template<typename> \
static no &chk(...); \
static bool const value = sizeof(chk<T>(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<class T>
void setProperties(std::unique_ptr<T> &item, const sol::table &children)
{
if constexpr (hasOnTextChanged<T, void (T::*)(const QString &)>::value) {
sol::optional<sol::protected_function> onTextChanged
= children.get<sol::optional<sol::protected_function>>("onTextChanged");
if (onTextChanged) {
item->onTextChanged(
[f = *onTextChanged](const QString &text) {
auto res = LuaEngine::void_safe_call(f, text);
QTC_CHECK_EXPECTED(res);
},
&LuaEngine::instance());
}
}
if constexpr (hasOnClicked<T, void (T::*)(const std::function<void()> &, QObject *guard)>::value) {
sol::optional<sol::protected_function> onClicked
= children.get<sol::optional<sol::protected_function>>("onClicked");
if (onClicked) {
item->onClicked(
[f = *onClicked]() {
auto res = LuaEngine::void_safe_call(f);
QTC_CHECK_EXPECTED(res);
},
&LuaEngine::instance());
}
}
if constexpr (hasSetText<T, void (T::*)(const QString &)>::value) {
item->setText(children.get_or<QString>("text", ""));
}
if constexpr (hasSetTitle<T, void (T::*)(const QString &)>::value) {
item->setTitle(children.get_or<QString>("title", ""));
}
if constexpr (hasSetValue<T, void (T::*)(int)>::value) {
sol::optional<int> value = children.get<sol::optional<int>>("value");
if (value)
item->setValue(*value);
}
}
template<class T>
std::unique_ptr<T> constructWidgetType(const sol::table &children)
{
std::unique_ptr<T> item(new T({}));
constructWidget(item, children);
setProperties(item, children);
return item;
}
std::unique_ptr<Tab> constructTab(const QString &tabName, const Layout &layout)
{
std::unique_ptr<Tab> item = std::make_unique<Tab>(tabName, layout);
return item;
}
std::unique_ptr<TabWidget> constructTabWidget(const sol::table &children)
{
std::unique_ptr<TabWidget> item(new TabWidget({}));
setProperties(item, children);
for (size_t i = 1; i < children.size(); ++i) {
const auto &child = children[i];
if (child.is<Tab *>())
addToTabWidget(item.get(), *child.get<Tab *>());
}
return item;
}
std::unique_ptr<Splitter> constructSplitter(const sol::table &children)
{
std::unique_ptr<Splitter> item(new Splitter({}));
constructWidget(item, children);
for (size_t i = 1; i < children.size(); ++i) {
const auto &child = children[i];
if (child.is<Layout *>()) {
addToSplitter(item.get(), *child.get<Layout *>());
} else if (child.is<Widget *>()) {
addToSplitter(item.get(), *child.get<Widget *>());
} else {
qWarning() << "Incompatible object added to Splitter: " << (int) child.get_type()
<< " (expected Layout or Widget)";
}
}
return item;
}
@@ -51,91 +179,119 @@ void addLayoutModule()
LuaEngine::registerProvider("Layout", [](sol::state_view l) -> sol::object {
sol::table layout = l.create_table();
layout.new_usertype<LayoutItem>("LayoutItem", "attachTo", &LayoutItem::attachTo);
layout.new_usertype<Span>(
"Span", sol::call_constructor, sol::constructors<Span(int n, const Layout::I &item)>());
layout["Span"] = [](int span, LayoutItem *item) {
return createItem(item, Span(span, *item));
};
layout["Space"] = [](int space) { return createItem(nullptr, Space(space)); };
layout["Stretch"] = [](int stretch) { return createItem(nullptr, Stretch(stretch)); };
layout.new_usertype<Space>("Space", sol::call_constructor, sol::constructors<Space(int)>());
layout.new_usertype<Stretch>(
"Stretch", sol::call_constructor, sol::constructors<Stretch(int)>());
// Layouts
layout.new_usertype<Form>(
"Form",
sol::call_constructor,
sol::factories(&construct<Form>),
sol::base_classes,
sol::bases<Layout, Object, Thing>());
layout.new_usertype<Column>(
"Column",
sol::call_constructor,
sol::factories(&construct<Column>),
sol::base_classes,
sol::bases<Layout, Object, Thing>());
layout.new_usertype<Row>(
"Row",
sol::call_constructor,
sol::factories(&construct<Row>),
sol::base_classes,
sol::bases<Layout, Object, Thing>());
layout.new_usertype<Flow>(
"Flow",
sol::call_constructor,
sol::factories(&construct<Flow>),
sol::base_classes,
sol::bases<Layout, Object, Thing>());
layout.new_usertype<Grid>(
"Grid",
sol::call_constructor,
sol::factories(&construct<Grid>),
sol::base_classes,
sol::bases<Layout, Object, Thing>());
// Widgets
layout.new_usertype<PushButton>(
"PushButton",
sol::call_constructor,
sol::factories(&constructWidgetType<PushButton>),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<Widget>(
"Widget",
sol::call_constructor,
sol::factories(&constructWidgetType<Widget>),
"show",
&Widget::show,
"resize",
&Widget::resize,
sol::base_classes,
sol::bases<Object, Thing>());
layout.new_usertype<Stack>(
"Stack",
sol::call_constructor,
sol::factories(&constructWidgetType<Stack>),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<Column>("Column",
sol::call_constructor,
sol::factories(&construct<Column>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Row>("Row",
sol::call_constructor,
sol::factories(&construct<Row>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Flow>("Flow",
sol::call_constructor,
sol::factories(&construct<Flow>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Grid>("Grid",
sol::call_constructor,
sol::factories(&construct<Grid>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Form>("Form",
sol::call_constructor,
sol::factories(&construct<Form>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Widget>("Widget",
sol::call_constructor,
sol::factories(&construct<Widget>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Stack>("Stack",
sol::call_constructor,
sol::factories(&construct<Stack>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Tab>(
"Tab",
sol::call_constructor,
sol::factories(&construct<Tab, QString>),
sol::factories(&constructTab),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<TextEdit>("TextEdit",
sol::call_constructor,
sol::factories(&construct<TextEdit>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<PushButton>("PushButton",
sol::call_constructor,
sol::factories(&construct<PushButton>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<SpinBox>("SpinBox",
sol::call_constructor,
sol::factories(&construct<SpinBox>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<Splitter>("Splitter",
sol::call_constructor,
sol::factories(&construct<Splitter>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<ToolBar>("ToolBar",
sol::call_constructor,
sol::factories(&construct<ToolBar>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<TabWidget>("TabWidget",
sol::call_constructor,
sol::factories(&construct<TabWidget>),
sol::base_classes,
sol::bases<LayoutItem>());
sol::bases<Widget, Object, Thing>());
layout.new_usertype<Group>("Group",
sol::call_constructor,
sol::factories(&construct<Group>),
sol::base_classes,
sol::bases<LayoutItem>());
layout.new_usertype<TextEdit>(
"TextEdit",
sol::call_constructor,
sol::factories(&constructWidgetType<TextEdit>),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<SpinBox>(
"SpinBox",
sol::call_constructor,
sol::factories(&constructWidgetType<SpinBox>),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<Splitter>(
"Splitter",
sol::call_constructor,
sol::factories(&constructSplitter),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<ToolBar>(
"ToolBar",
sol::call_constructor,
sol::factories(&constructWidgetType<ToolBar>),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<TabWidget>(
"TabWidget",
sol::call_constructor,
sol::factories(&constructTabWidget),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout.new_usertype<Group>(
"Group",
sol::call_constructor,
sol::factories(&constructWidgetType<Group>),
sol::base_classes,
sol::bases<Widget, Object, Thing>());
layout["br"] = &br;
layout["st"] = &st;
@@ -143,26 +299,16 @@ void addLayoutModule()
layout["hr"] = &hr;
layout["noMargin"] = &noMargin;
layout["normalMargin"] = &normalMargin;
layout["customMargin"] = [](int left, int top, int right, int bottom) {
return customMargin(QMargins(left, top, right, bottom));
};
layout["withFormAlignment"] = &withFormAlignment;
layout["title"] = &title;
layout["text"] = &text;
layout["tooltip"] = &tooltip;
layout["resize"] = &resize;
layout["columnStretch"] = &columnStretch;
layout["spacing"] = &spacing;
layout["windowTitle"] = &windowTitle;
layout["fieldGrowthPolicy"] = &fieldGrowthPolicy;
layout["id"] = &id;
layout["setText"] = &setText;
layout["onClicked"] = [](const sol::function &f) {
return onClicked([f]() {
auto res = LuaEngine::void_safe_call(f);
QTC_CHECK_EXPECTED(res);
});
};
//layout["customMargin"] = [](int left, int top, int right, int bottom) {
// return customMargin(QMargins(left, top, right, bottom));
//};
// layout["withFormAlignment"] = &withFormAlignment;
// layout["columnStretch"] = &columnStretch;
// layout["spacing"] = &spacing;
// layout["windowTitle"] = &windowTitle;
// layout["fieldGrowthPolicy"] = &fieldGrowthPolicy;
// layout["id"] = &id;
layout["onTextChanged"] = [](const sol::function &f) {
return onTextChanged([f](const QString &text) {
auto res = LuaEngine::void_safe_call(f, text);

View File

@@ -62,8 +62,8 @@ std::unique_ptr<LuaAspectContainer> aspectContainerCreate(const sol::table &opti
} else if (key == "layouter") {
if (v.is<sol::function>())
container->setLayouter(
[func = v.as<sol::function>()]() -> Layouting::LayoutItem {
auto res = Lua::LuaEngine::safe_call<Layouting::LayoutItem>(func);
[func = v.as<sol::function>()]() -> Layouting::Layout {
auto res = Lua::LuaEngine::safe_call<Layouting::Layout>(func);
QTC_ASSERT_EXPECTED(res, return {});
return *res;
});