mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 05:57:44 +00: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/GBoxLayout.h>
|
||||||
#include <LibGUI/GWidget.h>
|
#include <LibGUI/GWidget.h>
|
||||||
|
|
||||||
//#define GBOXLAYOUT_DEBUG
|
#define GBOXLAYOUT_DEBUG
|
||||||
|
|
||||||
#ifdef GBOXLAYOUT_DEBUG
|
#ifdef GBOXLAYOUT_DEBUG
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -41,14 +41,19 @@ void GBoxLayout::run(GWidget& widget)
|
||||||
Size available_size = widget.size();
|
Size available_size = widget.size();
|
||||||
int number_of_entries_with_fixed_size = 0;
|
int number_of_entries_with_fixed_size = 0;
|
||||||
|
|
||||||
|
int number_of_visible_entries = 0;
|
||||||
|
|
||||||
for (auto& entry : m_entries) {
|
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) {
|
if (entry.widget && entry.widget->size_policy(orientation()) == SizePolicy::Fixed) {
|
||||||
available_size -= entry.widget->preferred_size();
|
available_size -= entry.widget->preferred_size();
|
||||||
++number_of_entries_with_fixed_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
|
#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);
|
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();
|
int current_y = margins().top();
|
||||||
|
|
||||||
for (auto& entry : m_entries) {
|
for (auto& entry : m_entries) {
|
||||||
|
if (!entry.widget->is_visible())
|
||||||
|
continue;
|
||||||
Rect rect(current_x, current_y, 0, 0);
|
Rect rect(current_x, current_y, 0, 0);
|
||||||
if (entry.layout) {
|
if (entry.layout) {
|
||||||
// FIXME: Implement recursive 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 <SharedGraphics/Rect.h>
|
||||||
#include <AK/AKString.h>
|
#include <AK/AKString.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <AK/WeakPtr.h>
|
||||||
#include <Kernel/KeyCode.h>
|
#include <Kernel/KeyCode.h>
|
||||||
|
|
||||||
|
class GObject;
|
||||||
|
|
||||||
class GEvent {
|
class GEvent {
|
||||||
public:
|
public:
|
||||||
enum Type {
|
enum Type {
|
||||||
|
@ -31,6 +34,8 @@ public:
|
||||||
FocusIn,
|
FocusIn,
|
||||||
FocusOut,
|
FocusOut,
|
||||||
WindowCloseRequest,
|
WindowCloseRequest,
|
||||||
|
ChildAdded,
|
||||||
|
ChildRemoved,
|
||||||
};
|
};
|
||||||
|
|
||||||
GEvent() { }
|
GEvent() { }
|
||||||
|
@ -170,3 +175,15 @@ public:
|
||||||
private:
|
private:
|
||||||
int m_timer_id;
|
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:
|
case GEvent::DeferredDestroy:
|
||||||
delete this;
|
delete this;
|
||||||
break;
|
break;
|
||||||
|
case GEvent::ChildAdded:
|
||||||
|
case GEvent::ChildRemoved:
|
||||||
|
return child_event(static_cast<GChildEvent&>(event));
|
||||||
case GEvent::Invalid:
|
case GEvent::Invalid:
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
break;
|
break;
|
||||||
|
@ -39,6 +42,7 @@ void GObject::event(GEvent& event)
|
||||||
void GObject::add_child(GObject& object)
|
void GObject::add_child(GObject& object)
|
||||||
{
|
{
|
||||||
m_children.append(&object);
|
m_children.append(&object);
|
||||||
|
GEventLoop::main().post_event(*this, make<GChildEvent>(GEvent::ChildAdded, object));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GObject::remove_child(GObject& 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) {
|
for (ssize_t i = 0; i < m_children.size(); ++i) {
|
||||||
if (m_children[i] == &object) {
|
if (m_children[i] == &object) {
|
||||||
m_children.remove(i);
|
m_children.remove(i);
|
||||||
|
GEventLoop::main().post_event(*this, make<GChildEvent>(GEvent::ChildRemoved, object));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +60,10 @@ void GObject::timer_event(GTimerEvent&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GObject::child_event(GChildEvent&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void GObject::start_timer(int ms)
|
void GObject::start_timer(int ms)
|
||||||
{
|
{
|
||||||
if (m_timer_id) {
|
if (m_timer_id) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <AK/Weakable.h>
|
#include <AK/Weakable.h>
|
||||||
|
|
||||||
class GEvent;
|
class GEvent;
|
||||||
|
class GChildEvent;
|
||||||
class GTimerEvent;
|
class GTimerEvent;
|
||||||
|
|
||||||
class GObject : public Weakable<GObject> {
|
class GObject : public Weakable<GObject> {
|
||||||
|
@ -29,12 +30,14 @@ public:
|
||||||
|
|
||||||
void delete_later();
|
void delete_later();
|
||||||
|
|
||||||
private:
|
virtual bool is_widget() const { return false; }
|
||||||
|
|
||||||
|
protected:
|
||||||
virtual void timer_event(GTimerEvent&);
|
virtual void timer_event(GTimerEvent&);
|
||||||
|
virtual void child_event(GChildEvent&);
|
||||||
|
|
||||||
|
private:
|
||||||
GObject* m_parent { nullptr };
|
GObject* m_parent { nullptr };
|
||||||
|
|
||||||
int m_timer_id { 0 };
|
int m_timer_id { 0 };
|
||||||
|
|
||||||
Vector<GObject*> m_children;
|
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);
|
paint_event(event);
|
||||||
for (auto* ch : children()) {
|
for (auto* ch : children()) {
|
||||||
auto* child = (GWidget*)ch;
|
auto* child = (GWidget*)ch;
|
||||||
|
if (!child->is_visible())
|
||||||
|
continue;
|
||||||
if (child->relative_rect().intersects(event.rect())) {
|
if (child->relative_rect().intersects(event.rect())) {
|
||||||
auto local_rect = event.rect();
|
auto local_rect = event.rect();
|
||||||
local_rect.intersect(child->relative_rect());
|
local_rect.intersect(child->relative_rect());
|
||||||
|
@ -303,3 +305,14 @@ void GWidget::invalidate_layout()
|
||||||
return;
|
return;
|
||||||
w->main_widget()->do_layout();
|
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>);
|
void notify_layout_changed(Badge<GLayout>);
|
||||||
|
|
||||||
|
bool is_visible() const { return m_visible; }
|
||||||
|
void set_visible(bool);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual bool is_widget() const final { return true; }
|
||||||
|
|
||||||
void handle_paint_event(GPaintEvent&);
|
void handle_paint_event(GPaintEvent&);
|
||||||
void handle_resize_event(GResizeEvent&);
|
void handle_resize_event(GResizeEvent&);
|
||||||
void do_layout();
|
void do_layout();
|
||||||
|
@ -141,4 +146,5 @@ private:
|
||||||
Size m_preferred_size;
|
Size m_preferred_size;
|
||||||
|
|
||||||
bool m_fill_with_background_color { false };
|
bool m_fill_with_background_color { false };
|
||||||
|
bool m_visible { true };
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,8 @@ LIBGUI_OBJS = \
|
||||||
GTextEditor.o \
|
GTextEditor.o \
|
||||||
GClipboard.o \
|
GClipboard.o \
|
||||||
GSortingProxyTableModel.o \
|
GSortingProxyTableModel.o \
|
||||||
|
GStackWidget.o \
|
||||||
|
GEvent.o \
|
||||||
GWindow.o
|
GWindow.o
|
||||||
|
|
||||||
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)
|
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue