GMLCompiler+LibGUI: Add support for object properties and undo hack

Previously the GML compiler did not support object properties such as
`content_widget: @GUI::Widget{}` for GUI::ScrollableContainerWidget;
this commit adds support for such properties by simply calling
`set_<key>(<TProperty>&)` on the object.
This commit also removes the previous hack where
ScrollableContainerWidget was special-cased to have its singular child
used as the content widget; the only GML file using this behaviour was
also changed to be in line with 'proper' GML as handled by the GML
Playground.
This commit is contained in:
Ali Mohammad Pur 2024-04-23 12:02:13 +02:00 committed by Andrew Kaster
parent 5c17b61378
commit f3a4118aee
4 changed files with 55 additions and 5 deletions

View file

@ -313,6 +313,29 @@ static ErrorOr<void> generate_loader_for_object(GUI::GML::Object const& gml_obje
}));
generator.appendln("");
// Object properties
size_t current_object_property_index = 0;
auto next_object_property_name = [&]() {
return String::formatted("{}_property_{}", object_name, current_object_property_index++);
};
TRY(gml_object.try_for_each_object_property([&](StringView key, NonnullRefPtr<GUI::GML::Object> value) -> ErrorOr<void> {
if (key == "layout"sv)
return {}; // Layout is handled separately.
auto property_generator = generator.fork();
auto property_variable_name = TRY(next_object_property_name());
property_generator.set("property_variable_name", property_variable_name.bytes_as_string_view());
property_generator.set("property_class_name", value->name());
property_generator.set("key", key);
TRY(append(property_generator, "RefPtr<::@property_class_name@> @property_variable_name@;"));
TRY(generate_loader_for_object(*value, property_generator.fork(), property_variable_name, indentation + 1, UseObjectConstructor::Yes));
// Set the property on the object.
TRY(append(property_generator, "@object_name@->set_@key@(*@property_variable_name@);"));
property_generator.appendln("");
return {};
}));
// Layout
if (gml_object.layout_object() != nullptr) {
TRY(append(generator, "RefPtr<GUI::Layout> layout;"));
@ -340,10 +363,9 @@ static ErrorOr<void> generate_loader_for_object(GUI::GML::Object const& gml_obje
TRY(append(child_generator, "RefPtr<::@child_class_name@> @child_variable_name@;"));
TRY(generate_loader_for_object(child, child_generator.fork(), child_variable_name, indentation + 1, UseObjectConstructor::Yes));
// Handle the current two special cases of child adding.
if (gml_object.name() == "GUI::ScrollableContainerWidget"sv)
TRY(append(child_generator, "static_ptr_cast<GUI::ScrollableContainerWidget>(@object_name@)->set_widget(@child_variable_name@);"));
else if (gml_object.name() == "GUI::TabWidget"sv)
// Handle the current special case of child adding.
// FIXME: This should be using the proper API for handling object properties.
if (gml_object.name() == "GUI::TabWidget"sv)
TRY(append(child_generator, "static_ptr_cast<GUI::TabWidget>(@object_name@)->add_widget(*@child_variable_name@);"));
else
TRY(append(child_generator, "TRY(@object_name@->try_add_child(*@child_variable_name@));"));

View file

@ -24,7 +24,7 @@
@GUI::ScrollableContainerWidget {
name: "scrollable_container"
@GUI::Widget {
content_widget: @GUI::Widget {
name: "emojis"
layout: @GUI::VerticalBoxLayout {}
}

View file

@ -188,6 +188,18 @@ public:
}
}
template<typename Callback>
void for_each_object_property(Callback callback) const
{
for (auto const& child : m_properties) {
if (is<KeyValuePair>(child)) {
auto const& property = static_cast<KeyValuePair const&>(*child);
if (is<Object>(property.value().ptr()))
callback(property.key(), static_ptr_cast<Object>(property.value()));
}
}
}
template<FallibleFunction<StringView, NonnullRefPtr<JsonValueNode>> Callback>
ErrorOr<void> try_for_each_property(Callback callback) const
{
@ -201,6 +213,19 @@ public:
return {};
}
template<FallibleFunction<StringView, NonnullRefPtr<Object>> Callback>
ErrorOr<void> try_for_each_object_property(Callback callback) const
{
for (auto const& child : m_properties) {
if (is<KeyValuePair>(child)) {
auto const& property = static_cast<KeyValuePair const&>(*child);
if (is<Object>(property.value().ptr()))
TRY(callback(property.key(), static_ptr_cast<Object>(property.value())));
}
}
return {};
}
template<typename Callback>
void for_each_child_object(Callback callback) const
{

View file

@ -21,6 +21,9 @@ public:
GUI::Widget* widget() { return m_widget; }
GUI::Widget const* widget() const { return m_widget; }
// GMLCompiler support for the `content_widget` object property.
void set_content_widget(GUI::Widget& widget) { set_widget(&widget); }
protected:
virtual void did_scroll() override;
virtual void resize_event(GUI::ResizeEvent&) override;