gui: clickable tree, better sorting

This commit is contained in:
Sergiusz Bazanski 2018-08-01 01:27:20 +01:00
parent c8cf0bbc05
commit e9e7004bf9

View File

@ -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 <typename ElementT> template <typename ElementT>
class ElementList : public LazyTreeItem class ElementList : public LazyTreeItem
{ {
@ -156,14 +171,15 @@ class ElementList : public LazyTreeItem
int start = children_.size(); int start = children_.size();
size_t end = std::min(start + count, (int)elements()->size()); size_t end = std::min(start + count, (int)elements()->size());
for (int i = start; i < end; i++) { 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 // Remove X.../Y.../ prefix
QString prefix = QString("X%1/Y%2/").arg(x_).arg(y_); QString prefix = QString("X%1/Y%2/").arg(x_).arg(y_);
if (name.startsWith(prefix)) if (name.startsWith(prefix))
name.remove(0, prefix.size()); 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<StaticTreeItem>(item))); managed_.push_back(std::move(std::unique_ptr<StaticTreeItem>(item)));
} }
} }
@ -182,13 +198,40 @@ class ElementList : public LazyTreeItem
class IdStringList : public StaticTreeItem class IdStringList : public StaticTreeItem
{ {
private: private:
std::unordered_map<IdString, std::unique_ptr<StaticTreeItem>> managed_; std::unordered_map<IdString, std::unique_ptr<IdStringItem>> managed_;
ElementType child_type_; ElementType child_type_;
public: public:
IdStringList(QString name, LazyTreeItem *parent, ElementType type) : IdStringList(QString name, LazyTreeItem *parent, ElementType type) :
StaticTreeItem(name, parent, ElementType::NONE), child_type_(type) {} StaticTreeItem(name, parent, ElementType::NONE), child_type_(type) {}
using StaticTreeItem::StaticTreeItem; using StaticTreeItem::StaticTreeItem;
static std::vector<QString> alphaNumSplit(const QString &str)
{
std::vector<QString> 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<IdString> elements) void updateElements(Context *ctx, std::vector<IdString> elements)
{ {
// for any elements that are not yet in managed_, created them. // for any elements that are not yet in managed_, created them.
@ -197,8 +240,8 @@ class IdStringList : public StaticTreeItem
element_set.insert(elem); element_set.insert(elem);
auto existing = managed_.find(elem); auto existing = managed_.find(elem);
if (existing == managed_.end()) { if (existing == managed_.end()) {
auto item = new StaticTreeItem(elem.c_str(ctx), this, child_type_); auto item = new IdStringItem(ctx, elem, this, child_type_);
managed_.emplace(elem, std::unique_ptr<StaticTreeItem>(item)); managed_.emplace(elem, std::unique_ptr<IdStringItem>(item));
} }
} }
@ -214,38 +257,45 @@ class IdStringList : public StaticTreeItem
// sort new children // sort new children
qSort(children_.begin(), children_.end(), [&](const LazyTreeItem *a, const LazyTreeItem *b){ qSort(children_.begin(), children_.end(), [&](const LazyTreeItem *a, const LazyTreeItem *b){
QString name_a = a->name(); auto parts_a = alphaNumSplit(a->name());
QString name_b = b->name(); auto parts_b = alphaNumSplit(b->name());
// Try to extract a common prefix from both strings.
QString common; if (parts_a.size() != parts_b.size()) {
for (int i = 0; i < std::min(name_a.size(), name_b.size()); i++) { return parts_a.size() < parts_b.size();
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();
} }
// Get the non-common parts. for (int i = 0; i < parts_a.size(); i++) {
name_a.remove(0, common.size()); auto &part_a = parts_a.at(i);
name_b.remove(0, common.size()); auto &part_b = parts_b.at(i);
// And see if they're strings.
bool ok = true;
int num_a = name_a.toInt(&ok); bool a_is_number, b_is_number;
if (!ok) { int a_number = part_a.toInt(&a_is_number);
return a->name() < b->name(); 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;
} }
int num_b = name_b.toInt(&ok);
if (!ok) { // both equal
return a->name() < b->name(); return true;
}
return num_a < num_b;
}); });
} }
}; };