From e9e7004bf9b6abf27bf61063b4abbe5895a11073 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 01:27:20 +0100 Subject: gui: clickable tree, better sorting --- gui/treemodel.h | 118 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 34 deletions(-) diff --git a/gui/treemodel.h b/gui/treemodel.h index a6df8858..69116112 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -119,6 +119,21 @@ class StaticTreeItem : public LazyTreeItem } }; +class IdStringItem : public StaticTreeItem +{ + private: + IdString id_; + + public: + IdStringItem(Context *ctx, IdString str, LazyTreeItem *parent, ElementType type) : + StaticTreeItem(QString(str.c_str(ctx)), parent, type), id_(str) {} + + virtual IdString id() const override + { + return id_; + } +}; + template class ElementList : public LazyTreeItem { @@ -156,14 +171,15 @@ class ElementList : public LazyTreeItem int start = children_.size(); size_t end = std::min(start + count, (int)elements()->size()); for (int i = start; i < end; i++) { - QString name(getter_(ctx_, elements()->at(i)).c_str(ctx_)); + auto idstring = getter_(ctx_, elements()->at(i)); + QString name(idstring.c_str(ctx_)); // Remove X.../Y.../ prefix QString prefix = QString("X%1/Y%2/").arg(x_).arg(y_); if (name.startsWith(prefix)) name.remove(0, prefix.size()); - auto item = new StaticTreeItem(name, this, child_type_); + auto item = new IdStringItem(ctx_, idstring, this, child_type_); managed_.push_back(std::move(std::unique_ptr(item))); } } @@ -182,13 +198,40 @@ class ElementList : public LazyTreeItem class IdStringList : public StaticTreeItem { private: - std::unordered_map> managed_; + std::unordered_map> managed_; ElementType child_type_; public: IdStringList(QString name, LazyTreeItem *parent, ElementType type) : StaticTreeItem(name, parent, ElementType::NONE), child_type_(type) {} using StaticTreeItem::StaticTreeItem; + static std::vector alphaNumSplit(const QString &str) + { + std::vector res; + + QString current_part; + bool number = true; + for (const auto c : str) { + if (current_part.size() == 0 && res.size() == 0) { + current_part.push_back(c); + number = c.isNumber(); + continue; + } + + if (number != c.isNumber()) { + number = c.isNumber(); + res.push_back(current_part); + current_part.clear(); + } + + current_part.push_back(c); + } + + res.push_back(current_part); + + return res; + } + void updateElements(Context *ctx, std::vector elements) { // for any elements that are not yet in managed_, created them. @@ -197,8 +240,8 @@ class IdStringList : public StaticTreeItem element_set.insert(elem); auto existing = managed_.find(elem); if (existing == managed_.end()) { - auto item = new StaticTreeItem(elem.c_str(ctx), this, child_type_); - managed_.emplace(elem, std::unique_ptr(item)); + auto item = new IdStringItem(ctx, elem, this, child_type_); + managed_.emplace(elem, std::unique_ptr(item)); } } @@ -214,38 +257,45 @@ class IdStringList : public StaticTreeItem // sort new children qSort(children_.begin(), children_.end(), [&](const LazyTreeItem *a, const LazyTreeItem *b){ - QString name_a = a->name(); - QString name_b = b->name(); - // Try to extract a common prefix from both strings. - QString common; - for (int i = 0; i < std::min(name_a.size(), name_b.size()); i++) { - const QChar c_a = name_a[i]; - const QChar c_b = name_b[i]; - if (c_a == c_b) { - common.push_back(c_a); - } else { - break; - } - } - // No common part? lexical sort. - if (common.size() == 0) { - return a->name() < b->name(); - } + auto parts_a = alphaNumSplit(a->name()); + auto parts_b = alphaNumSplit(b->name()); - // Get the non-common parts. - name_a.remove(0, common.size()); - name_b.remove(0, common.size()); - // And see if they're strings. - bool ok = true; - int num_a = name_a.toInt(&ok); - if (!ok) { - return a->name() < b->name(); + if (parts_a.size() != parts_b.size()) { + return parts_a.size() < parts_b.size(); } - int num_b = name_b.toInt(&ok); - if (!ok) { - return a->name() < b->name(); + + for (int i = 0; i < parts_a.size(); i++) { + auto &part_a = parts_a.at(i); + auto &part_b = parts_b.at(i); + + + bool a_is_number, b_is_number; + int a_number = part_a.toInt(&a_is_number); + int b_number = part_b.toInt(&b_is_number); + + if (a_is_number && b_is_number) { + if (a_number != b_number) { + return a_number < b_number; + } else { + continue; + } + } + + if (a_is_number != b_is_number) { + return a_is_number; + } + + // both strings + + if (part_a == part_b) { + continue; + } + + return part_a < part_b; } - return num_a < num_b; + + // both equal + return true; }); } }; -- cgit v1.2.3