mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05:00
LibGUI: Add a GStackWidget for many widgets sharing a single location.
Call set_active_widget(GWidget*) to put a new widget on top.
This commit is contained in:
parent
ab92252ee6
commit
497300c492
10 changed files with 157 additions and 5 deletions
|
@ -1,7 +1,7 @@
|
|||
#include <LibGUI/GBoxLayout.h>
|
||||
#include <LibGUI/GWidget.h>
|
||||
|
||||
//#define GBOXLAYOUT_DEBUG
|
||||
#define GBOXLAYOUT_DEBUG
|
||||
|
||||
#ifdef GBOXLAYOUT_DEBUG
|
||||
#include <stdio.h>
|
||||
|
@ -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.
|
||||
|
|
12
LibGUI/GEvent.cpp
Normal file
12
LibGUI/GEvent.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <LibGUI/GEvent.h>
|
||||
#include <LibGUI/GObject.h>
|
||||
|
||||
GChildEvent::GChildEvent(Type type, GObject& child)
|
||||
: GEvent(type)
|
||||
, m_child(child.make_weak_ptr())
|
||||
{
|
||||
}
|
||||
|
||||
GChildEvent::~GChildEvent()
|
||||
{
|
||||
}
|
|
@ -4,8 +4,11 @@
|
|||
#include <SharedGraphics/Rect.h>
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
|
||||
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<GObject> m_child;
|
||||
};
|
||||
|
|
|
@ -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<GChildEvent&>(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<GChildEvent>(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<GChildEvent>(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) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <AK/Weakable.h>
|
||||
|
||||
class GEvent;
|
||||
class GChildEvent;
|
||||
class GTimerEvent;
|
||||
|
||||
class GObject : public Weakable<GObject> {
|
||||
|
@ -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<GObject*> m_children;
|
||||
};
|
||||
|
|
62
LibGUI/GStackWidget.cpp
Normal file
62
LibGUI/GStackWidget.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include <LibGUI/GStackWidget.h>
|
||||
#include <LibGUI/GBoxLayout.h>
|
||||
|
||||
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<GWidget&>(*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<GWidget*>(new_child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_active_widget(new_active_widget);
|
||||
}
|
||||
}
|
||||
}
|
21
LibGUI/GStackWidget.h
Normal file
21
LibGUI/GStackWidget.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GWidget.h>
|
||||
|
||||
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 };
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -122,7 +122,12 @@ public:
|
|||
|
||||
void notify_layout_changed(Badge<GLayout>);
|
||||
|
||||
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 };
|
||||
};
|
||||
|
|
|
@ -35,6 +35,8 @@ LIBGUI_OBJS = \
|
|||
GTextEditor.o \
|
||||
GClipboard.o \
|
||||
GSortingProxyTableModel.o \
|
||||
GStackWidget.o \
|
||||
GEvent.o \
|
||||
GWindow.o
|
||||
|
||||
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)
|
||||
|
|
Loading…
Add table
Reference in a new issue