浏览代码

LibGUI: Add a simple GWidget class registry/factory

You can now register a GWidget subclass with REGISTER_GWIDGET(class)
and it will be available for factory construction through the new
GWidgetClassRegistration interface.

To obtain a GWidgetClassRegistration for a given class name, you call
GWidgetClassRegistration::find(class_name). You can also iterate over
all the registered classes using GWCR::for_each(callback).

This will be very useful for implementing a proper GUI designer, and
also in the future for things like script bindings.

NOTE: All of the registrations are done in GWidget.cpp at the moment
since I ran into trouble with the fricken linker pruning the global
constructors this mechanism relies on. :^)
Andreas Kling 5 年之前
父节点
当前提交
ca538b6cee

+ 6 - 1
Libraries/LibGUI/GRadioButton.cpp

@@ -1,12 +1,17 @@
+#include <LibDraw/GraphicsBitmap.h>
 #include <LibGUI/GPainter.h>
 #include <LibGUI/GRadioButton.h>
-#include <LibDraw/GraphicsBitmap.h>
 
 static RefPtr<GraphicsBitmap> s_unfilled_circle_bitmap;
 static RefPtr<GraphicsBitmap> s_filled_circle_bitmap;
 static RefPtr<GraphicsBitmap> s_changing_filled_circle_bitmap;
 static RefPtr<GraphicsBitmap> s_changing_unfilled_circle_bitmap;
 
