diff --git a/Libraries/LibGUI/GAbstractView.cpp b/Libraries/LibGUI/GAbstractView.cpp index cbf370cb96..790554cb13 100644 --- a/Libraries/LibGUI/GAbstractView.cpp +++ b/Libraries/LibGUI/GAbstractView.cpp @@ -8,6 +8,7 @@ GAbstractView::GAbstractView(GWidget* parent) : GScrollableWidget(parent) + , m_selection(*this) { } @@ -96,3 +97,8 @@ void GAbstractView::activate(const GModelIndex& index) if (on_activation) on_activation(index); } + +void GAbstractView::notify_selection_changed(Badge) +{ + update(); +} diff --git a/Libraries/LibGUI/GAbstractView.h b/Libraries/LibGUI/GAbstractView.h index 53ff525ecf..35c569fad6 100644 --- a/Libraries/LibGUI/GAbstractView.h +++ b/Libraries/LibGUI/GAbstractView.h @@ -2,6 +2,7 @@ #include #include +#include #include class GModelEditingDelegate; @@ -9,6 +10,7 @@ class GModelEditingDelegate; class GAbstractView : public GScrollableWidget { C_OBJECT(GAbstractView) friend class GModel; + public: explicit GAbstractView(GWidget* parent); virtual ~GAbstractView() override; @@ -17,6 +19,9 @@ public: GModel* model() { return m_model.ptr(); } const GModel* model() const { return m_model.ptr(); } + GModelSelection& selection() { return m_selection; } + const GModelSelection& selection() const { return m_selection; } + bool is_editable() const { return m_editable; } void set_editable(bool editable) { m_editable = editable; } @@ -37,6 +42,8 @@ public: Function(const GModelIndex&)> aid_create_editing_delegate; + void notify_selection_changed(Badge); + protected: virtual void did_scroll() override; void activate(const GModelIndex&); @@ -50,5 +57,6 @@ protected: private: RefPtr m_model; OwnPtr m_editing_delegate; + GModelSelection m_selection; bool m_activates_on_selection { false }; }; diff --git a/Libraries/LibGUI/GModelSelection.cpp b/Libraries/LibGUI/GModelSelection.cpp new file mode 100644 index 0000000000..6eee4570f9 --- /dev/null +++ b/Libraries/LibGUI/GModelSelection.cpp @@ -0,0 +1,39 @@ +#include +#include + +void GModelSelection::set(const GModelIndex& index) +{ + ASSERT(index.is_valid()); + if (m_indexes.size() == 1 && m_indexes.contains(index)) + return; + m_indexes.clear(); + m_indexes.set(index); + m_view.notify_selection_changed({}); +} + +void GModelSelection::add(const GModelIndex& index) +{ + ASSERT(index.is_valid()); + if (m_indexes.contains(index)) + return; + m_indexes.set(index); + m_view.notify_selection_changed({}); +} + +bool GModelSelection::remove(const GModelIndex& index) +{ + ASSERT(index.is_valid()); + if (!m_indexes.contains(index)) + return false; + m_indexes.remove(index); + m_view.notify_selection_changed({}); + return true; +} + +void GModelSelection::clear() +{ + if (m_indexes.is_empty()) + return; + m_indexes.clear(); + m_view.notify_selection_changed({}); +} diff --git a/Libraries/LibGUI/GModelSelection.h b/Libraries/LibGUI/GModelSelection.h new file mode 100644 index 0000000000..d1c7ba9172 --- /dev/null +++ b/Libraries/LibGUI/GModelSelection.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +class GAbstractView; + +class GModelSelection { +public: + GModelSelection(GAbstractView& view) + : m_view(view) + { + } + + bool is_empty() const { return m_indexes.is_empty(); } + bool contains(const GModelIndex& index) const { return m_indexes.contains(index); } + + void set(const GModelIndex&); + void add(const GModelIndex&); + bool remove(const GModelIndex&); + void clear(); + + template + void for_each_index(Callback callback) + { + for (auto& index : m_indexes) + callback(index); + } + + // FIXME: This doesn't guarantee that what you get is the lowest or "first" index selected.. + GModelIndex first() const + { + if (m_indexes.is_empty()) + return {}; + return *m_indexes.begin(); + } + +private: + GAbstractView& m_view; + HashTable m_indexes; +}; diff --git a/Libraries/LibGUI/Makefile b/Libraries/LibGUI/Makefile index 43ae8f5cbc..ad806e4108 100644 --- a/Libraries/LibGUI/Makefile +++ b/Libraries/LibGUI/Makefile @@ -54,6 +54,7 @@ OBJS = \ GComboBox.o \ GJsonArrayModel.o \ GAboutDialog.o \ + GModelSelection.o \ GWindow.o LIBRARY = libgui.a