gui: clickable tree, better sorting
This commit is contained in:
parent
c8cf0bbc05
commit
e9e7004bf9
118
gui/treemodel.h
118
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 <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;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user