diff --git a/LibGUI/GComboBox.cpp b/LibGUI/GComboBox.cpp new file mode 100644 index 0000000000..3b5c949e0d --- /dev/null +++ b/LibGUI/GComboBox.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include + +GComboBox::GComboBox(GWidget* parent) + : GWidget(parent) +{ + m_editor = new GTextEditor(GTextEditor::Type::SingleLine, this); + m_editor->on_change = [this] { + if (on_change) + on_change(m_editor->text()); + }; + m_open_button = new GButton(this); + m_open_button->set_focusable(false); + m_open_button->set_text("\xf7"); + m_open_button->on_click = [this](auto&) { + if (m_list_window->is_visible()) + close(); + else + open(); + }; + + m_list_window = new GWindow(this); + // FIXME: This is obviously not a tooltip window, but it's the closest thing to what we want atm. + m_list_window->set_window_type(GWindowType::Tooltip); + m_list_window->set_should_destroy_on_close(false); + + m_list_view = new GListView(nullptr); + m_list_view->horizontal_scrollbar().set_visible(false); + m_list_window->set_main_widget(m_list_view); + + m_list_view->on_selection = [this](auto& index) { + ASSERT(model()); + auto new_value = model()->data(index).to_string(); + m_editor->set_text(new_value); + m_editor->select_all(); + m_list_window->hide(); + }; +} + +GComboBox::~GComboBox() +{ +} + +void GComboBox::resize_event(GResizeEvent& event) +{ + int frame_thickness = m_editor->frame_thickness(); + int button_height = event.size().height() - frame_thickness * 2; + int button_width = 15; + m_open_button->set_relative_rect(width() - button_width - frame_thickness, frame_thickness, button_width, button_height); + m_editor->set_relative_rect(0, 0, width(), height()); +} + +void GComboBox::set_model(NonnullRefPtr model) +{ + m_list_view->set_model(move(model)); +} + +void GComboBox::open() +{ + if (!model()) + return; + + auto my_screen_rect = screen_relative_rect(); + + int longest_item_width = 0; + for (int i = 0; i < model()->row_count(); ++i) { + auto index = model()->index(i); + auto item_text = model()->data(index).to_string(); + longest_item_width = max(longest_item_width, m_list_view->font().width(item_text)); + } + Size size { + max(width(), longest_item_width + m_list_view->width_occupied_by_vertical_scrollbar()) + m_list_view->frame_thickness() * 2 + m_list_view->horizontal_padding(), + model()->row_count() * m_list_view->item_height() + m_list_view->frame_thickness() * 2 + }; + + m_list_window->set_rect({ my_screen_rect.bottom_left(), size }); + m_list_window->show(); +} + +void GComboBox::close() +{ + m_list_window->hide(); +} + +String GComboBox::text() const +{ + return m_editor->text(); +} diff --git a/LibGUI/GComboBox.h b/LibGUI/GComboBox.h new file mode 100644 index 0000000000..7d70fe2ae5 --- /dev/null +++ b/LibGUI/GComboBox.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +class GButton; +class GTextEditor; + +class GComboBox : public GWidget { +public: + explicit GComboBox(GWidget* parent = nullptr); + virtual ~GComboBox() override; + + String text() const; + + void open(); + void close(); + + GModel* model() { return m_list_view->model(); } + const GModel* model() const { return m_list_view->model(); } + void set_model(NonnullRefPtr); + + Function on_change; + + virtual const char* class_name() const override { return "GComboBox"; } + +protected: + virtual void resize_event(GResizeEvent&) override; + +private: + GTextEditor* m_editor { nullptr }; + GButton* m_open_button { nullptr }; + GWindow* m_list_window { nullptr }; + GListView* m_list_view { nullptr }; +}; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index fc4ce53df1..24d6b56fb4 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -60,6 +60,7 @@ LIBGUI_OBJS = \ GRadioButton.o \ GAbstractButton.o \ GListView.o \ + GComboBox.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)