From 3b9bde533a6a86f6ae5b3902e55a6f649d092608 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Mon, 30 Jul 2018 09:46:01 +0100 Subject: gui: sort tree elements somewhat smarter --- gui/treemodel.cc | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index 59391f02..0a503003 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -21,11 +21,6 @@ NEXTPNR_NAMESPACE_BEGIN -static bool contextTreeItemLessThan(const ContextTreeItem *v1, const ContextTreeItem *v2) - { - return v1->name() < v2->name(); - } - ContextTreeItem::ContextTreeItem() { parentNode = nullptr; } ContextTreeItem::ContextTreeItem(QString name) @@ -54,7 +49,40 @@ void ContextTreeItem::sort() { for (auto item : children) if (item->count()>1) item->sort(); - qSort(children.begin(), children.end(), contextTreeItemLessThan); + qSort(children.begin(), children.end(), [&](const ContextTreeItem *a, const ContextTreeItem *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(); + } + + // 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(); + } + int num_b = name_b.toInt(&ok); + if (!ok) { + return a->name() < b->name(); + } + return num_a < num_b; + }); } ContextTreeModel::ContextTreeModel(QObject *parent) : QAbstractItemModel(parent) { root = new ContextTreeItem(); } @@ -326,4 +354,4 @@ Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const ContextTreeItem *node = nodeFromIndex(index); return Qt::ItemIsEnabled | (node->type() != ElementType::NONE ? Qt::ItemIsSelectable : Qt::NoItemFlags); } -NEXTPNR_NAMESPACE_END \ No newline at end of file +NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From cc0ffee3fe53371915a1aa583878b15a57f9e390 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 31 Jul 2018 15:49:47 +0100 Subject: gui: lazy loading of tree model --- gui/designwidget.cc | 138 +++++++++++------------ gui/designwidget.h | 4 +- gui/treemodel.cc | 312 ++++++++++++++++++++++------------------------------ gui/treemodel.h | 216 ++++++++++++++++++++++++++++++++++-- 4 files changed, 414 insertions(+), 256 deletions(-) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 5107fbee..249df423 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -222,9 +222,9 @@ void DesignWidget::updateTree() { clearProperties(); - QMap::iterator i = highlightSelected.begin(); + QMap::iterator i = highlightSelected.begin(); while (i != highlightSelected.end()) { - QMap::iterator prev = i; + QMap::iterator prev = i; ++i; if (prev.key()->type() == ElementType::NET && ctx->nets.find(prev.key()->id()) == ctx->nets.end()) { highlightSelected.erase(prev); @@ -308,25 +308,25 @@ QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name) void DesignWidget::onClickedBel(BelId bel, bool keep) { - ContextTreeItem *item = treeModel->nodeForIdType(ElementType::BEL, ctx->getBelName(bel).c_str(ctx)); - selectionModel->setCurrentIndex(treeModel->indexFromNode(item), - keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + //LazyTreeItem *item = treeModel->nodeForIdType(ElementType::BEL, ctx->getBelName(bel).c_str(ctx)); + //selectionModel->setCurrentIndex(treeModel->indexFromNode(item), + // keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); Q_EMIT selected(getDecals(ElementType::BEL, ctx->getBelName(bel)), keep); } void DesignWidget::onClickedWire(WireId wire, bool keep) { - ContextTreeItem *item = treeModel->nodeForIdType(ElementType::WIRE, ctx->getWireName(wire).c_str(ctx)); - selectionModel->setCurrentIndex(treeModel->indexFromNode(item), - keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + //LazyTreeItem *item = treeModel->nodeForIdType(ElementType::WIRE, ctx->getWireName(wire).c_str(ctx)); + //selectionModel->setCurrentIndex(treeModel->indexFromNode(item), + // keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); Q_EMIT selected(getDecals(ElementType::WIRE, ctx->getWireName(wire)), keep); } void DesignWidget::onClickedPip(PipId pip, bool keep) { - ContextTreeItem *item = treeModel->nodeForIdType(ElementType::PIP, ctx->getPipName(pip).c_str(ctx)); - selectionModel->setCurrentIndex(treeModel->indexFromNode(item), - keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + //LazyTreeItem *item = treeModel->nodeForIdType(ElementType::PIP, ctx->getPipName(pip).c_str(ctx)); + //selectionModel->setCurrentIndex(treeModel->indexFromNode(item), + // keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); Q_EMIT selected(getDecals(ElementType::PIP, ctx->getPipName(pip)), keep); } @@ -338,7 +338,7 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti if (selectionModel->selectedIndexes().size() > 1) { std::vector decals; for (auto index : selectionModel->selectedIndexes()) { - ContextTreeItem *item = treeModel->nodeFromIndex(index); + LazyTreeItem *item = treeModel->nodeFromIndex(index); std::vector d = getDecals(item->type(), item->id()); std::move(d.begin(), d.end(), std::back_inserter(decals)); } @@ -348,7 +348,7 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti QModelIndex index = selectionModel->selectedIndexes().at(0); if (!index.isValid()) return; - ContextTreeItem *clickItem = treeModel->nodeFromIndex(index); + LazyTreeItem *clickItem = treeModel->nodeFromIndex(index); ElementType type = clickItem->type(); if (type == ElementType::NONE) @@ -596,7 +596,7 @@ std::vector DesignWidget::getDecals(ElementType type, IdString value) return decals; } -void DesignWidget::updateHighlightGroup(QList items, int group) +void DesignWidget::updateHighlightGroup(QList items, int group) { const bool shouldClear = items.size() == 1; for (auto item : items) { @@ -620,53 +620,53 @@ void DesignWidget::updateHighlightGroup(QList items, int grou void DesignWidget::prepareMenuProperty(const QPoint &pos) { - QTreeWidget *tree = propertyEditor->treeWidget(); - QList items; - for (auto itemContextMenu : tree->selectedItems()) { - QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); - if (!browserItem) - continue; - QtProperty *selectedProperty = browserItem->property(); - ElementType type = getElementTypeByName(selectedProperty->propertyId()); - if (type == ElementType::NONE) - continue; - IdString value = ctx->id(selectedProperty->valueText().toStdString()); - items.append(treeModel->nodeForIdType(type, value.c_str(ctx))); - } - int selectedIndex = -1; - if (items.size() == 1) { - ContextTreeItem *item = items.at(0); - if (highlightSelected.contains(item)) - selectedIndex = highlightSelected[item]; - } - - QMenu menu(this); - QAction *selectAction = new QAction("&Select", this); - connect(selectAction, &QAction::triggered, this, [this, items] { - std::vector decals; - for (auto clickItem : items) { - std::vector d = getDecals(clickItem->type(), clickItem->id()); - std::move(d.begin(), d.end(), std::back_inserter(decals)); - } - Q_EMIT selected(decals, false); - }); - menu.addAction(selectAction); - - QMenu *subMenu = menu.addMenu("Highlight"); - QActionGroup *group = new QActionGroup(this); - group->setExclusive(true); - for (int i = 0; i < 8; i++) { - QPixmap pixmap(32, 32); - pixmap.fill(QColor(highlightColors[i])); - QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this); - action->setCheckable(true); - subMenu->addAction(action); - group->addAction(action); - if (selectedIndex == i) - action->setChecked(true); - connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); - } - menu.exec(tree->mapToGlobal(pos)); + //QTreeWidget *tree = propertyEditor->treeWidget(); + //QList items; + //for (auto itemContextMenu : tree->selectedItems()) { + // QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); + // if (!browserItem) + // continue; + // QtProperty *selectedProperty = browserItem->property(); + // ElementType type = getElementTypeByName(selectedProperty->propertyId()); + // if (type == ElementType::NONE) + // continue; + // IdString value = ctx->id(selectedProperty->valueText().toStdString()); + // items.append(treeModel->nodeForIdType(type, value.c_str(ctx))); + //} + //int selectedIndex = -1; + //if (items.size() == 1) { + // LazyTreeItem *item = items.at(0); + // if (highlightSelected.contains(item)) + // selectedIndex = highlightSelected[item]; + //} + + //QMenu menu(this); + //QAction *selectAction = new QAction("&Select", this); + //connect(selectAction, &QAction::triggered, this, [this, items] { + // std::vector decals; + // for (auto clickItem : items) { + // std::vector d = getDecals(clickItem->type(), clickItem->id()); + // std::move(d.begin(), d.end(), std::back_inserter(decals)); + // } + // Q_EMIT selected(decals, false); + //}); + //menu.addAction(selectAction); + + //QMenu *subMenu = menu.addMenu("Highlight"); + //QActionGroup *group = new QActionGroup(this); + //group->setExclusive(true); + //for (int i = 0; i < 8; i++) { + // QPixmap pixmap(32, 32); + // pixmap.fill(QColor(highlightColors[i])); + // QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this); + // action->setCheckable(true); + // subMenu->addAction(action); + // group->addAction(action); + // if (selectedIndex == i) + // action->setChecked(true); + // connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); + //} + //menu.exec(tree->mapToGlobal(pos)); } void DesignWidget::prepareMenuTree(const QPoint &pos) @@ -676,13 +676,13 @@ void DesignWidget::prepareMenuTree(const QPoint &pos) if (selectionModel->selectedIndexes().size() == 0) return; - QList items; + QList items; for (auto index : selectionModel->selectedIndexes()) { - ContextTreeItem *item = treeModel->nodeFromIndex(index); + LazyTreeItem *item = treeModel->nodeFromIndex(index); items.append(item); } if (items.size() == 1) { - ContextTreeItem *item = items.at(0); + LazyTreeItem *item = items.at(0); if (highlightSelected.contains(item)) selectedIndex = highlightSelected[item]; } @@ -706,11 +706,11 @@ void DesignWidget::prepareMenuTree(const QPoint &pos) void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column) { - QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property(); - ElementType type = getElementTypeByName(selectedProperty->propertyId()); - ContextTreeItem *it = treeModel->nodeForIdType(type, selectedProperty->valueText()); - if (it) - selectionModel->setCurrentIndex(treeModel->indexFromNode(it), QItemSelectionModel::ClearAndSelect); + //QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property(); + //ElementType type = getElementTypeByName(selectedProperty->propertyId()); + //LazyTreeItem *it = treeModel->nodeForIdType(type, selectedProperty->valueText()); + //if (it) + // selectionModel->setCurrentIndex(treeModel->indexFromNode(it), QItemSelectionModel::ClearAndSelect); } void DesignWidget::onDoubleClicked(const QModelIndex &index) { Q_EMIT zoomSelected(); } diff --git a/gui/designwidget.h b/gui/designwidget.h index 27ead589..b229a8a8 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -51,7 +51,7 @@ class DesignWidget : public QWidget void updateButtons(); void addToHistory(QModelIndex item); std::vector getDecals(ElementType type, IdString value); - void updateHighlightGroup(QList item, int group); + void updateHighlightGroup(QList item, int group); Q_SIGNALS: void info(std::string text); void selected(std::vector decal, bool keep); @@ -97,7 +97,7 @@ class DesignWidget : public QWidget QAction *actionClear; QColor highlightColors[8]; - QMap highlightSelected; + QMap highlightSelected; }; NEXTPNR_NAMESPACE_END diff --git a/gui/treemodel.cc b/gui/treemodel.cc index 0a503003..9a501eb9 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -39,11 +39,12 @@ ContextTreeItem::~ContextTreeItem() parentNode->children.removeOne(this); qDeleteAll(children); } -void ContextTreeItem::addChild(ContextTreeItem *item) -{ - item->parentNode = this; - children.append(item); -} + +//void ContextTreeItem::addChild(ContextTreeItem *item) +//{ +// item->parentNode = this; +// children.append(item); +//} void ContextTreeItem::sort() { @@ -85,9 +86,11 @@ void ContextTreeItem::sort() }); } -ContextTreeModel::ContextTreeModel(QObject *parent) : QAbstractItemModel(parent) { root = new ContextTreeItem(); } +ContextTreeModel::ContextTreeModel(QObject *parent) : + QAbstractItemModel(parent), + root_(new StaticTreeItem("Elements", nullptr)) {} -ContextTreeModel::~ContextTreeModel() { delete root; } +ContextTreeModel::~ContextTreeModel() {} void ContextTreeModel::loadData(Context *ctx) { @@ -96,109 +99,49 @@ void ContextTreeModel::loadData(Context *ctx) beginResetModel(); - delete root; - root = new ContextTreeItem(); - - for (int i = 0; i < 6; i++) - nameToItem[i].clear(); - - IdString none; - - ContextTreeItem *bels_root = new ContextTreeItem("Bels"); - root->addChild(bels_root); - QMap bel_items; - - // Add bels to tree - for (auto bel : ctx->getBels()) { - IdString id = ctx->getBelName(bel); - QStringList items = QString(id.c_str(ctx)).split("/"); - QString name; - ContextTreeItem *parent = bels_root; - for (int i = 0; i < items.size(); i++) { - if (!name.isEmpty()) - name += "/"; - name += items.at(i); - if (!bel_items.contains(name)) { - if (i == items.size() - 1) { - ContextTreeItem *item = new ContextTreeItem(id, ElementType::BEL, items.at(i)); - parent->addChild(item); - nameToItem[0].insert(name, item); - } else { - ContextTreeItem *item = new ContextTreeItem(none, ElementType::NONE, items.at(i)); - parent->addChild(item); - bel_items.insert(name, item); - } - } - parent = bel_items[name]; + { + printf("generating bel map...\n"); + std::map, std::vector> belMap; + for (auto bel : ctx->getBels()) { + auto loc = ctx->getBelLocation(bel); + belMap[std::pair(loc.x, loc.y)].push_back(bel); } - } - bels_root->sort(); - - ContextTreeItem *wire_root = new ContextTreeItem("Wires"); - root->addChild(wire_root); - QMap wire_items; - - // Add wires to tree - for (auto wire : ctx->getWires()) { - auto id = ctx->getWireName(wire); - QStringList items = QString(id.c_str(ctx)).split("/"); - QString name; - ContextTreeItem *parent = wire_root; - for (int i = 0; i < items.size(); i++) { - if (!name.isEmpty()) - name += "/"; - name += items.at(i); - if (!wire_items.contains(name)) { - if (i == items.size() - 1) { - ContextTreeItem *item = new ContextTreeItem(id, ElementType::WIRE, items.at(i)); - parent->addChild(item); - nameToItem[1].insert(name, item); - } else { - ContextTreeItem *item = new ContextTreeItem(none, ElementType::NONE, items.at(i)); - parent->addChild(item); - wire_items.insert(name, item); - } - } - parent = wire_items[name]; + printf("generating bel static tree...\n"); + auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); }; + bel_root_ = std::unique_ptr(new BelXYRoot(ctx, "Bels", root_.get(), belMap, belGetter)); + + printf("generating wire map...\n"); + std::map, std::vector> wireMap; + //TODO(q3k): change this once we have an API to get wire categories/locations/labels + for (int i = 0; i < ctx->chip_info->num_wires; i++) { + const auto wire = &ctx->chip_info->wire_data[i]; + WireId wireid; + wireid.index = i; + wireMap[std::pair(wire->x, wire->y)].push_back(wireid); } - } - wire_root->sort(); - - ContextTreeItem *pip_root = new ContextTreeItem("Pips"); - root->addChild(pip_root); - QMap pip_items; - - // Add pips to tree - for (auto pip : ctx->getPips()) { - auto id = ctx->getPipName(pip); - QStringList items = QString(id.c_str(ctx)).split("/"); - QString name; - ContextTreeItem *parent = pip_root; - for (int i = 0; i < items.size(); i++) { - if (!name.isEmpty()) - name += "/"; - name += items.at(i); - if (!pip_items.contains(name)) { - if (i == items.size() - 1) { - ContextTreeItem *item = new ContextTreeItem(id, ElementType::PIP, items.at(i)); - parent->addChild(item); - nameToItem[2].insert(name, item); - } else { - ContextTreeItem *item = new ContextTreeItem(none, ElementType::NONE, items.at(i)); - parent->addChild(item); - pip_items.insert(name, item); - } - } - parent = pip_items[name]; + printf("generating wire static tree...\n"); + auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; + wire_root_ = std::unique_ptr(new WireXYRoot(ctx, "Wires", root_.get(), wireMap, wireGetter)); + + printf("generating pip map...\n"); + std::map, std::vector> pipMap; + //TODO(q3k): change this once we have an API to get wire categories/locations/labels + for (int i = 0; i < ctx->chip_info->num_pips; i++) { + const auto pip = &ctx->chip_info->pip_data[i]; + PipId pipid; + pipid.index = i; + pipMap[std::pair(pip->x, pip->y)].push_back(pipid); } + printf("generating pip static tree...\n"); + auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); }; + pip_root_ = std::unique_ptr(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter)); } - pip_root->sort(); - nets_root = new ContextTreeItem("Nets"); - root->addChild(nets_root); + //nets_root = new ContextTreeItem("Nets"); + //root->addChild(nets_root); - cells_root = new ContextTreeItem("Cells"); - root->addChild(cells_root); + //cells_root = new ContextTreeItem("Cells"); + //root->addChild(cells_root); endResetModel(); } @@ -212,61 +155,61 @@ void ContextTreeModel::updateData(Context *ctx) //QModelIndex nets_index = indexFromNode(nets_root); // Remove nets not existing any more - QMap::iterator i = nameToItem[3].begin(); - while (i != nameToItem[3].end()) { - QMap::iterator prev = i; - ++i; - if (ctx->nets.find(ctx->id(prev.key().toStdString())) == ctx->nets.end()) { - //int pos = prev.value()->parent()->indexOf(prev.value()); - //beginRemoveRows(nets_index, pos, pos); - delete prev.value(); - nameToItem[3].erase(prev); - //endRemoveRows(); - } - } - // Add nets to tree - for (auto &item : ctx->nets) { - auto id = item.first; - QString name = QString(id.c_str(ctx)); - if (!nameToItem[3].contains(name)) { - //beginInsertRows(nets_index, nets_root->count() + 1, nets_root->count() + 1); - ContextTreeItem *newItem = new ContextTreeItem(id, ElementType::NET, name); - nets_root->addChild(newItem); - nameToItem[3].insert(name, newItem); - //endInsertRows(); - } - } - - nets_root->sort(); + //QMap::iterator i = nameToItem[3].begin(); + //while (i != nameToItem[3].end()) { + // QMap::iterator prev = i; + // ++i; + // if (ctx->nets.find(ctx->id(prev.key().toStdString())) == ctx->nets.end()) { + // //int pos = prev.value()->parent()->indexOf(prev.value()); + // //beginRemoveRows(nets_index, pos, pos); + // delete prev.value(); + // nameToItem[3].erase(prev); + // //endRemoveRows(); + // } + //} + //// Add nets to tree + //for (auto &item : ctx->nets) { + // auto id = item.first; + // QString name = QString(id.c_str(ctx)); + // if (!nameToItem[3].contains(name)) { + // //beginInsertRows(nets_index, nets_root->count() + 1, nets_root->count() + 1); + // ContextTreeItem *newItem = new ContextTreeItem(id, ElementType::NET, name); + // nets_root->addChild(newItem); + // nameToItem[3].insert(name, newItem); + // //endInsertRows(); + // } + //} + + //nets_root->sort(); //QModelIndex cell_index = indexFromNode(cells_root); // Remove cells not existing any more - i = nameToItem[4].begin(); - while (i != nameToItem[4].end()) { - QMap::iterator prev = i; - ++i; - if (ctx->cells.find(ctx->id(prev.key().toStdString())) == ctx->cells.end()) { - //int pos = prev.value()->parent()->indexOf(prev.value()); - //beginRemoveRows(cell_index, pos, pos); - delete prev.value(); - nameToItem[4].erase(prev); - //endRemoveRows(); - } - } - // Add cells to tree - for (auto &item : ctx->cells) { - auto id = item.first; - QString name = QString(id.c_str(ctx)); - if (!nameToItem[4].contains(name)) { - //beginInsertRows(cell_index, cells_root->count() + 1, cells_root->count() + 1); - ContextTreeItem *newItem = new ContextTreeItem(id, ElementType::CELL, name); - cells_root->addChild(newItem); - nameToItem[4].insert(name, newItem); - //endInsertRows(); - } - } - - cells_root->sort(); + //i = nameToItem[4].begin(); + //while (i != nameToItem[4].end()) { + // QMap::iterator prev = i; + // ++i; + // if (ctx->cells.find(ctx->id(prev.key().toStdString())) == ctx->cells.end()) { + // //int pos = prev.value()->parent()->indexOf(prev.value()); + // //beginRemoveRows(cell_index, pos, pos); + // delete prev.value(); + // nameToItem[4].erase(prev); + // //endRemoveRows(); + // } + //} + //// Add cells to tree + //for (auto &item : ctx->cells) { + // auto id = item.first; + // QString name = QString(id.c_str(ctx)); + // if (!nameToItem[4].contains(name)) { + // //beginInsertRows(cell_index, cells_root->count() + 1, cells_root->count() + 1); + // ContextTreeItem *newItem = new ContextTreeItem(id, ElementType::CELL, name); + // cells_root->addChild(newItem); + // nameToItem[4].insert(name, newItem); + // //endInsertRows(); + // } + //} + + //cells_root->sort(); endResetModel(); } @@ -277,18 +220,19 @@ int ContextTreeModel::columnCount(const QModelIndex &parent) const { return 1; } QModelIndex ContextTreeModel::index(int row, int column, const QModelIndex &parent) const { - ContextTreeItem *node = nodeFromIndex(parent); + LazyTreeItem *node = nodeFromIndex(parent); if (row >= node->count()) return QModelIndex(); - return createIndex(row, column, node->at(row)); + + return createIndex(row, column, node->child(row)); } QModelIndex ContextTreeModel::parent(const QModelIndex &child) const { - ContextTreeItem *parent = nodeFromIndex(child)->parent(); - if (parent == root) + LazyTreeItem *parent = nodeFromIndex(child)->parent(); + if (parent == root_.get()) return QModelIndex(); - ContextTreeItem *node = parent->parent(); + LazyTreeItem *node = parent->parent(); return createIndex(node->indexOf(parent), 0, parent); } @@ -298,7 +242,7 @@ QVariant ContextTreeModel::data(const QModelIndex &index, int role) const return QVariant(); if (role != Qt::DisplayRole) return QVariant(); - ContextTreeItem *node = nodeFromIndex(index); + LazyTreeItem *node = nodeFromIndex(index); return node->name(); } @@ -311,11 +255,11 @@ QVariant ContextTreeModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -ContextTreeItem *ContextTreeModel::nodeFromIndex(const QModelIndex &idx) const +LazyTreeItem *ContextTreeModel::nodeFromIndex(const QModelIndex &idx) const { if (idx.isValid()) - return (ContextTreeItem *)idx.internalPointer(); - return root; + return (LazyTreeItem *)idx.internalPointer(); + return root_.get(); } static int getElementIndex(ElementType type) @@ -333,25 +277,37 @@ static int getElementIndex(ElementType type) return -1; } -ContextTreeItem *ContextTreeModel::nodeForIdType(const ElementType type, const QString name) const +//ContextTreeItem *ContextTreeModel::nodeForIdType(const ElementType type, const QString name) const +//{ +// int index = getElementIndex(type); +// if (type != ElementType::NONE && nameToItem[index].contains(name)) +// return nameToItem[index].value(name); +// return nullptr; +//} + +//QModelIndex ContextTreeModel::indexFromNode(ContextTreeItem *node) +//{ +// ContextTreeItem *parent = node->parent(); +// if (parent == root) +// return QModelIndex(); +// return createIndex(parent->indexOf(node), 0, node); +//} + +Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const { - int index = getElementIndex(type); - if (type != ElementType::NONE && nameToItem[index].contains(name)) - return nameToItem[index].value(name); - return nullptr; + LazyTreeItem *node = nodeFromIndex(index); + return Qt::ItemIsEnabled | (node->type() != ElementType::NONE ? Qt::ItemIsSelectable : Qt::NoItemFlags); } -QModelIndex ContextTreeModel::indexFromNode(ContextTreeItem *node) + +void ContextTreeModel::fetchMore(const QModelIndex &parent) { - ContextTreeItem *parent = node->parent(); - if (parent == root) - return QModelIndex(); - return createIndex(parent->indexOf(node), 0, node); + nodeFromIndex(parent)->fetchMore(); } -Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const +bool ContextTreeModel::canFetchMore(const QModelIndex &parent) const { - ContextTreeItem *node = nodeFromIndex(index); - return Qt::ItemIsEnabled | (node->type() != ElementType::NONE ? Qt::ItemIsSelectable : Qt::NoItemFlags); + return nodeFromIndex(parent)->canFetchMore(); } + NEXTPNR_NAMESPACE_END diff --git a/gui/treemodel.h b/gui/treemodel.h index a85c290a..7de54db4 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -61,17 +61,212 @@ class ContextTreeItem QString itemName; }; +class LazyTreeItem +{ + protected: + QString name_; + LazyTreeItem *parent_; + QList children_; + + void addChild(LazyTreeItem *child) + { + children_.append(child); + } + + public: + LazyTreeItem(QString name, LazyTreeItem *parent) : + name_(name), parent_(parent) + { + // Register in parent if exists. + if (parent_ != nullptr) { + parent_->addChild(this); + } + }; + + int count() const + { + return children_.count(); + } + + QString name() const + { + return name_; + } + + LazyTreeItem *child(int index) + { + return children_.at(index); + } + + int indexOf(LazyTreeItem *child) const + { + return children_.indexOf(child, 0); + } + + LazyTreeItem *parent() + { + return parent_; + } + + virtual bool canFetchMore() const = 0; + virtual void fetchMore() = 0; + virtual ElementType type() const = 0; + virtual IdString id() const = 0; + + virtual ~LazyTreeItem() {} +}; + +class StaticTreeItem : public LazyTreeItem +{ + public: + using LazyTreeItem::LazyTreeItem; + + virtual bool canFetchMore() const override + { + return false; + } + + virtual void fetchMore() override + { + } + + virtual ~StaticTreeItem() {} + + virtual ElementType type() const override + { + return ElementType::NONE; + } + + virtual IdString id() const override + { + return IdString(); + } +}; + +template +class ElementList : public LazyTreeItem +{ + public: + using ElementMap = std::map, std::vector>; + using ElementGetter = std::function; + + private: + Context *ctx_; + const ElementMap *map_; + int x_, y_; + ElementGetter getter_; + std::vector> managed_; + + // scope valid until map gets mutated... + const std::vector *elements() const + { + return &map_->at(std::pair(x_, y_)); + } + + public: + ElementList(Context *ctx, QString name, LazyTreeItem *parent, ElementMap *map, int x, int y, ElementGetter getter) : + LazyTreeItem(name, parent), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter) + { + } + + virtual bool canFetchMore() const override + { + return children_.size() < elements()->size(); + } + + void fetchMore(int count) + { + 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_)); + + // 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); + managed_.push_back(std::move(std::unique_ptr(item))); + } + } + + virtual void fetchMore() override + { + fetchMore(100); + } + + virtual ElementType type() const override + { + return ElementType::NONE; + } + + virtual IdString id() const override + { + return IdString(); + } +}; + +template +class ElementXYRoot : public StaticTreeItem +{ + public: + using ElementMap = std::map, std::vector>; + using ElementGetter = std::function; + + + private: + Context *ctx_; + std::vector> bels_; + ElementMap map_; + ElementGetter getter_; + + public: + ElementXYRoot(Context *ctx, QString name, LazyTreeItem *parent, ElementMap map, ElementGetter getter) : + StaticTreeItem(name, parent), ctx_(ctx), map_(map), getter_(getter) + { + std::vector y_present; + + for (int i = 0; i < ctx->getGridDimX(); i++) { + y_present.clear(); + // first find all the elements in all Y coordinates in this X + for (int j = 0; j < ctx->getGridDimY(); j++) { + if (map_.count(std::pair(i, j)) == 0) + continue; + y_present.push_back(j); + } + // no bels in any X coordinate? do not add X tree item. + if (y_present.size() == 0) + continue; + + // create X item for tree + auto item = new StaticTreeItem(QString("X%1").arg(i), this); + bels_.push_back(std::move(std::unique_ptr(item))); + for (auto j : y_present) { + auto item2 = new ElementList(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_); + item2->fetchMore(1); + bels_.push_back(std::move(std::unique_ptr(item2))); + } + } + } +}; + class ContextTreeModel : public QAbstractItemModel { public: + using BelXYRoot = ElementXYRoot; + using WireXYRoot = ElementXYRoot; + using PipXYRoot = ElementXYRoot; + ContextTreeModel(QObject *parent = nullptr); ~ContextTreeModel(); void loadData(Context *ctx); void updateData(Context *ctx); - ContextTreeItem *nodeFromIndex(const QModelIndex &idx) const; - QModelIndex indexFromNode(ContextTreeItem *node); - ContextTreeItem *nodeForIdType(const ElementType type, const QString name) const; + LazyTreeItem *nodeFromIndex(const QModelIndex &idx) const; + //QModelIndex indexFromNode(ContextTreeItem *node); + //ContextTreeItem *nodeForIdType(const ElementType type, const QString name) const; + // Override QAbstractItemModel methods int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; @@ -80,12 +275,19 @@ class ContextTreeModel : public QAbstractItemModel QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + void fetchMore(const QModelIndex &parent) Q_DECL_OVERRIDE; + bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE; private: - ContextTreeItem *root; - QMap nameToItem[6]; - ContextTreeItem *nets_root; - ContextTreeItem *cells_root; + std::unique_ptr root_; + std::unique_ptr bel_root_; + std::unique_ptr wire_root_; + std::unique_ptr pip_root_; + //std::unique_ptr wires_root_; + //std::unique_ptr pips_root_; + //QMap nameToItem[6]; + //ContextTreeItem *nets_root; + //ContextTreeItem *cells_root; }; NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From c8cf0bbc0542635101d3b5d4e5553e0ddd4ee7c6 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 00:46:22 +0100 Subject: gui: make new tree model clickable --- gui/treemodel.cc | 12 ++++++------ gui/treemodel.h | 42 +++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index fd3ae45b..c3accd9a 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -24,7 +24,7 @@ NEXTPNR_NAMESPACE_BEGIN ContextTreeModel::ContextTreeModel(QObject *parent) : QAbstractItemModel(parent), - root_(new StaticTreeItem("Elements", nullptr)) {} + root_(new StaticTreeItem("Elements", nullptr, ElementType::NONE)) {} ContextTreeModel::~ContextTreeModel() {} @@ -47,7 +47,7 @@ void ContextTreeModel::loadContext(Context *ctx) belMap[std::pair(loc.x, loc.y)].push_back(bel); } auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); }; - bel_root_ = std::unique_ptr(new BelXYRoot(ctx, "Bels", root_.get(), belMap, belGetter)); + bel_root_ = std::unique_ptr(new BelXYRoot(ctx, "Bels", root_.get(), belMap, belGetter, ElementType::BEL)); std::map, std::vector> wireMap; for (int i = 0; i < ctx->chip_info->num_wires; i++) { @@ -57,7 +57,7 @@ void ContextTreeModel::loadContext(Context *ctx) wireMap[std::pair(wire->x, wire->y)].push_back(wireid); } auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; - wire_root_ = std::unique_ptr(new WireXYRoot(ctx, "Wires", root_.get(), wireMap, wireGetter)); + wire_root_ = std::unique_ptr(new WireXYRoot(ctx, "Wires", root_.get(), wireMap, wireGetter, ElementType::WIRE)); std::map, std::vector> pipMap; for (int i = 0; i < ctx->chip_info->num_pips; i++) { @@ -68,12 +68,12 @@ void ContextTreeModel::loadContext(Context *ctx) } printf("generating pip static tree...\n"); auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); }; - pip_root_ = std::unique_ptr(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter)); + pip_root_ = std::unique_ptr(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter, ElementType::PIP)); } #endif - cell_root_ = std::unique_ptr(new IdStringList(QString("Cells"), root_.get())); - net_root_ = std::unique_ptr(new IdStringList(QString("Nets"), root_.get())); + cell_root_ = std::unique_ptr(new IdStringList(QString("Cells"), root_.get(), ElementType::CELL)); + net_root_ = std::unique_ptr(new IdStringList(QString("Nets"), root_.get(), ElementType::NET)); endResetModel(); diff --git a/gui/treemodel.h b/gui/treemodel.h index f193468a..a6df8858 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -43,6 +43,7 @@ class LazyTreeItem QString name_; LazyTreeItem *parent_; QList children_; + ElementType type_; void addChild(LazyTreeItem *child) { @@ -50,8 +51,8 @@ class LazyTreeItem } public: - LazyTreeItem(QString name, LazyTreeItem *parent) : - name_(name), parent_(parent) + LazyTreeItem(QString name, LazyTreeItem *parent, ElementType type) : + name_(name), parent_(parent), type_(type) { // Register in parent if exists. if (parent_ != nullptr) { @@ -84,9 +85,13 @@ class LazyTreeItem return parent_; } + ElementType type() const + { + return type_; + } + virtual bool canFetchMore() const = 0; virtual void fetchMore() = 0; - virtual ElementType type() const = 0; virtual IdString id() const = 0; virtual ~LazyTreeItem() {} @@ -108,11 +113,6 @@ class StaticTreeItem : public LazyTreeItem virtual ~StaticTreeItem() {} - virtual ElementType type() const override - { - return ElementType::NONE; - } - virtual IdString id() const override { return IdString(); @@ -132,6 +132,7 @@ class ElementList : public LazyTreeItem int x_, y_; ElementGetter getter_; std::vector> managed_; + ElementType child_type_; // scope valid until map gets mutated... const std::vector *elements() const @@ -140,8 +141,8 @@ class ElementList : public LazyTreeItem } public: - ElementList(Context *ctx, QString name, LazyTreeItem *parent, ElementMap *map, int x, int y, ElementGetter getter) : - LazyTreeItem(name, parent), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter) + ElementList(Context *ctx, QString name, LazyTreeItem *parent, ElementMap *map, int x, int y, ElementGetter getter, ElementType type) : + LazyTreeItem(name, parent, ElementType::NONE), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter), child_type_(type) { } @@ -162,7 +163,7 @@ class ElementList : public LazyTreeItem if (name.startsWith(prefix)) name.remove(0, prefix.size()); - auto item = new StaticTreeItem(name, this); + auto item = new StaticTreeItem(name, this, child_type_); managed_.push_back(std::move(std::unique_ptr(item))); } } @@ -172,11 +173,6 @@ class ElementList : public LazyTreeItem fetchMore(100); } - virtual ElementType type() const override - { - return ElementType::NONE; - } - virtual IdString id() const override { return IdString(); @@ -187,7 +183,10 @@ class IdStringList : public StaticTreeItem { private: 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; void updateElements(Context *ctx, std::vector elements) @@ -198,7 +197,7 @@ 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); + auto item = new StaticTreeItem(elem.c_str(ctx), this, child_type_); managed_.emplace(elem, std::unique_ptr(item)); } } @@ -264,10 +263,11 @@ class ElementXYRoot : public StaticTreeItem std::vector> managed_; ElementMap map_; ElementGetter getter_; + ElementType child_type_; public: - ElementXYRoot(Context *ctx, QString name, LazyTreeItem *parent, ElementMap map, ElementGetter getter) : - StaticTreeItem(name, parent), ctx_(ctx), map_(map), getter_(getter) + ElementXYRoot(Context *ctx, QString name, LazyTreeItem *parent, ElementMap map, ElementGetter getter, ElementType type) : + StaticTreeItem(name, parent, ElementType::NONE), ctx_(ctx), map_(map), getter_(getter), child_type_(type) { std::vector y_present; @@ -284,10 +284,10 @@ class ElementXYRoot : public StaticTreeItem continue; // create X item for tree - auto item = new StaticTreeItem(QString("X%1").arg(i), this); + auto item = new StaticTreeItem(QString("X%1").arg(i), this, child_type_); managed_.push_back(std::move(std::unique_ptr(item))); for (auto j : y_present) { - auto item2 = new ElementList(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_); + auto item2 = new ElementList(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_, child_type_); item2->fetchMore(1); managed_.push_back(std::move(std::unique_ptr(item2))); } -- cgit v1.2.3 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 From bf43c27567c475cbd8f2c9c66295d492cfd565e8 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 01:59:58 +0100 Subject: gui: restore lookup into tree after new model --- gui/designwidget.cc | 27 +++++++++++------ gui/treemodel.h | 83 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index ad1362c8..0247d7c0 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -309,25 +309,34 @@ QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name) void DesignWidget::onClickedBel(BelId bel, bool keep) { - //LazyTreeItem *item = treeModel->nodeForIdType(ElementType::BEL, ctx->getBelName(bel).c_str(ctx)); - //selectionModel->setCurrentIndex(treeModel->indexFromNode(item), - // keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + auto item = treeModel->nodeForIdType(ElementType::BEL, ctx->getBelName(bel)); + if (!item) + return; + + selectionModel->setCurrentIndex(treeModel->indexFromNode(*item), + keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); Q_EMIT selected(getDecals(ElementType::BEL, ctx->getBelName(bel)), keep); } void DesignWidget::onClickedWire(WireId wire, bool keep) { - //LazyTreeItem *item = treeModel->nodeForIdType(ElementType::WIRE, ctx->getWireName(wire).c_str(ctx)); - //selectionModel->setCurrentIndex(treeModel->indexFromNode(item), - // keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + auto item = treeModel->nodeForIdType(ElementType::WIRE, ctx->getWireName(wire)); + if (!item) + return; + + selectionModel->setCurrentIndex(treeModel->indexFromNode(*item), + keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); Q_EMIT selected(getDecals(ElementType::WIRE, ctx->getWireName(wire)), keep); } void DesignWidget::onClickedPip(PipId pip, bool keep) { - //LazyTreeItem *item = treeModel->nodeForIdType(ElementType::PIP, ctx->getPipName(pip).c_str(ctx)); - //selectionModel->setCurrentIndex(treeModel->indexFromNode(item), - // keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); + auto item = treeModel->nodeForIdType(ElementType::PIP, ctx->getPipName(pip)); + if (!item) + return; + + selectionModel->setCurrentIndex(treeModel->indexFromNode(*item), + keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect); Q_EMIT selected(getDecals(ElementType::PIP, ctx->getPipName(pip)), keep); } diff --git a/gui/treemodel.h b/gui/treemodel.h index 69116112..37785f7a 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -21,8 +21,9 @@ #define TREEMODEL_H #include +#include + #include "nextpnr.h" -#include "log.h" NEXTPNR_NAMESPACE_BEGIN @@ -75,11 +76,22 @@ class LazyTreeItem return children_.at(index); } - int indexOf(LazyTreeItem *child) const + int indexOf(const LazyTreeItem *child) const + { + // Dropping the const for indexOf to work. + return children_.indexOf((LazyTreeItem *)child, 0); + } + + int indexOf(LazyTreeItem *child) { return children_.indexOf(child, 0); } + const LazyTreeItem *parent() const + { + return parent_; + } + LazyTreeItem *parent() { return parent_; @@ -146,7 +158,7 @@ class ElementList : public LazyTreeItem const ElementMap *map_; int x_, y_; ElementGetter getter_; - std::vector> managed_; + std::unordered_map> managed_; ElementType child_type_; // scope valid until map gets mutated... @@ -180,7 +192,7 @@ class ElementList : public LazyTreeItem name.remove(0, prefix.size()); auto item = new IdStringItem(ctx_, idstring, this, child_type_); - managed_.push_back(std::move(std::unique_ptr(item))); + managed_[idstring] = std::move(std::unique_ptr(item)); } } @@ -193,6 +205,18 @@ class ElementList : public LazyTreeItem { return IdString(); } + + boost::optional getById(IdString id) + { + // Search requires us to load all our elements... + while (canFetchMore()) fetchMore(); + + auto res = managed_.find(id); + if (res != managed_.end()) { + return res->second.get(); + } + return boost::none; + } }; class IdStringList : public StaticTreeItem @@ -232,6 +256,11 @@ class IdStringList : public StaticTreeItem return res; } + IdStringItem *getById(IdString id) const + { + return managed_.at(id).get(); + } + void updateElements(Context *ctx, std::vector elements) { // for any elements that are not yet in managed_, created them. @@ -310,7 +339,8 @@ class ElementXYRoot : public StaticTreeItem private: Context *ctx_; - std::vector> managed_; + std::vector> managed_labels_; + std::vector>> managed_lists_; ElementMap map_; ElementGetter getter_; ElementType child_type_; @@ -335,13 +365,26 @@ class ElementXYRoot : public StaticTreeItem // create X item for tree auto item = new StaticTreeItem(QString("X%1").arg(i), this, child_type_); - managed_.push_back(std::move(std::unique_ptr(item))); + managed_labels_.push_back(std::move(std::unique_ptr(item))); for (auto j : y_present) { auto item2 = new ElementList(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_, child_type_); item2->fetchMore(1); - managed_.push_back(std::move(std::unique_ptr(item2))); + managed_lists_.push_back(std::move(std::unique_ptr>(item2))); + } + } + } + + boost::optional getById(IdString id) + { + // For now, scan linearly all ElementLists. + // TODO(q3k) fix this once we have tree API from arch + for (auto &l : managed_lists_) { + auto res = l->getById(id); + if (res) { + return res; } } + return boost::none; } }; @@ -358,7 +401,33 @@ class ContextTreeModel : public QAbstractItemModel void loadContext(Context *ctx); void updateCellsNets(Context *ctx); LazyTreeItem *nodeFromIndex(const QModelIndex &idx) const; + QModelIndex indexFromNode(LazyTreeItem *node) + { + const LazyTreeItem *parent = node->parent(); + if (parent == nullptr) + return QModelIndex(); + + return createIndex(parent->indexOf(node), 0, node); + } + QList search(QString text); + boost::optional nodeForIdType(ElementType type, IdString id) const + { + switch (type) { + case ElementType::BEL: + return bel_root_->getById(id); + case ElementType::WIRE: + return wire_root_->getById(id); + case ElementType::PIP: + return pip_root_->getById(id); + case ElementType::CELL: + return cell_root_->getById(id); + case ElementType::NET: + return net_root_->getById(id); + default: + return boost::none; + } + } // Override QAbstractItemModel methods int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; -- cgit v1.2.3 From 1038b7ef23dbc1336212962be12994f98a68226a Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 02:08:56 +0100 Subject: gui: restore property navigation in tree --- gui/designwidget.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 0247d7c0..fe99a3c2 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -716,11 +716,11 @@ void DesignWidget::prepareMenuTree(const QPoint &pos) void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column) { - //QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property(); - //ElementType type = getElementTypeByName(selectedProperty->propertyId()); - //LazyTreeItem *it = treeModel->nodeForIdType(type, selectedProperty->valueText()); - //if (it) - // selectionModel->setCurrentIndex(treeModel->indexFromNode(it), QItemSelectionModel::ClearAndSelect); + QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property(); + ElementType type = getElementTypeByName(selectedProperty->propertyId()); + auto it = treeModel->nodeForIdType(type, ctx->id(selectedProperty->valueText().toStdString())); + if (it) + selectionModel->setCurrentIndex(treeModel->indexFromNode(*it), QItemSelectionModel::ClearAndSelect); } void DesignWidget::onDoubleClicked(const QModelIndex &index) { Q_EMIT zoomSelected(); } -- cgit v1.2.3 From c728cdcae0e73c3046628838dc318f1d92c65b79 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 02:11:23 +0100 Subject: gui: restore menu for new tree model --- gui/designwidget.cc | 97 +++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index fe99a3c2..d5c0c963 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -630,53 +630,56 @@ void DesignWidget::updateHighlightGroup(QList items, int group) void DesignWidget::prepareMenuProperty(const QPoint &pos) { - //QTreeWidget *tree = propertyEditor->treeWidget(); - //QList items; - //for (auto itemContextMenu : tree->selectedItems()) { - // QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); - // if (!browserItem) - // continue; - // QtProperty *selectedProperty = browserItem->property(); - // ElementType type = getElementTypeByName(selectedProperty->propertyId()); - // if (type == ElementType::NONE) - // continue; - // IdString value = ctx->id(selectedProperty->valueText().toStdString()); - // items.append(treeModel->nodeForIdType(type, value.c_str(ctx))); - //} - //int selectedIndex = -1; - //if (items.size() == 1) { - // LazyTreeItem *item = items.at(0); - // if (highlightSelected.contains(item)) - // selectedIndex = highlightSelected[item]; - //} - - //QMenu menu(this); - //QAction *selectAction = new QAction("&Select", this); - //connect(selectAction, &QAction::triggered, this, [this, items] { - // std::vector decals; - // for (auto clickItem : items) { - // std::vector d = getDecals(clickItem->type(), clickItem->id()); - // std::move(d.begin(), d.end(), std::back_inserter(decals)); - // } - // Q_EMIT selected(decals, false); - //}); - //menu.addAction(selectAction); - - //QMenu *subMenu = menu.addMenu("Highlight"); - //QActionGroup *group = new QActionGroup(this); - //group->setExclusive(true); - //for (int i = 0; i < 8; i++) { - // QPixmap pixmap(32, 32); - // pixmap.fill(QColor(highlightColors[i])); - // QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this); - // action->setCheckable(true); - // subMenu->addAction(action); - // group->addAction(action); - // if (selectedIndex == i) - // action->setChecked(true); - // connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); - //} - //menu.exec(tree->mapToGlobal(pos)); + QTreeWidget *tree = propertyEditor->treeWidget(); + QList items; + for (auto itemContextMenu : tree->selectedItems()) { + QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); + if (!browserItem) + continue; + QtProperty *selectedProperty = browserItem->property(); + ElementType type = getElementTypeByName(selectedProperty->propertyId()); + if (type == ElementType::NONE) + continue; + IdString value = ctx->id(selectedProperty->valueText().toStdString()); + auto node = treeModel->nodeForIdType(type, value); + if (!node) + continue; + items.append(*node); + } + int selectedIndex = -1; + if (items.size() == 1) { + LazyTreeItem *item = items.at(0); + if (highlightSelected.contains(item)) + selectedIndex = highlightSelected[item]; + } + + QMenu menu(this); + QAction *selectAction = new QAction("&Select", this); + connect(selectAction, &QAction::triggered, this, [this, items] { + std::vector decals; + for (auto clickItem : items) { + std::vector d = getDecals(clickItem->type(), clickItem->id()); + std::move(d.begin(), d.end(), std::back_inserter(decals)); + } + Q_EMIT selected(decals, false); + }); + menu.addAction(selectAction); + + QMenu *subMenu = menu.addMenu("Highlight"); + QActionGroup *group = new QActionGroup(this); + group->setExclusive(true); + for (int i = 0; i < 8; i++) { + QPixmap pixmap(32, 32); + pixmap.fill(QColor(highlightColors[i])); + QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this); + action->setCheckable(true); + subMenu->addAction(action); + group->addAction(action); + if (selectedIndex == i) + action->setChecked(true); + connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); }); + } + menu.exec(tree->mapToGlobal(pos)); } void DesignWidget::prepareMenuTree(const QPoint &pos) -- cgit v1.2.3 From 049e487f3fd8eef78b974bc8c3f40da70b276f5c Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 02:14:20 +0100 Subject: gui: fix warnings --- gui/treemodel.cc | 15 --------------- gui/treemodel.h | 10 +++++----- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index c3accd9a..e3d9b4ae 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -150,21 +150,6 @@ LazyTreeItem *ContextTreeModel::nodeFromIndex(const QModelIndex &idx) const return root_.get(); } -static int getElementIndex(ElementType type) -{ - if (type == ElementType::BEL) - return 0; - if (type == ElementType::WIRE) - return 1; - if (type == ElementType::PIP) - return 2; - if (type == ElementType::NET) - return 3; - if (type == ElementType::CELL) - return 4; - return -1; -} - Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const { LazyTreeItem *node = nodeFromIndex(index); diff --git a/gui/treemodel.h b/gui/treemodel.h index 37785f7a..620b4e21 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -175,14 +175,14 @@ class ElementList : public LazyTreeItem virtual bool canFetchMore() const override { - return children_.size() < elements()->size(); + return (size_t)children_.size() < elements()->size(); } void fetchMore(int count) { - int start = children_.size(); - size_t end = std::min(start + count, (int)elements()->size()); - for (int i = start; i < end; i++) { + size_t start = children_.size(); + size_t end = std::min(start + count, elements()->size()); + for (size_t i = start; i < end; i++) { auto idstring = getter_(ctx_, elements()->at(i)); QString name(idstring.c_str(ctx_)); @@ -293,7 +293,7 @@ class IdStringList : public StaticTreeItem return parts_a.size() < parts_b.size(); } - for (int i = 0; i < parts_a.size(); i++) { + for (size_t i = 0; i < parts_a.size(); i++) { auto &part_a = parts_a.at(i); auto &part_b = parts_b.at(i); -- cgit v1.2.3 From 6baf8216ed1c6bd730a130a4aa4ad489ad70b2a7 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 02:19:30 +0100 Subject: gui: move tree model into own namespace --- gui/designwidget.cc | 22 +++++++++--------- gui/designwidget.h | 6 ++--- gui/treemodel.cc | 46 ++++++++++++++++++++----------------- gui/treemodel.h | 66 ++++++++++++++++++++++++++++------------------------- 4 files changed, 74 insertions(+), 66 deletions(-) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index d5c0c963..c75991eb 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -34,7 +34,7 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), sel { // Add tree view treeView = new QTreeView(); - treeModel = new ContextTreeModel(); + treeModel = new TreeModel::Model(); treeView->setModel(treeModel); treeView->setContextMenuPolicy(Qt::CustomContextMenu); treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -223,9 +223,9 @@ void DesignWidget::updateTree() { clearProperties(); - QMap::iterator i = highlightSelected.begin(); + QMap::iterator i = highlightSelected.begin(); while (i != highlightSelected.end()) { - QMap::iterator prev = i; + QMap::iterator prev = i; ++i; if (prev.key()->type() == ElementType::NET && ctx->nets.find(prev.key()->id()) == ctx->nets.end()) { highlightSelected.erase(prev); @@ -348,7 +348,7 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti if (selectionModel->selectedIndexes().size() > 1) { std::vector decals; for (auto index : selectionModel->selectedIndexes()) { - LazyTreeItem *item = treeModel->nodeFromIndex(index); + TreeModel::Item *item = treeModel->nodeFromIndex(index); std::vector d = getDecals(item->type(), item->id()); std::move(d.begin(), d.end(), std::back_inserter(decals)); } @@ -358,7 +358,7 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti QModelIndex index = selectionModel->selectedIndexes().at(0); if (!index.isValid()) return; - LazyTreeItem *clickItem = treeModel->nodeFromIndex(index); + TreeModel::Item *clickItem = treeModel->nodeFromIndex(index); ElementType type = clickItem->type(); if (type == ElementType::NONE) @@ -606,7 +606,7 @@ std::vector DesignWidget::getDecals(ElementType type, IdString value) return decals; } -void DesignWidget::updateHighlightGroup(QList items, int group) +void DesignWidget::updateHighlightGroup(QList items, int group) { const bool shouldClear = items.size() == 1; for (auto item : items) { @@ -631,7 +631,7 @@ void DesignWidget::updateHighlightGroup(QList items, int group) void DesignWidget::prepareMenuProperty(const QPoint &pos) { QTreeWidget *tree = propertyEditor->treeWidget(); - QList items; + QList items; for (auto itemContextMenu : tree->selectedItems()) { QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu); if (!browserItem) @@ -648,7 +648,7 @@ void DesignWidget::prepareMenuProperty(const QPoint &pos) } int selectedIndex = -1; if (items.size() == 1) { - LazyTreeItem *item = items.at(0); + TreeModel::Item *item = items.at(0); if (highlightSelected.contains(item)) selectedIndex = highlightSelected[item]; } @@ -689,13 +689,13 @@ void DesignWidget::prepareMenuTree(const QPoint &pos) if (selectionModel->selectedIndexes().size() == 0) return; - QList items; + QList items; for (auto index : selectionModel->selectedIndexes()) { - LazyTreeItem *item = treeModel->nodeFromIndex(index); + TreeModel::Item *item = treeModel->nodeFromIndex(index); items.append(item); } if (items.size() == 1) { - LazyTreeItem *item = items.at(0); + TreeModel::Item *item = items.at(0); if (highlightSelected.contains(item)) selectedIndex = highlightSelected[item]; } diff --git a/gui/designwidget.h b/gui/designwidget.h index d6af83a0..628586f4 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -51,7 +51,7 @@ class DesignWidget : public QWidget void updateButtons(); void addToHistory(QModelIndex item); std::vector getDecals(ElementType type, IdString value); - void updateHighlightGroup(QList item, int group); + void updateHighlightGroup(QList item, int group); Q_SIGNALS: void info(std::string text); void selected(std::vector decal, bool keep); @@ -77,7 +77,7 @@ class DesignWidget : public QWidget QTreeView *treeView; QItemSelectionModel *selectionModel; - ContextTreeModel *treeModel; + TreeModel::Model *treeModel; QLineEdit *searchEdit; QtVariantPropertyManager *variantManager; QtVariantPropertyManager *readOnlyManager; @@ -99,7 +99,7 @@ class DesignWidget : public QWidget QAction *actionClear; QColor highlightColors[8]; - QMap highlightSelected; + QMap highlightSelected; QString currentSearch; QList currentSearchIndexes; diff --git a/gui/treemodel.cc b/gui/treemodel.cc index e3d9b4ae..ac256104 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -22,13 +22,15 @@ NEXTPNR_NAMESPACE_BEGIN -ContextTreeModel::ContextTreeModel(QObject *parent) : +namespace TreeModel { + +Model::Model(QObject *parent) : QAbstractItemModel(parent), root_(new StaticTreeItem("Elements", nullptr, ElementType::NONE)) {} -ContextTreeModel::~ContextTreeModel() {} +Model::~Model() {} -void ContextTreeModel::loadContext(Context *ctx) +void Model::loadContext(Context *ctx) { if (!ctx) return; @@ -80,7 +82,7 @@ void ContextTreeModel::loadContext(Context *ctx) updateCellsNets(ctx); } -void ContextTreeModel::updateCellsNets(Context *ctx) +void Model::updateCellsNets(Context *ctx) { if (!ctx) return; @@ -102,39 +104,39 @@ void ContextTreeModel::updateCellsNets(Context *ctx) endResetModel(); } -int ContextTreeModel::rowCount(const QModelIndex &parent) const { return nodeFromIndex(parent)->count(); } +int Model::rowCount(const QModelIndex &parent) const { return nodeFromIndex(parent)->count(); } -int ContextTreeModel::columnCount(const QModelIndex &parent) const { return 1; } +int Model::columnCount(const QModelIndex &parent) const { return 1; } -QModelIndex ContextTreeModel::index(int row, int column, const QModelIndex &parent) const +QModelIndex Model::index(int row, int column, const QModelIndex &parent) const { - LazyTreeItem *node = nodeFromIndex(parent); + Item *node = nodeFromIndex(parent); if (row >= node->count()) return QModelIndex(); return createIndex(row, column, node->child(row)); } -QModelIndex ContextTreeModel::parent(const QModelIndex &child) const +QModelIndex Model::parent(const QModelIndex &child) const { - LazyTreeItem *parent = nodeFromIndex(child)->parent(); + Item *parent = nodeFromIndex(child)->parent(); if (parent == root_.get()) return QModelIndex(); - LazyTreeItem *node = parent->parent(); + Item *node = parent->parent(); return createIndex(node->indexOf(parent), 0, parent); } -QVariant ContextTreeModel::data(const QModelIndex &index, int role) const +QVariant Model::data(const QModelIndex &index, int role) const { if (index.column() != 0) return QVariant(); if (role != Qt::DisplayRole) return QVariant(); - LazyTreeItem *node = nodeFromIndex(index); + Item *node = nodeFromIndex(index); return node->name(); } -QVariant ContextTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section); if (orientation == Qt::Horizontal && role == Qt::DisplayRole) @@ -143,31 +145,31 @@ QVariant ContextTreeModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -LazyTreeItem *ContextTreeModel::nodeFromIndex(const QModelIndex &idx) const +Item *Model::nodeFromIndex(const QModelIndex &idx) const { if (idx.isValid()) - return (LazyTreeItem *)idx.internalPointer(); + return (Item *)idx.internalPointer(); return root_.get(); } -Qt::ItemFlags ContextTreeModel::flags(const QModelIndex &index) const +Qt::ItemFlags Model::flags(const QModelIndex &index) const { - LazyTreeItem *node = nodeFromIndex(index); + Item *node = nodeFromIndex(index); return Qt::ItemIsEnabled | (node->type() != ElementType::NONE ? Qt::ItemIsSelectable : Qt::NoItemFlags); } -void ContextTreeModel::fetchMore(const QModelIndex &parent) +void Model::fetchMore(const QModelIndex &parent) { nodeFromIndex(parent)->fetchMore(); } -bool ContextTreeModel::canFetchMore(const QModelIndex &parent) const +bool Model::canFetchMore(const QModelIndex &parent) const { return nodeFromIndex(parent)->canFetchMore(); } -QList ContextTreeModel::search(QString text) +QList Model::search(QString text) { QList list; //for (int i = 0; i < 6; i++) { @@ -182,4 +184,6 @@ QList ContextTreeModel::search(QString text) return list; } +}; // namespace TreeModel + NEXTPNR_NAMESPACE_END diff --git a/gui/treemodel.h b/gui/treemodel.h index 620b4e21..8793d8e5 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -38,21 +38,23 @@ enum class ElementType GROUP }; -class LazyTreeItem +namespace TreeModel { + +class Item { protected: QString name_; - LazyTreeItem *parent_; - QList children_; + Item *parent_; + QList children_; ElementType type_; - void addChild(LazyTreeItem *child) + void addChild(Item *child) { children_.append(child); } public: - LazyTreeItem(QString name, LazyTreeItem *parent, ElementType type) : + Item(QString name, Item *parent, ElementType type) : name_(name), parent_(parent), type_(type) { // Register in parent if exists. @@ -71,28 +73,28 @@ class LazyTreeItem return name_; } - LazyTreeItem *child(int index) + Item *child(int index) { return children_.at(index); } - int indexOf(const LazyTreeItem *child) const + int indexOf(const Item *child) const { // Dropping the const for indexOf to work. - return children_.indexOf((LazyTreeItem *)child, 0); + return children_.indexOf((Item *)child, 0); } - int indexOf(LazyTreeItem *child) + int indexOf(Item *child) { return children_.indexOf(child, 0); } - const LazyTreeItem *parent() const + const Item *parent() const { return parent_; } - LazyTreeItem *parent() + Item *parent() { return parent_; } @@ -106,13 +108,13 @@ class LazyTreeItem virtual void fetchMore() = 0; virtual IdString id() const = 0; - virtual ~LazyTreeItem() {} + virtual ~Item() {} }; -class StaticTreeItem : public LazyTreeItem +class StaticTreeItem : public Item { public: - using LazyTreeItem::LazyTreeItem; + using Item::Item; virtual bool canFetchMore() const override { @@ -137,7 +139,7 @@ class IdStringItem : public StaticTreeItem IdString id_; public: - IdStringItem(Context *ctx, IdString str, LazyTreeItem *parent, ElementType type) : + IdStringItem(Context *ctx, IdString str, Item *parent, ElementType type) : StaticTreeItem(QString(str.c_str(ctx)), parent, type), id_(str) {} virtual IdString id() const override @@ -147,7 +149,7 @@ class IdStringItem : public StaticTreeItem }; template -class ElementList : public LazyTreeItem +class ElementList : public Item { public: using ElementMap = std::map, std::vector>; @@ -168,8 +170,8 @@ class ElementList : public LazyTreeItem } public: - ElementList(Context *ctx, QString name, LazyTreeItem *parent, ElementMap *map, int x, int y, ElementGetter getter, ElementType type) : - LazyTreeItem(name, parent, ElementType::NONE), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter), child_type_(type) + ElementList(Context *ctx, QString name, Item *parent, ElementMap *map, int x, int y, ElementGetter getter, ElementType type) : + Item(name, parent, ElementType::NONE), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter), child_type_(type) { } @@ -206,7 +208,7 @@ class ElementList : public LazyTreeItem return IdString(); } - boost::optional getById(IdString id) + boost::optional getById(IdString id) { // Search requires us to load all our elements... while (canFetchMore()) fetchMore(); @@ -225,7 +227,7 @@ class IdStringList : public StaticTreeItem std::unordered_map> managed_; ElementType child_type_; public: - IdStringList(QString name, LazyTreeItem *parent, ElementType type) : + IdStringList(QString name, Item *parent, ElementType type) : StaticTreeItem(name, parent, ElementType::NONE), child_type_(type) {} using StaticTreeItem::StaticTreeItem; @@ -285,7 +287,7 @@ class IdStringList : public StaticTreeItem } // sort new children - qSort(children_.begin(), children_.end(), [&](const LazyTreeItem *a, const LazyTreeItem *b){ + qSort(children_.begin(), children_.end(), [&](const Item *a, const Item *b){ auto parts_a = alphaNumSplit(a->name()); auto parts_b = alphaNumSplit(b->name()); @@ -346,7 +348,7 @@ class ElementXYRoot : public StaticTreeItem ElementType child_type_; public: - ElementXYRoot(Context *ctx, QString name, LazyTreeItem *parent, ElementMap map, ElementGetter getter, ElementType type) : + ElementXYRoot(Context *ctx, QString name, Item *parent, ElementMap map, ElementGetter getter, ElementType type) : StaticTreeItem(name, parent, ElementType::NONE), ctx_(ctx), map_(map), getter_(getter), child_type_(type) { std::vector y_present; @@ -374,7 +376,7 @@ class ElementXYRoot : public StaticTreeItem } } - boost::optional getById(IdString id) + boost::optional getById(IdString id) { // For now, scan linearly all ElementLists. // TODO(q3k) fix this once we have tree API from arch @@ -388,22 +390,22 @@ class ElementXYRoot : public StaticTreeItem } }; -class ContextTreeModel : public QAbstractItemModel +class Model : public QAbstractItemModel { public: using BelXYRoot = ElementXYRoot; using WireXYRoot = ElementXYRoot; using PipXYRoot = ElementXYRoot; - ContextTreeModel(QObject *parent = nullptr); - ~ContextTreeModel(); + Model(QObject *parent = nullptr); + ~Model(); void loadContext(Context *ctx); void updateCellsNets(Context *ctx); - LazyTreeItem *nodeFromIndex(const QModelIndex &idx) const; - QModelIndex indexFromNode(LazyTreeItem *node) + Item *nodeFromIndex(const QModelIndex &idx) const; + QModelIndex indexFromNode(Item *node) { - const LazyTreeItem *parent = node->parent(); + const Item *parent = node->parent(); if (parent == nullptr) return QModelIndex(); @@ -411,7 +413,7 @@ class ContextTreeModel : public QAbstractItemModel } QList search(QString text); - boost::optional nodeForIdType(ElementType type, IdString id) const + boost::optional nodeForIdType(ElementType type, IdString id) const { switch (type) { case ElementType::BEL: @@ -441,7 +443,7 @@ class ContextTreeModel : public QAbstractItemModel bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE; private: - std::unique_ptr root_; + std::unique_ptr root_; std::unique_ptr bel_root_; std::unique_ptr wire_root_; std::unique_ptr pip_root_; @@ -449,6 +451,8 @@ class ContextTreeModel : public QAbstractItemModel std::unique_ptr net_root_; }; +}; // namespace TreeModel + NEXTPNR_NAMESPACE_END #endif // TREEMODEL_H -- cgit v1.2.3 From 8e5c6557d6e5983c3e27eedab4bd1d176a64de49 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 02:24:57 +0100 Subject: gui: treemodel: simplify class hierarchy --- gui/treemodel.cc | 2 +- gui/treemodel.h | 48 +++++++++++++++++------------------------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index ac256104..d46d7b6b 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -26,7 +26,7 @@ namespace TreeModel { Model::Model(QObject *parent) : QAbstractItemModel(parent), - root_(new StaticTreeItem("Elements", nullptr, ElementType::NONE)) {} + root_(new Item("Elements", nullptr, ElementType::NONE)) {} Model::~Model() {} diff --git a/gui/treemodel.h b/gui/treemodel.h index 8793d8e5..ff4e7254 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -104,43 +104,29 @@ class Item return type_; } - virtual bool canFetchMore() const = 0; - virtual void fetchMore() = 0; - virtual IdString id() const = 0; - - virtual ~Item() {} -}; - -class StaticTreeItem : public Item -{ - public: - using Item::Item; - - virtual bool canFetchMore() const override + virtual bool canFetchMore() const { return false; } - virtual void fetchMore() override - { - } - - virtual ~StaticTreeItem() {} + virtual void fetchMore() {} - virtual IdString id() const override + virtual IdString id() const { return IdString(); } + + ~Item() {} }; -class IdStringItem : public StaticTreeItem +class IdStringItem : public Item { private: IdString id_; public: IdStringItem(Context *ctx, IdString str, Item *parent, ElementType type) : - StaticTreeItem(QString(str.c_str(ctx)), parent, type), id_(str) {} + Item(QString(str.c_str(ctx)), parent, type), id_(str) {} virtual IdString id() const override { @@ -160,7 +146,7 @@ class ElementList : public Item const ElementMap *map_; int x_, y_; ElementGetter getter_; - std::unordered_map> managed_; + std::unordered_map> managed_; ElementType child_type_; // scope valid until map gets mutated... @@ -194,7 +180,7 @@ class ElementList : public Item name.remove(0, prefix.size()); auto item = new IdStringItem(ctx_, idstring, this, child_type_); - managed_[idstring] = std::move(std::unique_ptr(item)); + managed_[idstring] = std::move(std::unique_ptr(item)); } } @@ -221,15 +207,15 @@ class ElementList : public Item } }; -class IdStringList : public StaticTreeItem +class IdStringList : public Item { private: std::unordered_map> managed_; ElementType child_type_; public: IdStringList(QString name, Item *parent, ElementType type) : - StaticTreeItem(name, parent, ElementType::NONE), child_type_(type) {} - using StaticTreeItem::StaticTreeItem; + Item(name, parent, ElementType::NONE), child_type_(type) {} + using Item::Item; static std::vector alphaNumSplit(const QString &str) { @@ -332,7 +318,7 @@ class IdStringList : public StaticTreeItem }; template -class ElementXYRoot : public StaticTreeItem +class ElementXYRoot : public Item { public: using ElementMap = std::map, std::vector>; @@ -341,7 +327,7 @@ class ElementXYRoot : public StaticTreeItem private: Context *ctx_; - std::vector> managed_labels_; + std::vector> managed_labels_; std::vector>> managed_lists_; ElementMap map_; ElementGetter getter_; @@ -349,7 +335,7 @@ class ElementXYRoot : public StaticTreeItem public: ElementXYRoot(Context *ctx, QString name, Item *parent, ElementMap map, ElementGetter getter, ElementType type) : - StaticTreeItem(name, parent, ElementType::NONE), ctx_(ctx), map_(map), getter_(getter), child_type_(type) + Item(name, parent, ElementType::NONE), ctx_(ctx), map_(map), getter_(getter), child_type_(type) { std::vector y_present; @@ -366,8 +352,8 @@ class ElementXYRoot : public StaticTreeItem continue; // create X item for tree - auto item = new StaticTreeItem(QString("X%1").arg(i), this, child_type_); - managed_labels_.push_back(std::move(std::unique_ptr(item))); + auto item = new Item(QString("X%1").arg(i), this, child_type_); + managed_labels_.push_back(std::move(std::unique_ptr(item))); for (auto j : y_present) { auto item2 = new ElementList(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_, child_type_); item2->fetchMore(1); -- cgit v1.2.3 From a117fcdefd3ce22af04906a447b0f21d403489fa Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 02:59:07 +0100 Subject: gui: treemodel: cleanups --- gui/treemodel.cc | 103 +++++++++++++++++++- gui/treemodel.h | 282 +++++++++++++++++++++++-------------------------------- 2 files changed, 217 insertions(+), 168 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index d46d7b6b..eaf62928 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Miodrag Milanovic + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,9 +25,109 @@ NEXTPNR_NAMESPACE_BEGIN namespace TreeModel { + +// converts 'aa123bb432' -> ['aa', '123', 'bb', '432'] +std::vector IdStringList::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 IdStringList::updateElements(Context *ctx, std::vector elements) +{ + // For any elements that are not yet in managed_, created them. + std::unordered_set element_set; + for (auto elem : elements) { + element_set.insert(elem); + auto existing = managed_.find(elem); + if (existing == managed_.end()) { + auto item = new IdStringItem(ctx, elem, this, child_type_); + managed_.emplace(elem, std::unique_ptr(item)); + } + } + + children_.clear(); + // For any elements that are in managed_ but not in new, delete them. + for (auto &pair : managed_) { + if (element_set.count(pair.first) != 0) { + children_.push_back(pair.second.get()); + continue; + } + managed_.erase(pair.first); + } + + // Sort new children + qSort(children_.begin(), children_.end(), [&](const Item *a, const Item *b){ + auto parts_a = alphaNumSplit(a->name()); + auto parts_b = alphaNumSplit(b->name()); + + // Short-circuit for different part count. + if (parts_a.size() != parts_b.size()) { + return parts_a.size() < parts_b.size(); + } + + for (size_t 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 both parts are numbers, compare numerically. + // If they're equal, continue to next part. + if (a_is_number && b_is_number) { + if (a_number != b_number) { + return a_number < b_number; + } else { + continue; + } + } + + // For different alpha/nonalpha types, make numeric parts appear + // first. + if (a_is_number != b_is_number) { + return a_is_number; + } + + // If both parts are numbers, compare lexically. + // If they're equal, continue to next part. + if (part_a == part_b) { + continue; + } + return part_a < part_b; + } + + // Same string. + return true; + }); +} + + Model::Model(QObject *parent) : QAbstractItemModel(parent), - root_(new Item("Elements", nullptr, ElementType::NONE)) {} + root_(new Item("Elements", nullptr)) {} Model::~Model() {} diff --git a/gui/treemodel.h b/gui/treemodel.h index ff4e7254..4f708768 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Miodrag Milanovic + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,13 +41,24 @@ enum class ElementType namespace TreeModel { +// Item is a leaf or non-leaf item in the TreeModel hierarchy. It does not +// manage any memory. +// It has a list of children, and when created it registers itself as a child +// of its parent. +// It has some PNR-specific members, like type (if any), idstring (if ay). +// They should be overwritten by deriving classes to make them relate to an +// object somewhere in the arch universe. +// It also has provisions for lazy loading of data, via the canFetchMore and +// fetchMore methods. class Item { protected: + // Human-friendly name of this item. QString name_; + // Parent or nullptr if root. Item *parent_; + // Children that are loaded into memory. QList children_; - ElementType type_; void addChild(Item *child) { @@ -54,8 +66,8 @@ class Item } public: - Item(QString name, Item *parent, ElementType type) : - name_(name), parent_(parent), type_(type) + Item(QString name, Item *parent) : + name_(name), parent_(parent) { // Register in parent if exists. if (parent_ != nullptr) { @@ -63,93 +75,121 @@ class Item } }; - int count() const - { - return children_.count(); - } + // Number of children. + int count() const { return children_.count(); } - QString name() const - { - return name_; - } + // Name getter. + QString name() const { return name_; } - Item *child(int index) - { - return children_.at(index); - } + // Child getter. + Item *child(int index) { return children_.at(index); } + // Parent getter. + const Item *parent() const { return parent_; } + Item *parent() { return parent_; } + + // indexOf gets index of child in children array. int indexOf(const Item *child) const { // Dropping the const for indexOf to work. return children_.indexOf((Item *)child, 0); } + int indexOf(Item *child) { return children_.indexOf(child, 0); } - int indexOf(Item *child) - { - return children_.indexOf(child, 0); - } - - const Item *parent() const - { - return parent_; - } - - Item *parent() - { - return parent_; - } - - ElementType type() const - { - return type_; - } - - virtual bool canFetchMore() const - { - return false; - } + // Arch id and type that correspond to this element. + virtual IdString id() const { return IdString(); } + virtual ElementType type() const { return ElementType::NONE; } + // Lazy loading methods. + virtual bool canFetchMore() const { return false; } virtual void fetchMore() {} - virtual IdString id() const - { - return IdString(); - } - ~Item() {} }; +// IdString is an Item that corresponds to a real element in Arch. class IdStringItem : public Item { private: IdString id_; + ElementType type_; public: IdStringItem(Context *ctx, IdString str, Item *parent, ElementType type) : - Item(QString(str.c_str(ctx)), parent, type), id_(str) {} + Item(QString(str.c_str(ctx)), parent), id_(str), type_(type) {} virtual IdString id() const override { return id_; } + + virtual ElementType type() const override + { + return type_; + } }; +// IdString list is a static list of IdStrings which can be set/updates from +// a vector of IdStrings. It will render each IdStrings as a child, with the +// list sorted in a smart way. +class IdStringList : public Item +{ + private: + // Children that we manage the memory for, stored for quick lookup from + // IdString to child. + std::unordered_map> managed_; + // Type of children that the list creates. + ElementType child_type_; + + public: + // Create an IdStringList at given partent that will contain elements of + // the given type. + IdStringList(QString name, Item *parent, ElementType type) : + Item(name, parent), child_type_(type) {} + + // Split a name into alpha/non-alpha parts, which is then used for sorting + // of children. + static std::vector alphaNumSplit(const QString &str); + + // getById finds a child for the given IdString. + IdStringItem *getById(IdString id) const + { + return managed_.at(id).get(); + } + + // (Re-)create children from a list of IdStrings. + void updateElements(Context *ctx, std::vector elements); +}; + + +// ElementList is a dynamic list of ElementT (BelId,WireId,...) that are +// automatically generated based on an overall map of elements. +// ElementList is emitted from ElementXYRoot, and contains the actual +// Bels/Wires/Pips underneath it. template class ElementList : public Item { public: + // A map from tile (X,Y) to list of ElementTs in that tile. using ElementMap = std::map, std::vector>; + // A method that converts an ElementT to an IdString. using ElementGetter = std::function; private: Context *ctx_; + // ElementMap given to use by our constructor. const ElementMap *map_; + // The X, Y that this list handles. int x_, y_; ElementGetter getter_; + // Children that we manage the memory for, stored for quick lookup from + // IdString to child. std::unordered_map> managed_; + // Type of children that he list creates. ElementType child_type_; - // scope valid until map gets mutated... + // Gets elements that this list should create from the map. This pointer is + // short-lived (as it will change when the map mutates. const std::vector *elements() const { return &map_->at(std::pair(x_, y_)); @@ -157,10 +197,12 @@ class ElementList : public Item public: ElementList(Context *ctx, QString name, Item *parent, ElementMap *map, int x, int y, ElementGetter getter, ElementType type) : - Item(name, parent, ElementType::NONE), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter), child_type_(type) + Item(name, parent), ctx_(ctx), map_(map), x_(x), y_(y), getter_(getter), child_type_(type) { } + // Lazy loading of elements. + virtual bool canFetchMore() const override { return (size_t)children_.size() < elements()->size(); @@ -189,11 +231,7 @@ class ElementList : public Item fetchMore(100); } - virtual IdString id() const override - { - return IdString(); - } - + // getById finds a child for the given IdString. boost::optional getById(IdString id) { // Search requires us to load all our elements... @@ -207,161 +245,69 @@ class ElementList : public Item } }; -class IdStringList : public Item -{ - private: - std::unordered_map> managed_; - ElementType child_type_; - public: - IdStringList(QString name, Item *parent, ElementType type) : - Item(name, parent, ElementType::NONE), child_type_(type) {} - using Item::Item; - - 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; - } - - IdStringItem *getById(IdString id) const - { - return managed_.at(id).get(); - } - - void updateElements(Context *ctx, std::vector elements) - { - // for any elements that are not yet in managed_, created them. - std::unordered_set element_set; - for (auto elem : elements) { - element_set.insert(elem); - auto existing = managed_.find(elem); - if (existing == managed_.end()) { - auto item = new IdStringItem(ctx, elem, this, child_type_); - managed_.emplace(elem, std::unique_ptr(item)); - } - } - - children_.clear(); - // for any elements that are in managed_ but not in new, delete them. - for (auto &pair : managed_) { - if (element_set.count(pair.first) != 0) { - children_.push_back(pair.second.get()); - continue; - } - managed_.erase(pair.first); - } - - // sort new children - qSort(children_.begin(), children_.end(), [&](const Item *a, const Item *b){ - auto parts_a = alphaNumSplit(a->name()); - auto parts_b = alphaNumSplit(b->name()); - - if (parts_a.size() != parts_b.size()) { - return parts_a.size() < parts_b.size(); - } - - for (size_t 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; - } - - // both equal - return true; - }); - } -}; - +// ElementXYRoot is the root of an ElementT multi-level lazy loading list. +// It can take any of {BelId,WireId,PipId} and create a tree that +// hierarchizes them by X and Y tile positions, when given a map from X,Y to +// list of ElementTs in that tile. template class ElementXYRoot : public Item { public: + // A map from tile (X,Y) to list of ElementTs in that tile. using ElementMap = std::map, std::vector>; + // A method that converts an ElementT to an IdString. using ElementGetter = std::function; private: Context *ctx_; + // X-index children that we manage the memory for. std::vector> managed_labels_; + // Y-index children (ElementLists) that we manage the memory for. std::vector>> managed_lists_; + // Source of truth for elements to display. ElementMap map_; ElementGetter getter_; + // Type of children that he list creates in X->Y->... ElementType child_type_; public: ElementXYRoot(Context *ctx, QString name, Item *parent, ElementMap map, ElementGetter getter, ElementType type) : - Item(name, parent, ElementType::NONE), ctx_(ctx), map_(map), getter_(getter), child_type_(type) + Item(name, parent), ctx_(ctx), map_(map), getter_(getter), child_type_(type) { + // Create all X and Y label Items/ElementLists. + + // Y coordinates at which an element exists for a given X - taken out + // of loop to limit heap allocation/deallocation. std::vector y_present; for (int i = 0; i < ctx->getGridDimX(); i++) { y_present.clear(); - // first find all the elements in all Y coordinates in this X + // First find all the elements in all Y coordinates in this X. for (int j = 0; j < ctx->getGridDimY(); j++) { if (map_.count(std::pair(i, j)) == 0) continue; y_present.push_back(j); } - // no bels in any X coordinate? do not add X tree item. + // No elements in any X coordinate? Do not add X tree item. if (y_present.size() == 0) continue; - // create X item for tree - auto item = new Item(QString("X%1").arg(i), this, child_type_); + // Create X list Item. + auto item = new Item(QString("X%1").arg(i), this); managed_labels_.push_back(std::move(std::unique_ptr(item))); + for (auto j : y_present) { + // Create Y list ElementList. auto item2 = new ElementList(ctx_, QString("Y%1").arg(j), item, &map_, i, j, getter_, child_type_); + // Pre-populate list with one element, other Qt will never ask for more. item2->fetchMore(1); managed_lists_.push_back(std::move(std::unique_ptr>(item2))); } } } + // getById finds a child for the given IdString. boost::optional getById(IdString id) { // For now, scan linearly all ElementLists. @@ -429,6 +375,8 @@ class Model : public QAbstractItemModel bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE; private: + + // Tree elements that we manage the memory for. std::unique_ptr root_; std::unique_ptr bel_root_; std::unique_ptr wire_root_; -- cgit v1.2.3 From 900649ce7a337dd7d3dcadf0d323302f09339325 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 03:11:22 +0100 Subject: gui: restore search --- gui/treemodel.cc | 36 +++++++++++++++++++++++++----------- gui/treemodel.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index eaf62928..4fc3d4f5 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -124,6 +124,17 @@ void IdStringList::updateElements(Context *ctx, std::vector elements) }); } +void IdStringList::search(QList &results, QString text, int limit) +{ + for (const auto &child : children_) { + if (limit != -1 && results.size() > limit) + return; + + if (child->name().contains(text)) + results.push_back(child); + } +} + Model::Model(QObject *parent) : QAbstractItemModel(parent), @@ -272,17 +283,20 @@ bool Model::canFetchMore(const QModelIndex &parent) const QList Model::search(QString text) { - QList list; - //for (int i = 0; i < 6; i++) { - // for (auto key : nameToItem[i].keys()) { - // if (key.contains(text, Qt::CaseInsensitive)) { - // list.append(indexFromNode(nameToItem[i].value(key))); - // if (list.count() > 500) - // break; // limit to 500 results - // } - // } - //} - return list; + const int limit = 500; + + QList list; + cell_root_->search(list, text, limit); + net_root_->search(list, text, limit); + bel_root_->search(list, text, limit); + wire_root_->search(list, text, limit); + pip_root_->search(list, text, limit); + + QList res; + for (auto i : list) { + res.push_back(indexFromNode(i)); + } + return res; } }; // namespace TreeModel diff --git a/gui/treemodel.h b/gui/treemodel.h index 4f708768..61dc339d 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -159,6 +159,9 @@ class IdStringList : public Item // (Re-)create children from a list of IdStrings. void updateElements(Context *ctx, std::vector elements); + + // Find children that contain the given text. + void search(QList &results, QString text, int limit); }; @@ -243,6 +246,24 @@ class ElementList : public Item } return boost::none; } + + // Find children that contain the given text. + void search(QList &results, QString text, int limit) + { + // Last chance to bail out from loading entire tree into memory. + if (limit != -1 && results.size() > limit) + return; + + // Search requires us to load all our elements... + while (canFetchMore()) fetchMore(); + + for (const auto &child : children_) { + if (limit != -1 && results.size() > limit) + return; + if (child->name().contains(text)) + results.push_back(child); + } + } }; // ElementXYRoot is the root of an ElementT multi-level lazy loading list. @@ -320,6 +341,16 @@ class ElementXYRoot : public Item } return boost::none; } + + // Find children that contain the given text. + void search(QList &results, QString text, int limit) + { + for (auto &l : managed_lists_) { + if (limit != -1 && results.size() > limit) + return; + l->search(results, text, limit); + } + } }; class Model : public QAbstractItemModel @@ -345,6 +376,7 @@ class Model : public QAbstractItemModel } QList search(QString text); + boost::optional nodeForIdType(ElementType type, IdString id) const { switch (type) { -- cgit v1.2.3 From 9fb9eab6c9699df5cd86ca03563e0fa871defb83 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 03:17:02 +0100 Subject: gui: cosmetics --- gui/treemodel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/treemodel.h b/gui/treemodel.h index 61dc339d..15370658 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -195,7 +195,7 @@ class ElementList : public Item // short-lived (as it will change when the map mutates. const std::vector *elements() const { - return &map_->at(std::pair(x_, y_)); + return &map_->at(std::make_pair(x_, y_)); } public: @@ -306,7 +306,7 @@ class ElementXYRoot : public Item y_present.clear(); // First find all the elements in all Y coordinates in this X. for (int j = 0; j < ctx->getGridDimY(); j++) { - if (map_.count(std::pair(i, j)) == 0) + if (map_.count(std::make_pair(i, j)) == 0) continue; y_present.push_back(j); } -- cgit v1.2.3 From f9d30bcdea72e4860361d8ab350282703dc3bfcf Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 03:26:27 +0100 Subject: gui: lock arch when accessing/building treemodel --- gui/designwidget.cc | 15 +++++++++++++-- gui/treemodel.cc | 8 +++++++- gui/treemodel.h | 3 +++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index c75991eb..34e358ae 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -215,7 +215,11 @@ void DesignWidget::newContext(Context *ctx) highlightSelected.clear(); this->ctx = ctx; - treeModel->loadContext(ctx); + { + std::lock_guard lock_ui(ctx->ui_mutex); + std::lock_guard lock(ctx->mutex); + treeModel->loadContext(ctx); + } updateTree(); } @@ -235,7 +239,11 @@ void DesignWidget::updateTree() } } - treeModel->updateCellsNets(ctx); + { + std::lock_guard lock_ui(ctx->ui_mutex); + std::lock_guard lock(ctx->mutex); + treeModel->updateCellsNets(ctx); + } } QtProperty *DesignWidget::addTopLevelProperty(const QString &id) { @@ -735,6 +743,9 @@ void DesignWidget::onSearchInserted() if (currentIndex >= currentSearchIndexes.size()) currentIndex = 0; } else { + std::lock_guard lock_ui(ctx->ui_mutex); + std::lock_guard lock(ctx->mutex); + currentSearch = searchEdit->text(); currentSearchIndexes = treeModel->search(searchEdit->text()); currentIndex = 0; diff --git a/gui/treemodel.cc b/gui/treemodel.cc index 4fc3d4f5..bf7d81a3 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -146,6 +146,7 @@ void Model::loadContext(Context *ctx) { if (!ctx) return; + ctx_ = ctx; beginResetModel(); @@ -273,6 +274,12 @@ Qt::ItemFlags Model::flags(const QModelIndex &index) const void Model::fetchMore(const QModelIndex &parent) { + if (ctx_ == nullptr) + return; + + std::lock_guard lock_ui(ctx_->ui_mutex); + std::lock_guard lock(ctx_->mutex); + nodeFromIndex(parent)->fetchMore(); } @@ -284,7 +291,6 @@ bool Model::canFetchMore(const QModelIndex &parent) const QList Model::search(QString text) { const int limit = 500; - QList list; cell_root_->search(list, text, limit); net_root_->search(list, text, limit); diff --git a/gui/treemodel.h b/gui/treemodel.h index 15370658..1d25dde4 100644 --- a/gui/treemodel.h +++ b/gui/treemodel.h @@ -355,6 +355,9 @@ class ElementXYRoot : public Item class Model : public QAbstractItemModel { + private: + Context *ctx_ = nullptr; + public: using BelXYRoot = ElementXYRoot; using WireXYRoot = ElementXYRoot; -- cgit v1.2.3 From e423cceba81288ce1d9a8ed1ea2df2dd7660cf3f Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 03:31:06 +0100 Subject: gui: treemodel: bail early on empty children updates in IdStringList --- gui/treemodel.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index bf7d81a3..35221231 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -56,6 +56,8 @@ std::vector IdStringList::alphaNumSplit(const QString &str) void IdStringList::updateElements(Context *ctx, std::vector elements) { + bool changed = false; + // For any elements that are not yet in managed_, created them. std::unordered_set element_set; for (auto elem : elements) { @@ -64,17 +66,29 @@ void IdStringList::updateElements(Context *ctx, std::vector elements) if (existing == managed_.end()) { auto item = new IdStringItem(ctx, elem, this, child_type_); managed_.emplace(elem, std::unique_ptr(item)); + changed = true; } } - children_.clear(); // For any elements that are in managed_ but not in new, delete them. for (auto &pair : managed_) { if (element_set.count(pair.first) != 0) { - children_.push_back(pair.second.get()); continue; } managed_.erase(pair.first); + changed = true; + } + + // Return early if there are no changes. + if (!changed) + return; + + // Rebuild children list. + children_.clear(); + for (auto &pair : managed_) { + if (element_set.count(pair.first) != 0) { + children_.push_back(pair.second.get()); + } } // Sort new children -- cgit v1.2.3 From 1f787c37a3f6d96f30c06f936674943cc5de7893 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 03:55:28 +0100 Subject: gui: fix zoom outbound not happening with fast startup time --- gui/fpgaviewwidget.cc | 16 ++++++++++------ gui/fpgaviewwidget.h | 2 -- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index ed25a187..66739b28 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -323,14 +323,18 @@ void FPGAViewWidget::paintGL() flags = rendererData_->flags; } - { - QMutexLocker locker(&rendererArgsLock_); - rendererArgs_->flags.clear(); - } - // Check flags passed through pipeline. if (flags.zoomOutbound) { - zoomOutbound(); + // If we're doing init zoomOutbound, make sure we're actually drawing + // something already. + if (rendererData_->gfxByStyle[GraphicElement::STYLE_FRAME].vertices.size() != 0) { + zoomOutbound(); + flags.zoomOutbound = false; + { + QMutexLocker lock(&rendererArgsLock_); + rendererArgs_->flags.zoomOutbound = false; + } + } } } diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index c35821d9..a40a0153 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -251,8 +251,6 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions zoomOutbound = other.zoomOutbound; return *this; } - - void clear() { zoomOutbound = false; } }; struct RendererArgs -- cgit v1.2.3 From 3408e427f9f70c7138b993d76b77ab6c9f2feef4 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Wed, 1 Aug 2018 03:55:47 +0100 Subject: gui: cosmetics --- gui/treemodel.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/treemodel.cc b/gui/treemodel.cc index 35221231..755e46ec 100644 --- a/gui/treemodel.cc +++ b/gui/treemodel.cc @@ -195,7 +195,6 @@ void Model::loadContext(Context *ctx) pipid.index = i; pipMap[std::pair(pip->x, pip->y)].push_back(pipid); } - printf("generating pip static tree...\n"); auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); }; pip_root_ = std::unique_ptr(new PipXYRoot(ctx, "Pips", root_.get(), pipMap, pipGetter, ElementType::PIP)); } -- cgit v1.2.3