From d3558b6137dfe9f8e895dc34844d35082eff5e3d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 5 Nov 2019 20:41:27 +0100 Subject: [PATCH] LibCore+LibGUI: Allow inserting a CObject/GWidget before another When adding a widget to a parent, you don't always want to append it to the set of existing children, but instead insert it before one of them. This patch makes that possible by adding CObject::insert_child_before() which also produces a ChildAdded event with an additional before_child pointer. This pointer is then used by GWidget to make sure that any layout present maintains the correct order. (Without doing that, newly added children would still be appended into the layout order, despite having a different widget sibling order.) --- Libraries/LibCore/CEvent.cpp | 3 ++- Libraries/LibCore/CEvent.h | 6 +++++- Libraries/LibCore/CObject.cpp | 9 +++++++++ Libraries/LibCore/CObject.h | 1 + Libraries/LibGUI/GLayout.cpp | 12 ++++++++++++ Libraries/LibGUI/GLayout.h | 1 + Libraries/LibGUI/GWidget.cpp | 8 ++++++-- 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Libraries/LibCore/CEvent.cpp b/Libraries/LibCore/CEvent.cpp index eacce964c2..ebc9eeff3d 100644 --- a/Libraries/LibCore/CEvent.cpp +++ b/Libraries/LibCore/CEvent.cpp @@ -1,9 +1,10 @@ #include #include -CChildEvent::CChildEvent(Type type, CObject& child) +CChildEvent::CChildEvent(Type type, CObject& child, CObject* insertion_before_child) : CEvent(type) , m_child(child.make_weak_ptr()) + , m_insertion_before_child(insertion_before_child ? insertion_before_child->make_weak_ptr() : nullptr) { } diff --git a/Libraries/LibCore/CEvent.h b/Libraries/LibCore/CEvent.h index 8e49c07fd4..28c593be45 100644 --- a/Libraries/LibCore/CEvent.h +++ b/Libraries/LibCore/CEvent.h @@ -100,14 +100,18 @@ private: class CChildEvent final : public CEvent { public: - CChildEvent(Type, CObject& child); + CChildEvent(Type, CObject& child, CObject* insertion_before_child = nullptr); ~CChildEvent(); CObject* child() { return m_child.ptr(); } const CObject* child() const { return m_child.ptr(); } + CObject* insertion_before_child() { return m_insertion_before_child.ptr(); } + const CObject* insertion_before_child() const { return m_insertion_before_child.ptr(); } + private: WeakPtr m_child; + WeakPtr m_insertion_before_child; }; class CCustomEvent : public CEvent { diff --git a/Libraries/LibCore/CObject.cpp b/Libraries/LibCore/CObject.cpp index 0058de5b39..7de5a5a044 100644 --- a/Libraries/LibCore/CObject.cpp +++ b/Libraries/LibCore/CObject.cpp @@ -64,6 +64,15 @@ void CObject::add_child(CObject& object) event(*make(CEvent::ChildAdded, object)); } +void CObject::insert_child_before(CObject& new_child, CObject& before_child) +{ + // FIXME: Should we support reparenting objects? + ASSERT(!new_child.parent() || new_child.parent() == this); + new_child.m_parent = this; + m_children.insert_before_matching(new_child, [&](auto& existing_child) { return existing_child.ptr() == &before_child; }); + event(*make(CEvent::ChildAdded, new_child, &before_child)); +} + void CObject::remove_child(CObject& object) { for (int i = 0; i < m_children.size(); ++i) { diff --git a/Libraries/LibCore/CObject.h b/Libraries/LibCore/CObject.h index 7a5ce63785..709d2e607e 100644 --- a/Libraries/LibCore/CObject.h +++ b/Libraries/LibCore/CObject.h @@ -70,6 +70,7 @@ public: bool has_timer() const { return m_timer_id; } void add_child(CObject&); + void insert_child_before(CObject& new_child, CObject& before_child); void remove_child(CObject&); void dump_tree(int indent = 0); diff --git a/Libraries/LibGUI/GLayout.cpp b/Libraries/LibGUI/GLayout.cpp index 13a547e4c0..0875c4ffed 100644 --- a/Libraries/LibGUI/GLayout.cpp +++ b/Libraries/LibGUI/GLayout.cpp @@ -52,6 +52,18 @@ void GLayout::add_widget(GWidget& widget) add_entry(move(entry)); } +void GLayout::insert_widget_before(GWidget& widget, GWidget& before_widget) +{ + Entry entry; + entry.type = Entry::Type::Widget; + entry.widget = widget.make_weak_ptr(); + m_entries.insert_before_matching(move(entry), [&](auto& existing_entry) { + return existing_entry.type == Entry::Type::Widget && existing_entry.widget.ptr() == &before_widget; + }); + if (m_owner) + m_owner->notify_layout_changed({}); +} + void GLayout::remove_widget(GWidget& widget) { m_entries.remove_first_matching([&](auto& entry) { diff --git a/Libraries/LibGUI/GLayout.h b/Libraries/LibGUI/GLayout.h index c98e113426..935c271e42 100644 --- a/Libraries/LibGUI/GLayout.h +++ b/Libraries/LibGUI/GLayout.h @@ -14,6 +14,7 @@ public: virtual ~GLayout(); void add_widget(GWidget&); + void insert_widget_before(GWidget& widget, GWidget& before_widget); void add_layout(OwnPtr&&); void add_spacer(); diff --git a/Libraries/LibGUI/GWidget.cpp b/Libraries/LibGUI/GWidget.cpp index a5004de332..56d9137339 100644 --- a/Libraries/LibGUI/GWidget.cpp +++ b/Libraries/LibGUI/GWidget.cpp @@ -27,8 +27,12 @@ GWidget::~GWidget() void GWidget::child_event(CChildEvent& event) { if (event.type() == GEvent::ChildAdded) { - if (event.child() && is(*event.child()) && layout()) - layout()->add_widget(to(*event.child())); + if (event.child() && is(*event.child()) && layout()) { + if (event.insertion_before_child() && event.insertion_before_child()->is_widget()) + layout()->insert_widget_before(to(*event.child()), to(*event.insertion_before_child())); + else + layout()->add_widget(to(*event.child())); + } } if (event.type() == GEvent::ChildRemoved) { if (layout()) {