+GRadioButton::GRadioButton(GWidget* parent)
+    : GRadioButton({}, parent)
+{
+}
+
 GRadioButton::GRadioButton(const StringView& text, GWidget* parent)
     : GAbstractButton(text, parent)
 {

+ 2 - 1
Libraries/LibGUI/GRadioButton.h

@@ -10,7 +10,8 @@ public:
     virtual void click() override;
 
 protected:
-    GRadioButton(const StringView& text, GWidget* parent);
+    explicit GRadioButton(GWidget* parent);
+    explicit GRadioButton(const StringView& text, GWidget* parent);
     virtual void paint_event(GPaintEvent&) override;
 
 private:

+ 8 - 3
Libraries/LibGUI/GScrollBar.cpp

@@ -1,8 +1,8 @@
-#include <LibGUI/GPainter.h>
-#include <LibGUI/GScrollBar.h>
 #include <LibDraw/CharacterBitmap.h>
 #include <LibDraw/GraphicsBitmap.h>
 #include <LibDraw/StylePainter.h>
+#include <LibGUI/GPainter.h>
+#include <LibGUI/GScrollBar.h>
 
 static const char* s_up_arrow_bitmap_data = {
     "         "
@@ -57,6 +57,11 @@ static CharacterBitmap* s_down_arrow_bitmap;
 static CharacterBitmap* s_left_arrow_bitmap;
 static CharacterBitmap* s_right_arrow_bitmap;
 
+GScrollBar::GScrollBar(GWidget* parent)
+    : GScrollBar(Orientation::Vertical, parent)
+{
+}
+
 GScrollBar::GScrollBar(Orientation orientation, GWidget* parent)
     : GWidget(parent)
     , m_orientation(orientation)
@@ -239,7 +244,7 @@ void GScrollBar::mousedown_event(GMouseEvent& event)
         return;
     }
     if (has_scrubber() && scrubber_rect().contains(event.position())) {
-	m_scrubber_in_use = true;
+        m_scrubber_in_use = true;
         m_scrubbing = true;
         m_scrub_start_value = value();
         m_scrub_origin = event.position();

+ 1 - 0
Libraries/LibGUI/GScrollBar.h

@@ -38,6 +38,7 @@ public:
     };
 
 private:
+    explicit GScrollBar(GWidget* parent);
     explicit GScrollBar(Orientation, GWidget* parent);
 
     virtual void paint_event(GPaintEvent&) override;

+ 5 - 0
Libraries/LibGUI/GSlider.cpp

@@ -2,6 +2,11 @@
 #include <LibGUI/GPainter.h>
 #include <LibGUI/GSlider.h>
 
+GSlider::GSlider(GWidget* parent)
+    : GSlider(Orientation::Horizontal, parent)
+{
+}
+
 GSlider::GSlider(Orientation orientation, GWidget* parent)
     : GWidget(parent)
     , m_orientation(orientation)

+ 2 - 1
Libraries/LibGUI/GSlider.h

@@ -44,7 +44,8 @@ public:
     Function<void(int)> on_value_changed;
 
 protected:
-    GSlider(Orientation, GWidget*);
+    explicit GSlider(GWidget*);
+    explicit GSlider(Orientation, GWidget*);
 
     virtual void paint_event(GPaintEvent&) override;
     virtual void mousedown_event(GMouseEvent&) override;

+ 5 - 0
Libraries/LibGUI/GTextEditor.cpp

@@ -15,6 +15,11 @@
 
 //#define DEBUG_GTEXTEDITOR
 
+GTextEditor::GTextEditor(GWidget* parent)
+    : GTextEditor(Type::MultiLine, parent)
+{
+}
+
 GTextEditor::GTextEditor(Type type, GWidget* parent)
     : GScrollableWidget(parent)
     , m_type(type)

+ 2 - 1
Libraries/LibGUI/GTextEditor.h

@@ -102,7 +102,8 @@ public:
     void set_cursor(const GTextPosition&);
 
 protected:
-    GTextEditor(Type, GWidget* parent);
+    explicit GTextEditor(GWidget* parent);
+    explicit GTextEditor(Type, GWidget* parent);
 
     virtual void did_change_font() override;
     virtual void paint_event(GPaintEvent&) override;

+ 53 - 0
Libraries/LibGUI/GWidget.cpp

@@ -12,6 +12,59 @@
 #include <LibGUI/GWindowServerConnection.h>
 #include <unistd.h>
 
+#include <LibGUI/GButton.h>
+#include <LibGUI/GCheckBox.h>
+#include <LibGUI/GGroupBox.h>
+#include <LibGUI/GLabel.h>
+#include <LibGUI/GRadioButton.h>
+#include <LibGUI/GScrollBar.h>
+#include <LibGUI/GSlider.h>
+#include <LibGUI/GSpinBox.h>
+#include <LibGUI/GTextBox.h>
+
+REGISTER_GWIDGET(GButton)
+REGISTER_GWIDGET(GCheckBox)
+REGISTER_GWIDGET(GGroupBox)
+REGISTER_GWIDGET(GLabel)
+REGISTER_GWIDGET(GRadioButton)
+REGISTER_GWIDGET(GScrollBar)
+REGISTER_GWIDGET(GSlider)
+REGISTER_GWIDGET(GSpinBox)
+REGISTER_GWIDGET(GTextBox)
+REGISTER_GWIDGET(GWidget)
+
+static HashMap<String, GWidgetClassRegistration*>& widget_classes()
+{
+    static HashMap<String, GWidgetClassRegistration*>* map;
+    if (!map)
+        map = new HashMap<String, GWidgetClassRegistration*>;
+    return *map;
+}
+
+GWidgetClassRegistration::GWidgetClassRegistration(const String& class_name, Function<NonnullRefPtr<GWidget>(GWidget*)> factory)
+    : m_class_name(class_name)
+    , m_factory(move(factory))
+{
+    widget_classes().set(class_name, this);
+}
+
+GWidgetClassRegistration::~GWidgetClassRegistration()
+{
+    ASSERT_NOT_REACHED();
+}
+
+void GWidgetClassRegistration::for_each(Function<void(const GWidgetClassRegistration&)> callback)
+{
+    for (auto& it : widget_classes()) {
+        callback(*it.value);
+    }
+}
+
+const GWidgetClassRegistration* GWidgetClassRegistration::find(const String& class_name)
+{
+    return widget_classes().get(class_name).value_or(nullptr);
+}
+
 GWidget::GWidget(GWidget* parent)
     : CObject(parent, true)
     , m_font(Font::default_font())

+ 25 - 1
Libraries/LibGUI/GWidget.h

@@ -1,8 +1,8 @@
 #pragma once
 
-#include <AK/String.h>
 #include <AK/Badge.h>
 #include <AK/HashMap.h>
+#include <AK/String.h>
 #include <LibCore/CElapsedTimer.h>
 #include <LibCore/CObject.h>
 #include <LibDraw/Color.h>
@@ -12,6 +12,10 @@
 #include <LibGUI/GEvent.h>
 #include <LibGUI/GShortcut.h>
 
+#define REGISTER_GWIDGET(class_name)                           \
+    extern GWidgetClassRegistration registration_##class_name; \
+    GWidgetClassRegistration registration_##class_name(#class_name, [](GWidget* parent) { return class_name::construct(parent); });
+
 class GraphicsBitmap;
 class GAction;
 class GLayout;
@@ -42,6 +46,26 @@ enum class VerticalDirection {
     Down
 };
 
+class GWidget;
+
+class GWidgetClassRegistration {
+    AK_MAKE_NONCOPYABLE(GWidgetClassRegistration)
+    AK_MAKE_NONMOVABLE(GWidgetClassRegistration)
+public:
+    GWidgetClassRegistration(const String& class_name, Function<NonnullRefPtr<GWidget>(GWidget*)> factory);
+    ~GWidgetClassRegistration();
+
+    String class_name() const { return m_class_name; }
+    NonnullRefPtr<GWidget> construct(GWidget* parent) const { return m_factory(parent); }
+
+    static void for_each(Function<void(const GWidgetClassRegistration&)>);
+    static const GWidgetClassRegistration* find(const String& class_name);
+
+private:
+    String m_class_name;
+    Function<NonnullRefPtr<GWidget>(GWidget*)> m_factory;
+};
+
 class GWidget : public CObject {
     C_OBJECT(GWidget)
 public: