diff --git a/Applications/ProcessManager/main.cpp b/Applications/ProcessManager/main.cpp index 9382f59c7c..62f31ff3ae 100644 --- a/Applications/ProcessManager/main.cpp +++ b/Applications/ProcessManager/main.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,7 +18,15 @@ int main(int argc, char** argv) { GApplication app(argc, argv); - auto* widget = new GWidget; + auto* tabwidget = new GTabWidget(nullptr); + + auto* widget = new GWidget(nullptr); + tabwidget->add_widget("Processes", widget); + auto* placeholder_label = new GLabel("Placeholder text"); + placeholder_label->set_fill_with_background_color(true); + placeholder_label->set_background_color(Color::from_rgb(0xffc0c0)); + tabwidget->add_widget("Placeholder", placeholder_label); + widget->set_layout(make(Orientation::Vertical)); auto* toolbar = new GToolBar(widget); @@ -90,7 +100,7 @@ int main(int argc, char** argv) auto* window = new GWindow; window->set_title("ProcessManager"); window->set_rect(20, 200, 680, 400); - window->set_main_widget(widget); + window->set_main_widget(tabwidget); window->set_should_exit_event_loop_on_close(true); window->show(); diff --git a/LibGUI/GTabWidget.cpp b/LibGUI/GTabWidget.cpp new file mode 100644 index 0000000000..0803604d06 --- /dev/null +++ b/LibGUI/GTabWidget.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +GTabWidget::GTabWidget(GWidget* parent) + : GWidget(parent) +{ +} + +GTabWidget::~GTabWidget() +{ +} + +void GTabWidget::add_widget(const String& title, GWidget* widget) +{ + m_tabs.append({ title, widget }); + add_child(*widget); +} + +void GTabWidget::set_active_widget(GWidget* 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(child_rect_for_size(size())); + m_active_widget->set_visible(true); + } + + update(bar_rect()); +} + +void GTabWidget::resize_event(GResizeEvent& event) +{ + if (!m_active_widget) + return; + m_active_widget->set_relative_rect(child_rect_for_size(event.size())); +} + +Rect GTabWidget::child_rect_for_size(const Size& size) const +{ + return { { 0, bar_height() }, { size.width(), size.height() - bar_height() } }; +} + +void GTabWidget::child_event(CChildEvent& event) +{ + if (!event.child() || !event.child()->is_widget()) + return GWidget::child_event(event); + auto& child = static_cast(*event.child()); + if (event.type() == GEvent::ChildAdded) { + if (!m_active_widget) + set_active_widget(&child); + else if (m_active_widget != &child) + child.set_visible(false); + } else if (event.type() == GEvent::ChildRemoved) { + 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); + } + } + GWidget::child_event(event); +} + +Rect GTabWidget::bar_rect() const +{ + return { 0, 0, width(), bar_height() }; +} + +void GTabWidget::paint_event(GPaintEvent& event) +{ + GPainter painter(*this); + painter.add_clip_rect(event.rect()); + + painter.fill_rect(bar_rect(), Color::MidGray); + + for (int i = 0; i < m_tabs.size(); ++i) { + auto button_rect = this->button_rect(i); + StylePainter::paint_button(painter, button_rect, ButtonStyle::Normal, m_tabs[i].widget == m_active_widget); + painter.draw_text(button_rect, m_tabs[i].title, TextAlignment::Center); + } +} + +Rect GTabWidget::button_rect(int index) const +{ + int x_offset = 0; + for (int i = 0; i < index; ++i) + x_offset += m_tabs[i].width(font()); + return { x_offset, 0, m_tabs[index].width(font()), bar_height() }; +} + +int GTabWidget::TabData::width(const Font& font) const +{ + return 16 + font.width(title); +} + +void GTabWidget::mousedown_event(GMouseEvent& event) +{ + for (int i = 0; i < m_tabs.size(); ++i) { + auto button_rect = this->button_rect(i); + if (!button_rect.contains(event.position())) + continue; + set_active_widget(m_tabs[i].widget); + } +} diff --git a/LibGUI/GTabWidget.h b/LibGUI/GTabWidget.h new file mode 100644 index 0000000000..09bd3fc81b --- /dev/null +++ b/LibGUI/GTabWidget.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +class GTabWidget : public GWidget { +public: + explicit GTabWidget(GWidget* parent); + virtual ~GTabWidget() override; + + GWidget* active_widget() const { return m_active_widget; } + void set_active_widget(GWidget*); + + int bar_height() const { return 22; } + + void add_widget(const String&, GWidget*); + + virtual const char* class_name() const override { return "GTabWidget"; } + +protected: + virtual void paint_event(GPaintEvent&) override; + virtual void child_event(CChildEvent&) override; + virtual void resize_event(GResizeEvent&) override; + virtual void mousedown_event(GMouseEvent&) override; + +private: + Rect child_rect_for_size(const Size&) const; + Rect button_rect(int index) const; + Rect bar_rect() const; + + GWidget* m_active_widget { nullptr }; + + struct TabData { + Rect rect(const Font&) const; + int width(const Font&) const; + String title; + GWidget* widget { nullptr }; + }; + Vector m_tabs; +}; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index 14b0427c43..9e27ced8fd 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -54,6 +54,7 @@ LIBGUI_OBJS = \ GGroupBox.o \ GSlider.o \ GResizeCorner.o \ + GTabWidget.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)