From 497300c492c6272df14b0190f19848a25ab6b0de Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 15 Mar 2019 16:12:06 +0100 Subject: [PATCH] LibGUI: Add a GStackWidget for many widgets sharing a single location. Call set_active_widget(GWidget*) to put a new widget on top. --- LibGUI/GBoxLayout.cpp | 11 ++++++-- LibGUI/GEvent.cpp | 12 ++++++++ LibGUI/GEvent.h | 17 +++++++++++ LibGUI/GObject.cpp | 9 ++++++ LibGUI/GObject.h | 9 ++++-- LibGUI/GStackWidget.cpp | 62 +++++++++++++++++++++++++++++++++++++++++ LibGUI/GStackWidget.h | 21 ++++++++++++++ LibGUI/GWidget.cpp | 13 +++++++++ LibGUI/GWidget.h | 6 ++++ LibGUI/Makefile | 2 ++ 10 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 LibGUI/GEvent.cpp create mode 100644 LibGUI/GStackWidget.cpp create mode 100644 LibGUI/GStackWidget.h diff --git a/LibGUI/GBoxLayout.cpp b/LibGUI/GBoxLayout.cpp index 684ae35bdc1..1aa8cd7326e 100644 --- a/LibGUI/GBoxLayout.cpp +++ b/LibGUI/GBoxLayout.cpp @@ -1,7 +1,7 @@ #include #include -//#define GBOXLAYOUT_DEBUG +#define GBOXLAYOUT_DEBUG #ifdef GBOXLAYOUT_DEBUG #include @@ -41,14 +41,19 @@ void GBoxLayout::run(GWidget& widget) Size available_size = widget.size(); int number_of_entries_with_fixed_size = 0; + int number_of_visible_entries = 0; + for (auto& entry : m_entries) { + if (!entry.widget->is_visible()) + continue; + ++number_of_visible_entries; if (entry.widget && entry.widget->size_policy(orientation()) == SizePolicy::Fixed) { available_size -= entry.widget->preferred_size(); ++number_of_entries_with_fixed_size; } } - int number_of_entries_with_automatic_size = m_entries.size() - number_of_entries_with_fixed_size; + int number_of_entries_with_automatic_size = number_of_visible_entries - number_of_entries_with_fixed_size; #ifdef GBOXLAYOUT_DEBUG printf("GBoxLayout: available_size=%s, fixed=%d, fill=%d\n", available_size.to_string().characters(), number_of_entries_with_fixed_size, number_of_entries_with_automatic_size); @@ -74,6 +79,8 @@ void GBoxLayout::run(GWidget& widget) int current_y = margins().top(); for (auto& entry : m_entries) { + if (!entry.widget->is_visible()) + continue; Rect rect(current_x, current_y, 0, 0); if (entry.layout) { // FIXME: Implement recursive layout. diff --git a/LibGUI/GEvent.cpp b/LibGUI/GEvent.cpp new file mode 100644 index 00000000000..4f6b0b2dfbd --- /dev/null +++ b/LibGUI/GEvent.cpp @@ -0,0 +1,12 @@ +#include +#include + +GChildEvent::GChildEvent(Type type, GObject& child) + : GEvent(type) + , m_child(child.make_weak_ptr()) +{ +} + +GChildEvent::~GChildEvent() +{ +} diff --git a/LibGUI/GEvent.h b/LibGUI/GEvent.h index b858539abe6..fb395190b4b 100644 --- a/LibGUI/GEvent.h +++ b/LibGUI/GEvent.h @@ -4,8 +4,11 @@ #include #include #include +#include #include +class GObject; + class GEvent { public: enum Type { @@ -31,6 +34,8 @@ public: FocusIn, FocusOut, WindowCloseRequest, + ChildAdded, + ChildRemoved, }; GEvent() { } @@ -170,3 +175,15 @@ public: private: int m_timer_id; }; + +class GChildEvent final : public GEvent { +public: + GChildEvent(Type, GObject& child); + ~GChildEvent(); + + GObject* child() { return m_child.ptr(); } + const GObject* child() const { return m_child.ptr(); } + +private: + WeakPtr m_child; +}; diff --git a/LibGUI/GObject.cpp b/LibGUI/GObject.cpp index d50e7bdcd98..1d2c59b427b 100644 --- a/LibGUI/GObject.cpp +++ b/LibGUI/GObject.cpp @@ -28,6 +28,9 @@ void GObject::event(GEvent& event) case GEvent::DeferredDestroy: delete this; break; + case GEvent::ChildAdded: + case GEvent::ChildRemoved: + return child_event(static_cast(event)); case GEvent::Invalid: ASSERT_NOT_REACHED(); break; @@ -39,6 +42,7 @@ void GObject::event(GEvent& event) void GObject::add_child(GObject& object) { m_children.append(&object); + GEventLoop::main().post_event(*this, make(GEvent::ChildAdded, object)); } void GObject::remove_child(GObject& object) @@ -46,6 +50,7 @@ void GObject::remove_child(GObject& object) for (ssize_t i = 0; i < m_children.size(); ++i) { if (m_children[i] == &object) { m_children.remove(i); + GEventLoop::main().post_event(*this, make(GEvent::ChildRemoved, object)); return; } } @@ -55,6 +60,10 @@ void GObject::timer_event(GTimerEvent&) { } +void GObject::child_event(GChildEvent&) +{ +} + void GObject::start_timer(int ms) { if (m_timer_id) { diff --git a/LibGUI/GObject.h b/LibGUI/GObject.h index 9ca85b41d3f..b1f24462fe7 100644 --- a/LibGUI/GObject.h +++ b/LibGUI/GObject.h @@ -4,6 +4,7 @@ #include class GEvent; +class GChildEvent; class GTimerEvent; class GObject : public Weakable { @@ -29,12 +30,14 @@ public: void delete_later(); -private: + virtual bool is_widget() const { return false; } + +protected: virtual void timer_event(GTimerEvent&); + virtual void child_event(GChildEvent&); +private: GObject* m_parent { nullptr }; - int m_timer_id { 0 }; - Vector m_children; }; diff --git a/LibGUI/GStackWidget.cpp b/LibGUI/GStackWidget.cpp new file mode 100644 index 00000000000..964c8ced9d3 --- /dev/null +++ b/LibGUI/GStackWidget.cpp @@ -0,0 +1,62 @@ +#include +#include + +GStackWidget::GStackWidget(GWidget* parent) + : GWidget(parent) +{ + set_fill_with_background_color(true); + set_background_color(Color::Red); +} + +GStackWidget::~GStackWidget() +{ +} + +void GStackWidget::set_active_widget(GWidget* widget) +{ + dbgprintf("XXX: GStackWidget: set_active_widget %p\n", widget); + if (widget == m_active_widget) + return; + + if (m_active_widget) + m_active_widget->set_visible(false); + m_active_widget = widget; + if (m_active_widget) { + m_active_widget->set_relative_rect(rect()); + m_active_widget->set_visible(true); + } +} + +void GStackWidget::resize_event(GResizeEvent& event) +{ + if (!m_active_widget) + return; + m_active_widget->set_relative_rect({ { }, event.size() }); +} + +void GStackWidget::child_event(GChildEvent& event) +{ + if (!event.child() || !event.child()->is_widget()) + return; + auto& child = static_cast(*event.child()); + if (event.type() == GEvent::ChildAdded) { + dbgprintf("XXX: GStackWidget: did_add_child %p\n", &child); + if (!m_active_widget) { + set_active_widget(&child); + } else { + child.set_visible(false); + } + } else if (event.type() == GEvent::ChildRemoved) { + dbgprintf("XXX: GStackWidget: did_remove_child %p\n", &child); + if (m_active_widget == &child) { + GWidget* new_active_widget = nullptr; + for (auto* new_child : children()) { + if (new_child->is_widget()) { + new_active_widget = static_cast(new_child); + break; + } + } + set_active_widget(new_active_widget); + } + } +} diff --git a/LibGUI/GStackWidget.h b/LibGUI/GStackWidget.h new file mode 100644 index 00000000000..97c55d920d6 --- /dev/null +++ b/LibGUI/GStackWidget.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +class GStackWidget : public GWidget { +public: + explicit GStackWidget(GWidget* parent); + virtual ~GStackWidget() override; + + GWidget* active_widget() const { return m_active_widget; } + void set_active_widget(GWidget*); + +protected: + virtual void child_event(GChildEvent&) override; + virtual void resize_event(GResizeEvent&) override; + +private: + virtual const char* class_name() const override { return "GStackWidget"; } + + GWidget* m_active_widget { nullptr }; +}; diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index f6e5e1568e8..5a02ce28875 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -91,6 +91,8 @@ void GWidget::handle_paint_event(GPaintEvent& event) paint_event(event); for (auto* ch : children()) { auto* child = (GWidget*)ch; + if (!child->is_visible()) + continue; if (child->relative_rect().intersects(event.rect())) { auto local_rect = event.rect(); local_rect.intersect(child->relative_rect()); @@ -303,3 +305,14 @@ void GWidget::invalidate_layout() return; w->main_widget()->do_layout(); } + +void GWidget::set_visible(bool visible) +{ + if (visible == m_visible) + return; + m_visible = visible; + if (auto* parent = parent_widget()) + parent->invalidate_layout(); + if (m_visible) + update(); +} diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index a7fa50c8b75..b9a65598b2b 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -122,7 +122,12 @@ public: void notify_layout_changed(Badge); + bool is_visible() const { return m_visible; } + void set_visible(bool); + private: + virtual bool is_widget() const final { return true; } + void handle_paint_event(GPaintEvent&); void handle_resize_event(GResizeEvent&); void do_layout(); @@ -141,4 +146,5 @@ private: Size m_preferred_size; bool m_fill_with_background_color { false }; + bool m_visible { true }; }; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index e3f18f61f1e..7ff61444456 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -35,6 +35,8 @@ LIBGUI_OBJS = \ GTextEditor.o \ GClipboard.o \ GSortingProxyTableModel.o \ + GStackWidget.o \ + GEvent.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)