mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 09:51:57 -05:00
LibGUI: Move widget registration to LibCore
This also moves Widget::load_from_json into Core::Object as a virtual function in order to allow loading non-widget objects in GML (e.g. BoxLayout). Co-authored-by: Gunnar Beutner <gbeutner@serenityos.org>
This commit is contained in:
parent
6e101adc28
commit
3aaffa2c47
13 changed files with 134 additions and 85 deletions
|
@ -11,10 +11,10 @@
|
|||
#include <LibGfx/Font.h>
|
||||
#include <WindowServer/WindowManager.h>
|
||||
|
||||
namespace SpaceAnalyzer {
|
||||
|
||||
REGISTER_WIDGET(SpaceAnalyzer, TreeMapWidget)
|
||||
|
||||
namespace SpaceAnalyzer {
|
||||
|
||||
TreeMapWidget::TreeMapWidget()
|
||||
: m_viewpoint(0)
|
||||
{
|
||||
|
|
|
@ -799,7 +799,11 @@ void HackStudioWidget::create_form_editor(GUI::Widget& parent)
|
|||
|
||||
form_widgets_toolbar.add_action(cursor_tool_action);
|
||||
|
||||
GUI::WidgetClassRegistration::for_each([&, this](const GUI::WidgetClassRegistration& reg) {
|
||||
auto& widget_class = *Core::ObjectClassRegistration::find("GUI::Widget");
|
||||
|
||||
Core::ObjectClassRegistration::for_each([&, this](const Core::ObjectClassRegistration& reg) {
|
||||
if (!reg.is_derived_from(widget_class))
|
||||
return;
|
||||
constexpr size_t gui_namespace_prefix_length = sizeof("GUI::") - 1;
|
||||
auto icon_path = String::formatted("/res/icons/hackstudio/G{}.png",
|
||||
reg.class_name().substring(gui_namespace_prefix_length, reg.class_name().length() - gui_namespace_prefix_length));
|
||||
|
@ -808,7 +812,7 @@ void HackStudioWidget::create_form_editor(GUI::Widget& parent)
|
|||
|
||||
auto action = GUI::Action::create_checkable(reg.class_name(), Gfx::Bitmap::load_from_file(icon_path), [®, this](auto&) {
|
||||
m_form_editor_widget->set_tool(make<WidgetTool>(*m_form_editor_widget, reg));
|
||||
auto widget = reg.construct();
|
||||
auto widget = static_ptr_cast<Widget>(reg.construct());
|
||||
m_form_editor_widget->form_widget().add_child(widget);
|
||||
widget->set_relative_rect(30, 30, 30, 30);
|
||||
m_form_editor_widget->model().update();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibGUI/Forward.h>
|
||||
|
||||
namespace HackStudio {
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace HackStudio {
|
|||
|
||||
class WidgetTool final : public Tool {
|
||||
public:
|
||||
explicit WidgetTool(FormEditorWidget& editor, const GUI::WidgetClassRegistration& meta_class)
|
||||
explicit WidgetTool(FormEditorWidget& editor, const Core::ObjectClassRegistration& meta_class)
|
||||
: Tool(editor)
|
||||
, m_meta_class(meta_class)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ private:
|
|||
virtual void on_mousemove(GUI::MouseEvent&) override;
|
||||
virtual void on_keydown(GUI::KeyEvent&) override;
|
||||
|
||||
const GUI::WidgetClassRegistration& m_meta_class;
|
||||
const Core::ObjectClassRegistration& m_meta_class;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -102,6 +102,8 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
state = previous_states.take_last();
|
||||
}
|
||||
|
||||
auto& widget_class = *Core::ObjectClassRegistration::find("GUI::Widget");
|
||||
|
||||
Vector<GUI::AutocompleteProvider::Entry> class_entries, identifier_entries;
|
||||
switch (state) {
|
||||
case Free:
|
||||
|
@ -110,7 +112,9 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
// Nothing to put here.
|
||||
break;
|
||||
}
|
||||
GUI::WidgetClassRegistration::for_each([&](const GUI::WidgetClassRegistration& registration) {
|
||||
Core::ObjectClassRegistration::for_each([&](const Core::ObjectClassRegistration& registration) {
|
||||
if (!registration.is_derived_from(widget_class))
|
||||
return;
|
||||
class_entries.empend(String::formatted("@{}", registration.class_name()), 0u);
|
||||
});
|
||||
break;
|
||||
|
@ -122,12 +126,14 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
// TODO: Suggest braces?
|
||||
break;
|
||||
}
|
||||
GUI::WidgetClassRegistration::for_each([&](const GUI::WidgetClassRegistration& registration) {
|
||||
Core::ObjectClassRegistration::for_each([&](const Core::ObjectClassRegistration& registration) {
|
||||
if (!registration.is_derived_from(widget_class))
|
||||
return;
|
||||
if (registration.class_name().starts_with(class_names.last()))
|
||||
identifier_entries.empend(registration.class_name(), class_names.last().length());
|
||||
});
|
||||
break;
|
||||
case InIdentifier:
|
||||
case InIdentifier: {
|
||||
if (class_names.is_empty())
|
||||
break;
|
||||
if (last_seen_token && last_seen_token->m_end.column + 1 != cursor.column() && last_seen_token->m_end.line == cursor.line()) {
|
||||
|
@ -135,7 +141,8 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
// TODO: Maybe suggest a colon?
|
||||
break;
|
||||
}
|
||||
if (auto registration = GUI::WidgetClassRegistration::find(class_names.last())) {
|
||||
auto registration = Core::ObjectClassRegistration::find(class_names.last());
|
||||
if (registration && registration->is_derived_from(widget_class)) {
|
||||
auto instance = registration->construct();
|
||||
for (auto& it : instance->properties()) {
|
||||
if (it.key.starts_with(identifier_string))
|
||||
|
@ -148,7 +155,8 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
if (identifier_entries.size() == 1 && identifier_entries.first().completion == identifier_string)
|
||||
identifier_entries.clear();
|
||||
break;
|
||||
case AfterClassName:
|
||||
}
|
||||
case AfterClassName: {
|
||||
if (last_seen_token && last_seen_token->m_end.line == cursor.line()) {
|
||||
if (last_seen_token->m_type != GUI::GMLToken::Type::Identifier || last_seen_token->m_end.column + 1 != cursor.column()) {
|
||||
// Inside braces, but on the same line as some other stuff (and not the continuation of one!)
|
||||
|
@ -157,7 +165,8 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
}
|
||||
}
|
||||
if (!class_names.is_empty()) {
|
||||
if (auto registration = GUI::WidgetClassRegistration::find(class_names.last())) {
|
||||
auto registration = Core::ObjectClassRegistration::find(class_names.last());
|
||||
if (registration && registration->is_derived_from(widget_class)) {
|
||||
auto instance = registration->construct();
|
||||
for (auto& it : instance->properties()) {
|
||||
if (!it.value->is_readonly())
|
||||
|
@ -165,16 +174,21 @@ void GMLAutocompleteProvider::provide_completions(Function<void(Vector<Entry>)>
|
|||
}
|
||||
}
|
||||
}
|
||||
GUI::WidgetClassRegistration::for_each([&](const GUI::WidgetClassRegistration& registration) {
|
||||
Core::ObjectClassRegistration::for_each([&](const Core::ObjectClassRegistration& registration) {
|
||||
if (!registration.is_derived_from(widget_class))
|
||||
return;
|
||||
class_entries.empend(String::formatted("@{}", registration.class_name()), 0u);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case AfterIdentifier:
|
||||
if (last_seen_token && last_seen_token->m_end.line != cursor.line()) {
|
||||
break;
|
||||
}
|
||||
if (identifier_string == "layout") {
|
||||
GUI::WidgetClassRegistration::for_each([&](const GUI::WidgetClassRegistration& registration) {
|
||||
Core::ObjectClassRegistration::for_each([&](const Core::ObjectClassRegistration& registration) {
|
||||
if (!registration.is_derived_from(widget_class))
|
||||
return;
|
||||
if (registration.class_name().contains("Layout"))
|
||||
class_entries.empend(String::formatted("@{}", registration.class_name()), 0u);
|
||||
});
|
||||
|
|
|
@ -128,7 +128,7 @@ int main(int argc, char** argv)
|
|||
|
||||
editor.on_change = [&] {
|
||||
preview.remove_all_children();
|
||||
preview.load_from_gml(editor.text(), [](const String& class_name) -> RefPtr<GUI::Widget> {
|
||||
preview.load_from_gml(editor.text(), [](const String& class_name) -> RefPtr<Core::Object> {
|
||||
return UnregisteredWidget::construct(class_name);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@ class NetworkJob;
|
|||
class NetworkResponse;
|
||||
class Notifier;
|
||||
class Object;
|
||||
class ObjectClassRegistration;
|
||||
class ProcessStatisticsReader;
|
||||
class Socket;
|
||||
class SocketAddress;
|
||||
|
|
|
@ -250,4 +250,44 @@ void Object::set_event_filter(Function<bool(Core::Event&)> filter)
|
|||
m_event_filter = move(filter);
|
||||
}
|
||||
|
||||
static HashMap<String, ObjectClassRegistration*>& object_classes()
|
||||
{
|
||||
static HashMap<String, ObjectClassRegistration*>* map;
|
||||
if (!map)
|
||||
map = new HashMap<String, ObjectClassRegistration*>;
|
||||
return *map;
|
||||
}
|
||||
|
||||
ObjectClassRegistration::ObjectClassRegistration(const String& class_name, Function<NonnullRefPtr<Object>()> factory, ObjectClassRegistration* parent_class)
|
||||
: m_class_name(class_name)
|
||||
, m_factory(move(factory))
|
||||
, m_parent_class(parent_class)
|
||||
{
|
||||
object_classes().set(class_name, this);
|
||||
}
|
||||
|
||||
ObjectClassRegistration::~ObjectClassRegistration()
|
||||
{
|
||||
}
|
||||
|
||||
bool ObjectClassRegistration::is_derived_from(const ObjectClassRegistration& base_class) const
|
||||
{
|
||||
if (&base_class == this)
|
||||
return true;
|
||||
if (!m_parent_class)
|
||||
return false;
|
||||
return m_parent_class->is_derived_from(base_class);
|
||||
}
|
||||
|
||||
void ObjectClassRegistration::for_each(Function<void(const ObjectClassRegistration&)> callback)
|
||||
{
|
||||
for (auto& it : object_classes()) {
|
||||
callback(*it.value);
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectClassRegistration* ObjectClassRegistration::find(const String& class_name)
|
||||
{
|
||||
return object_classes().get(class_name).value_or(nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,35 @@
|
|||
|
||||
namespace Core {
|
||||
|
||||
#define REGISTER_CORE_OBJECT(namespace_, class_name) \
|
||||
namespace Core { \
|
||||
namespace Registration { \
|
||||
Core::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name, []() { return namespace_::class_name::construct(); }); \
|
||||
} \
|
||||
}
|
||||
|
||||
class ObjectClassRegistration {
|
||||
AK_MAKE_NONCOPYABLE(ObjectClassRegistration);
|
||||
AK_MAKE_NONMOVABLE(ObjectClassRegistration);
|
||||
|
||||
public:
|
||||
ObjectClassRegistration(const String& class_name, Function<NonnullRefPtr<Object>()> factory, ObjectClassRegistration* parent_class = nullptr);
|
||||
~ObjectClassRegistration();
|
||||
|
||||
String class_name() const { return m_class_name; }
|
||||
const ObjectClassRegistration* parent_class() const { return m_parent_class; }
|
||||
NonnullRefPtr<Object> construct() const { return m_factory(); }
|
||||
bool is_derived_from(const ObjectClassRegistration& base_class) const;
|
||||
|
||||
static void for_each(Function<void(const ObjectClassRegistration&)>);
|
||||
static const ObjectClassRegistration* find(const String& class_name);
|
||||
|
||||
private:
|
||||
String m_class_name;
|
||||
Function<NonnullRefPtr<Object>()> m_factory;
|
||||
ObjectClassRegistration* m_parent_class { nullptr };
|
||||
};
|
||||
|
||||
class RPCClient;
|
||||
|
||||
enum class TimerShouldFireWhenNotVisible {
|
||||
|
@ -129,6 +158,8 @@ public:
|
|||
void increment_inspector_count(Badge<RPCClient>);
|
||||
void decrement_inspector_count(Badge<RPCClient>);
|
||||
|
||||
virtual bool load_from_json(const JsonObject&, RefPtr<Core::Object> (*)(const String&)) { return false; }
|
||||
|
||||
protected:
|
||||
explicit Object(Object* parent = nullptr);
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include <LibGfx/Orientation.h>
|
||||
#include <stdio.h>
|
||||
|
||||
REGISTER_WIDGET(GUI, HorizontalBoxLayout)
|
||||
REGISTER_WIDGET(GUI, VerticalBoxLayout)
|
||||
REGISTER_CORE_OBJECT(GUI, HorizontalBoxLayout)
|
||||
REGISTER_CORE_OBJECT(GUI, VerticalBoxLayout)
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ class VerticalBoxLayout;
|
|||
class VerticalSlider;
|
||||
class WMEvent;
|
||||
class Widget;
|
||||
class WidgetClassRegistration;
|
||||
class Window;
|
||||
class WindowServerConnection;
|
||||
|
||||
|
|
|
@ -23,41 +23,10 @@
|
|||
#include <LibGfx/Palette.h>
|
||||
#include <unistd.h>
|
||||
|
||||
REGISTER_WIDGET(GUI, Widget)
|
||||
REGISTER_CORE_OBJECT(GUI, Widget)
|
||||
|
||||
namespace GUI {
|
||||
|
||||
static HashMap<String, WidgetClassRegistration*>& widget_classes()
|
||||
{
|
||||
static HashMap<String, WidgetClassRegistration*>* map;
|
||||
if (!map)
|
||||
map = new HashMap<String, WidgetClassRegistration*>;
|
||||
return *map;
|
||||
}
|
||||
|
||||
WidgetClassRegistration::WidgetClassRegistration(const String& class_name, Function<NonnullRefPtr<Widget>()> factory)
|
||||
: m_class_name(class_name)
|
||||
, m_factory(move(factory))
|
||||
{
|
||||
widget_classes().set(class_name, this);
|
||||
}
|
||||
|
||||
WidgetClassRegistration::~WidgetClassRegistration()
|
||||
{
|
||||
}
|
||||
|
||||
void WidgetClassRegistration::for_each(Function<void(const WidgetClassRegistration&)> callback)
|
||||
{
|
||||
for (auto& it : widget_classes()) {
|
||||
callback(*it.value);
|
||||
}
|
||||
}
|
||||
|
||||
const WidgetClassRegistration* WidgetClassRegistration::find(const String& class_name)
|
||||
{
|
||||
return widget_classes().get(class_name).value_or(nullptr);
|
||||
}
|
||||
|
||||
Widget::Widget()
|
||||
: Core::Object(nullptr)
|
||||
, m_background_role(Gfx::ColorRole::Window)
|
||||
|
@ -996,13 +965,13 @@ void Widget::set_override_cursor(Gfx::StandardCursor cursor)
|
|||
|
||||
bool Widget::load_from_gml(const StringView& gml_string)
|
||||
{
|
||||
return load_from_gml(gml_string, [](const String& class_name) -> RefPtr<Widget> {
|
||||
return load_from_gml(gml_string, [](const String& class_name) -> RefPtr<Core::Object> {
|
||||
dbgln("Class '{}' not registered", class_name);
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
bool Widget::load_from_gml(const StringView& gml_string, RefPtr<Widget> (*unregistered_child_handler)(const String&))
|
||||
bool Widget::load_from_gml(const StringView& gml_string, RefPtr<Core::Object> (*unregistered_child_handler)(const String&))
|
||||
{
|
||||
auto value = parse_gml(gml_string);
|
||||
if (!value.is_object())
|
||||
|
@ -1010,7 +979,7 @@ bool Widget::load_from_gml(const StringView& gml_string, RefPtr<Widget> (*unregi
|
|||
return load_from_json(value.as_object(), unregistered_child_handler);
|
||||
}
|
||||
|
||||
bool Widget::load_from_json(const JsonObject& json, RefPtr<Widget> (*unregistered_child_handler)(const String&))
|
||||
bool Widget::load_from_json(const JsonObject& json, RefPtr<Core::Object> (*unregistered_child_handler)(const String&))
|
||||
{
|
||||
json.for_each_member([&](auto& key, auto& value) {
|
||||
set_property(key, value);
|
||||
|
@ -1055,16 +1024,16 @@ bool Widget::load_from_json(const JsonObject& json, RefPtr<Widget> (*unregistere
|
|||
return false;
|
||||
}
|
||||
|
||||
RefPtr<Widget> child_widget;
|
||||
if (auto* registration = WidgetClassRegistration::find(class_name.as_string())) {
|
||||
child_widget = registration->construct();
|
||||
RefPtr<Core::Object> child;
|
||||
if (auto* registration = Core::ObjectClassRegistration::find(class_name.as_string())) {
|
||||
child = registration->construct();
|
||||
} else {
|
||||
child_widget = unregistered_child_handler(class_name.as_string());
|
||||
if (!child_widget)
|
||||
return false;
|
||||
child = unregistered_child_handler(class_name.as_string());
|
||||
}
|
||||
add_child(*child_widget);
|
||||
child_widget->load_from_json(child_json, unregistered_child_handler);
|
||||
if (!child)
|
||||
return false;
|
||||
add_child(*child);
|
||||
child->load_from_json(child_json, unregistered_child_handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,18 @@
|
|||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/StandardCursor.h>
|
||||
|
||||
namespace Core {
|
||||
namespace Registration {
|
||||
extern Core::ObjectClassRegistration registration_Widget;
|
||||
}
|
||||
}
|
||||
|
||||
#define REGISTER_WIDGET(namespace_, class_name) \
|
||||
namespace { \
|
||||
GUI::WidgetClassRegistration registration_##class_name(#namespace_ "::" #class_name, []() { return namespace_::class_name::construct(); }); \
|
||||
namespace Core { \
|
||||
namespace Registration { \
|
||||
Core::ObjectClassRegistration registration_##class_name( \
|
||||
#namespace_ "::" #class_name, []() { return static_ptr_cast<Core::Object>(namespace_::class_name::construct()); }, ®istration_Widget); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace GUI {
|
||||
|
@ -35,25 +44,6 @@ enum class VerticalDirection {
|
|||
Down
|
||||
};
|
||||
|
||||
class WidgetClassRegistration {
|
||||
AK_MAKE_NONCOPYABLE(WidgetClassRegistration);
|
||||
AK_MAKE_NONMOVABLE(WidgetClassRegistration);
|
||||
|
||||
public:
|
||||
WidgetClassRegistration(const String& class_name, Function<NonnullRefPtr<Widget>()> factory);
|
||||
~WidgetClassRegistration();
|
||||
|
||||
String class_name() const { return m_class_name; }
|
||||
NonnullRefPtr<Widget> construct() const { return m_factory(); }
|
||||
|
||||
static void for_each(Function<void(const WidgetClassRegistration&)>);
|
||||
static const WidgetClassRegistration* find(const String& class_name);
|
||||
|
||||
private:
|
||||
String m_class_name;
|
||||
Function<NonnullRefPtr<Widget>()> m_factory;
|
||||
};
|
||||
|
||||
enum class FocusPolicy {
|
||||
NoFocus = 0,
|
||||
TabFocus = 0x1,
|
||||
|
@ -281,7 +271,7 @@ public:
|
|||
void set_override_cursor(Gfx::StandardCursor);
|
||||
|
||||
bool load_from_gml(const StringView&);
|
||||
bool load_from_gml(const StringView&, RefPtr<Widget> (*unregistered_child_handler)(const String&));
|
||||
bool load_from_gml(const StringView&, RefPtr<Core::Object> (*unregistered_child_handler)(const String&));
|
||||
|
||||
void set_shrink_to_fit(bool);
|
||||
bool is_shrink_to_fit() const { return m_shrink_to_fit; }
|
||||
|
@ -341,7 +331,7 @@ private:
|
|||
void focus_previous_widget(FocusSource, bool siblings_only);
|
||||
void focus_next_widget(FocusSource, bool siblings_only);
|
||||
|
||||
bool load_from_json(const JsonObject&, RefPtr<Widget> (*unregistered_child_handler)(const String&));
|
||||
virtual bool load_from_json(const JsonObject&, RefPtr<Core::Object> (*unregistered_child_handler)(const String&)) override;
|
||||
|
||||
// HACK: These are used as property getters for the fixed_* size property aliases.
|
||||
int dummy_fixed_width() { return 0; }
|
||||
|
|
Loading…
Add table
Reference in a new issue