From baf589ec76bcfb15fe4346dc0cfd18a4309ab29f Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sun, 24 Dec 2023 02:44:05 +0100 Subject: [PATCH] Enable natural sort in the nations view Closes #2050. --- client/CMakeLists.txt | 1 + client/utils/collated_sort_proxy.cpp | 60 ++++++++++++++++++++++++++++ client/utils/collated_sort_proxy.h | 28 +++++++++++++ client/views/view_nations.cpp | 11 ++++- client/views/view_nations.h | 8 ++-- 5 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 client/utils/collated_sort_proxy.cpp create mode 100644 client/utils/collated_sort_proxy.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 3c9a847a59..eb83539af6 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -119,6 +119,7 @@ target_sources( tileset/layer.cpp tileset/sprite.cpp tileset/tilespec.cpp + utils/collated_sort_proxy.cpp utils/colorizer.cpp utils/improvement_seller.cpp utils/unit_quick_menu.cpp diff --git a/client/utils/collated_sort_proxy.cpp b/client/utils/collated_sort_proxy.cpp new file mode 100644 index 0000000000..83f8691542 --- /dev/null +++ b/client/utils/collated_sort_proxy.cpp @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: GPLv3-or-later + * SPDX-FileCopyrightText: Louis Moureaux + */ + +#include "collated_sort_proxy.h" + +namespace freeciv { + +/** + * \class collated_sort_filter_proxy_model + * \brief A sort and filter proxy model supporting string collation. + * + * This model can be used when strings in a model should use a "natural" sort + * order. + */ + +/** + * \brief Constructor. + */ +collated_sort_filter_proxy_model::collated_sort_filter_proxy_model( + QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +/** + * \brief Changes the collator currently in use. + */ +void collated_sort_filter_proxy_model::set_collator(const QCollator &coll) +{ + m_collator = coll; + invalidate(); +} + +/** + * \brief Reimplemented protected function. + * + * This overrides @c QSortFilterProxyModel::lessThan to use the collator when + * comparing strings. + */ +bool collated_sort_filter_proxy_model::lessThan( + const QModelIndex &source_left, const QModelIndex &source_right) const +{ + // Copied from QSortFilterProxyModel + QVariant l = (source_left.model() + ? source_left.model()->data(source_left, sortRole()) + : QVariant()); + QVariant r = (source_right.model() + ? source_right.model()->data(source_right, sortRole()) + : QVariant()); + + if (l.type() == QVariant::String && r.type() == QVariant::String) { + return m_collator(l.toString(), r.toString()); + } + + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + +} // namespace freeciv diff --git a/client/utils/collated_sort_proxy.h b/client/utils/collated_sort_proxy.h new file mode 100644 index 0000000000..1a51c2b5a4 --- /dev/null +++ b/client/utils/collated_sort_proxy.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: GPLv3-or-later + * SPDX-FileCopyrightText: Louis Moureaux + */ + +#pragma once + +#include +#include + +namespace freeciv { + +class collated_sort_filter_proxy_model : public QSortFilterProxyModel { + QCollator m_collator; + +public: + collated_sort_filter_proxy_model(QObject *parent = nullptr); + + /// Retrieves the string collator currently in use. + QCollator collator() const { return m_collator; } + void set_collator(const QCollator &coll); + +protected: + bool lessThan(const QModelIndex &source_left, + const QModelIndex &source_right) const override; +}; + +} // namespace freeciv diff --git a/client/views/view_nations.cpp b/client/views/view_nations.cpp index 3cd11c767a..1914bed18b 100644 --- a/client/views/view_nations.cpp +++ b/client/views/view_nations.cpp @@ -18,6 +18,7 @@ #include #include #include +#include // utility #include "astring.h" #include "fcintl.h" @@ -30,6 +31,7 @@ #include "improvement.h" #include "nation.h" #include "research.h" +#include "utils/collated_sort_proxy.h" #include "views/view_nations_data.h" // client #include "chatline_common.h" @@ -315,11 +317,18 @@ plr_widget::plr_widget(QWidget *widget) : QTableView(widget) pid = new plr_item_delegate(this); setItemDelegate(pid); list_model = new plr_model(this); - filter_model = new QSortFilterProxyModel(); + + filter_model = new freeciv::collated_sort_filter_proxy_model; filter_model->setDynamicSortFilter(true); filter_model->setSourceModel(list_model); filter_model->setFilterRole(Qt::DisplayRole); + QCollator coll; + coll.setCaseSensitivity(Qt::CaseInsensitive); + coll.setLocale(locale()); + coll.setNumericMode(true); + filter_model->set_collator(coll); setModel(filter_model); + setSortingEnabled(true); setSelectionMode(QAbstractItemView::SingleSelection); setAutoScroll(true); diff --git a/client/views/view_nations.h b/client/views/view_nations.h index eea7bc97d1..9843561277 100644 --- a/client/views/view_nations.h +++ b/client/views/view_nations.h @@ -9,13 +9,15 @@ **************************************************************************/ #pragma once +// client +#include "utils/collated_sort_proxy.h" +#include "views/view_nations_data.h" + // Qt #include #include #include #include -// client -#include "views/view_nations_data.h" class QHBoxLayout; class QItemSelection; @@ -102,7 +104,7 @@ private slots: class plr_widget : public QTableView { Q_OBJECT plr_model *list_model; - QSortFilterProxyModel *filter_model; + freeciv::collated_sort_filter_proxy_model *filter_model; plr_item_delegate *pid; plr_report *plr; QString techs_known;