diff options
Diffstat (limited to 'gui')
32 files changed, 1213 insertions, 494 deletions
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 68fd0229..2e8e367e 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -12,7 +12,6 @@ if (BUILD_PYTHON) ../3rdparty/python-console/modified/pyredirector.cc ../3rdparty/python-console/modified/pyinterpreter.cc - ../3rdparty/python-console/modified/pyconsole.cc ) endif() @@ -26,6 +25,7 @@ qt5_add_resources(GUI_RESOURCE_FILES ${_RESOURCES}) set(GUI_LIBRARY_FILES_${ufamily} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} QtPropertyBrowser PARENT_SCOPE) add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${PYTHON_CONSOLE_SRC} ${GUI_RESOURCE_FILES}) +include(${family}/family.cmake) target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src) if (BUILD_PYTHON) diff --git a/gui/base.qrc b/gui/base.qrc index b9e2f237..bf21986b 100644 --- a/gui/base.qrc +++ b/gui/base.qrc @@ -4,5 +4,11 @@ <file>resources/open.png</file> <file>resources/save.png</file> <file>resources/exit.png</file> + <file>resources/zoom.png</file> + <file>resources/resultset_first.png</file> + <file>resources/resultset_previous.png</file> + <file>resources/resultset_next.png</file> + <file>resources/resultset_last.png</file> + <file>resources/splash.png</file> </qresource> </RCC> diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 2463027a..07b71105 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -18,6 +18,7 @@ */
#include <QAction>
+#include <QCoreApplication>
#include <QFileDialog>
#include <QGridLayout>
#include <QIcon>
@@ -27,16 +28,14 @@ #include "jsonparse.h"
#include "log.h"
#include "mainwindow.h"
-
-#ifndef NO_PYTHON
#include "pythontab.h"
-#endif
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
NEXTPNR_NAMESPACE_BEGIN
-BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullptr)
+BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent)
+ : QMainWindow(parent), ctx(std::move(context))
{
initBasenameResource();
qRegisterMetaType<std::string>();
@@ -44,7 +43,7 @@ BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullp log_files.clear();
log_streams.clear();
- setObjectName(QStringLiteral("BaseMainWindow"));
+ setObjectName("BaseMainWindow");
resize(1024, 768);
createMenusAndBars();
@@ -63,70 +62,80 @@ BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent), ctx(nullp setCentralWidget(centralWidget);
- DesignWidget *designview = new DesignWidget();
+ designview = new DesignWidget();
designview->setMinimumWidth(300);
- designview->setMaximumWidth(300);
splitter_h->addWidget(designview);
- connect(this, SIGNAL(contextChanged(Context *)), designview, SLOT(newContext(Context *)));
- connect(this, SIGNAL(updateTreeView()), designview, SLOT(updateTree()));
-
- connect(designview, SIGNAL(info(std::string)), this, SLOT(writeInfo(std::string)));
-
tabWidget = new QTabWidget();
-#ifndef NO_PYTHON
- PythonTab *pythontab = new PythonTab();
- tabWidget->addTab(pythontab, "Python");
- connect(this, SIGNAL(contextChanged(Context *)), pythontab, SLOT(newContext(Context *)));
-#endif
- info = new InfoTab();
- tabWidget->addTab(info, "Info");
+
+ console = new PythonTab();
+ tabWidget->addTab(console, "Console");
+ connect(this, SIGNAL(contextChanged(Context *)), console, SLOT(newContext(Context *)));
centralTabWidget = new QTabWidget();
FPGAViewWidget *fpgaView = new FPGAViewWidget();
centralTabWidget->addTab(fpgaView, "Graphics");
connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *)));
+ connect(designview, SIGNAL(selected(std::vector<DecalXY>)), fpgaView,
+ SLOT(onSelectedArchItem(std::vector<DecalXY>)));
+
+ connect(designview, SIGNAL(highlight(std::vector<DecalXY>, int)), fpgaView,
+ SLOT(onHighlightGroupChanged(std::vector<DecalXY>, int)));
+
+ connect(this, SIGNAL(contextChanged(Context *)), designview, SLOT(newContext(Context *)));
+ connect(this, SIGNAL(updateTreeView()), designview, SLOT(updateTree()));
+
+ connect(designview, SIGNAL(info(std::string)), this, SLOT(writeInfo(std::string)));
splitter_v->addWidget(centralTabWidget);
splitter_v->addWidget(tabWidget);
+ displaySplash();
}
BaseMainWindow::~BaseMainWindow() {}
-void BaseMainWindow::writeInfo(std::string text) { info->info(text); }
+void BaseMainWindow::displaySplash()
+{
+ splash = new QSplashScreen();
+ splash->setPixmap(QPixmap(":/icons/resources/splash.png"));
+ splash->show();
+ connect(designview, SIGNAL(finishContextLoad()), splash, SLOT(close()));
+ connect(designview, SIGNAL(contextLoadStatus(std::string)), this, SLOT(displaySplashMessage(std::string)));
+ QCoreApplication::instance()->processEvents();
+}
+
+void BaseMainWindow::displaySplashMessage(std::string msg)
+{
+ splash->showMessage(msg.c_str(), Qt::AlignCenter | Qt::AlignBottom, Qt::white);
+ QCoreApplication::instance()->processEvents();
+}
+
+void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars()
{
actionNew = new QAction("New", this);
- QIcon iconNew;
- iconNew.addFile(QStringLiteral(":/icons/resources/new.png"));
- actionNew->setIcon(iconNew);
+ actionNew->setIcon(QIcon(":/icons/resources/new.png"));
actionNew->setShortcuts(QKeySequence::New);
actionNew->setStatusTip("New project file");
connect(actionNew, SIGNAL(triggered()), this, SLOT(new_proj()));
actionOpen = new QAction("Open", this);
- QIcon iconOpen;
- iconOpen.addFile(QStringLiteral(":/icons/resources/open.png"));
- actionOpen->setIcon(iconOpen);
+ actionOpen->setIcon(QIcon(":/icons/resources/open.png"));
actionOpen->setShortcuts(QKeySequence::Open);
actionOpen->setStatusTip("Open an existing project file");
connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj()));
QAction *actionSave = new QAction("Save", this);
- QIcon iconSave;
- iconSave.addFile(QStringLiteral(":/icons/resources/save.png"));
- actionSave->setIcon(iconSave);
+ actionSave->setIcon(QIcon(":/icons/resources/save.png"));
actionSave->setShortcuts(QKeySequence::Save);
actionSave->setStatusTip("Save existing project to disk");
- connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj()));
actionSave->setEnabled(false);
+ connect(actionSave, SIGNAL(triggered()), this, SLOT(save_proj()));
QAction *actionExit = new QAction("Exit", this);
- QIcon iconExit;
- iconExit.addFile(QStringLiteral(":/icons/resources/exit.png"));
- actionExit->setIcon(iconExit);
+ actionExit->setIcon(QIcon(":/icons/resources/exit.png"));
actionExit->setShortcuts(QKeySequence::Quit);
actionExit->setStatusTip("Exit the application");
connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
@@ -145,6 +154,12 @@ void BaseMainWindow::createMenusAndBars() addToolBar(Qt::TopToolBarArea, mainToolBar);
statusBar = new QStatusBar();
+ progressBar = new QProgressBar(statusBar);
+ progressBar->setAlignment(Qt::AlignRight);
+ progressBar->setMaximumSize(180, 19);
+ statusBar->addPermanentWidget(progressBar);
+ progressBar->setValue(0);
+ progressBar->setEnabled(false);
setStatusBar(statusBar);
menu_File->addAction(actionNew);
diff --git a/gui/basewindow.h b/gui/basewindow.h index 5c06fa6e..18b5339e 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -20,34 +20,41 @@ #ifndef BASEMAINWINDOW_H
#define BASEMAINWINDOW_H
-#include "infotab.h"
#include "nextpnr.h"
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
+#include <QProgressBar>
+#include <QSplashScreen>
#include <QStatusBar>
#include <QTabWidget>
#include <QToolBar>
Q_DECLARE_METATYPE(std::string)
+Q_DECLARE_METATYPE(NEXTPNR_NAMESPACE_PREFIX DecalXY)
NEXTPNR_NAMESPACE_BEGIN
+class PythonTab;
+class DesignWidget;
+
class BaseMainWindow : public QMainWindow
{
Q_OBJECT
public:
- explicit BaseMainWindow(QWidget *parent = 0);
+ explicit BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
virtual ~BaseMainWindow();
- Context *getContext() { return ctx; }
+ Context *getContext() { return ctx.get(); }
protected:
void createMenusAndBars();
+ void displaySplash();
protected Q_SLOTS:
void writeInfo(std::string text);
+ void displaySplashMessage(std::string msg);
virtual void new_proj() = 0;
virtual void open_proj() = 0;
@@ -58,16 +65,19 @@ class BaseMainWindow : public QMainWindow void updateTreeView();
protected:
- Context *ctx;
+ std::unique_ptr<Context> ctx;
QTabWidget *tabWidget;
QTabWidget *centralTabWidget;
- InfoTab *info;
+ PythonTab *console;
QMenuBar *menuBar;
QToolBar *mainToolBar;
QStatusBar *statusBar;
QAction *actionNew;
QAction *actionOpen;
+ QProgressBar *progressBar;
+ QSplashScreen *splash;
+ DesignWidget *designview;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 4922074b..335ed929 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -20,23 +20,15 @@ #include "designwidget.h"
#include <QAction>
#include <QGridLayout>
+#include <QLineEdit>
#include <QMenu>
#include <QSplitter>
+#include <QToolBar>
#include <QTreeWidgetItem>
#include "fpgaviewwidget.h"
NEXTPNR_NAMESPACE_BEGIN
-enum class ElementType
-{
- NONE,
- BEL,
- WIRE,
- PIP,
- NET,
- CELL
-};
-
class ElementTreeItem : public QTreeWidgetItem
{
public:
@@ -85,12 +77,88 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net propertyEditor = new QtTreePropertyBrowser(this);
propertyEditor->setFactoryForManager(variantManager, variantFactory);
propertyEditor->setPropertiesWithoutValueMarked(true);
-
propertyEditor->show();
+ propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ QLineEdit *lineEdit = new QLineEdit();
+ lineEdit->setClearButtonEnabled(true);
+ lineEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
+ lineEdit->setPlaceholderText("Search...");
+
+ actionFirst = new QAction("", this);
+ actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
+ actionFirst->setEnabled(false);
+ connect(actionFirst, &QAction::triggered, this, [this] {
+ history_ignore = true;
+ history_index = 0;
+ treeWidget->setCurrentItem(history.at(history_index));
+ updateButtons();
+ });
+
+ actionPrev = new QAction("", this);
+ actionPrev->setIcon(QIcon(":/icons/resources/resultset_previous.png"));
+ actionPrev->setEnabled(false);
+ connect(actionPrev, &QAction::triggered, this, [this] {
+ history_ignore = true;
+ history_index--;
+ treeWidget->setCurrentItem(history.at(history_index));
+ updateButtons();
+ });
+
+ actionNext = new QAction("", this);
+ actionNext->setIcon(QIcon(":/icons/resources/resultset_next.png"));
+ actionNext->setEnabled(false);
+ connect(actionNext, &QAction::triggered, this, [this] {
+ history_ignore = true;
+ history_index++;
+ treeWidget->setCurrentItem(history.at(history_index));
+ updateButtons();
+ });
+
+ actionLast = new QAction("", this);
+ actionLast->setIcon(QIcon(":/icons/resources/resultset_last.png"));
+ actionLast->setEnabled(false);
+ connect(actionLast, &QAction::triggered, this, [this] {
+ history_ignore = true;
+ history_index = int(history.size() - 1);
+ treeWidget->setCurrentItem(history.at(history_index));
+ updateButtons();
+ });
+
+ QToolBar *toolbar = new QToolBar();
+ toolbar->addAction(actionFirst);
+ toolbar->addAction(actionPrev);
+ toolbar->addAction(actionNext);
+ toolbar->addAction(actionLast);
+
+ QWidget *topWidget = new QWidget();
+ QVBoxLayout *vbox1 = new QVBoxLayout();
+ topWidget->setLayout(vbox1);
+ vbox1->setSpacing(5);
+ vbox1->setContentsMargins(0, 0, 0, 0);
+ vbox1->addWidget(lineEdit);
+ vbox1->addWidget(treeWidget);
+
+ QWidget *toolbarWidget = new QWidget();
+ QHBoxLayout *hbox = new QHBoxLayout;
+ hbox->setAlignment(Qt::AlignCenter);
+ toolbarWidget->setLayout(hbox);
+ hbox->setSpacing(0);
+ hbox->setContentsMargins(0, 0, 0, 0);
+ hbox->addWidget(toolbar);
+
+ QWidget *btmWidget = new QWidget();
+
+ QVBoxLayout *vbox2 = new QVBoxLayout();
+ btmWidget->setLayout(vbox2);
+ vbox2->setSpacing(0);
+ vbox2->setContentsMargins(0, 0, 0, 0);
+ vbox2->addWidget(toolbarWidget);
+ vbox2->addWidget(propertyEditor);
QSplitter *splitter = new QSplitter(Qt::Vertical);
- splitter->addWidget(treeWidget);
- splitter->addWidget(propertyEditor);
+ splitter->addWidget(topWidget);
+ splitter->addWidget(btmWidget);
QGridLayout *mainLayout = new QGridLayout();
mainLayout->setSpacing(0);
@@ -99,16 +167,61 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net setLayout(mainLayout);
// Connection
- connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenu);
-
- connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), SLOT(onItemClicked(QTreeWidgetItem *, int)));
+ connect(propertyEditor->treeWidget(), &QTreeWidget::customContextMenuRequested, this,
+ &DesignWidget::prepareMenuProperty);
+ connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked);
+
+ connect(treeWidget, SIGNAL(itemSelectionChanged()), SLOT(onItemSelectionChanged()));
+ connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenuTree);
+
+ history_index = -1;
+ history_ignore = false;
+
+ highlightColors[0] = QColor("#6495ed");
+ highlightColors[1] = QColor("#7fffd4");
+ highlightColors[2] = QColor("#98fb98");
+ highlightColors[3] = QColor("#ffd700");
+ highlightColors[4] = QColor("#cd5c5c");
+ highlightColors[5] = QColor("#fa8072");
+ highlightColors[6] = QColor("#ff69b4");
+ highlightColors[7] = QColor("#da70d6");
}
DesignWidget::~DesignWidget() {}
+void DesignWidget::updateButtons()
+{
+ int count = int(history.size());
+ actionFirst->setEnabled(history_index > 0);
+ actionPrev->setEnabled(history_index > 0);
+ actionNext->setEnabled(history_index < (count - 1));
+ actionLast->setEnabled(history_index < (count - 1));
+}
+
+void DesignWidget::addToHistory(QTreeWidgetItem *item)
+{
+ if (!history_ignore) {
+ int count = int(history.size());
+ for (int i = count - 1; i > history_index; i--)
+ history.pop_back();
+ history.push_back(item);
+ history_index++;
+ }
+ history_ignore = false;
+ updateButtons();
+}
+
void DesignWidget::newContext(Context *ctx)
{
treeWidget->clear();
+ history_ignore = false;
+ history_index = -1;
+ history.clear();
+ updateButtons();
+
+ for (int i = 0; i < 6; i++)
+ nameToItem[i].clear();
+
this->ctx = ctx;
// Add bels to tree
@@ -117,6 +230,7 @@ void DesignWidget::newContext(Context *ctx) bel_root->setText(0, "Bels");
treeWidget->insertTopLevelItem(0, bel_root);
if (ctx) {
+ Q_EMIT contextLoadStatus("Configuring bels...");
for (auto bel : ctx->getBels()) {
auto id = ctx->getBelName(bel);
QStringList items = QString(id.c_str(ctx)).split("/");
@@ -128,7 +242,7 @@ void DesignWidget::newContext(Context *ctx) name += items.at(i);
if (!bel_items.contains(name)) {
if (i == items.size() - 1)
- bel_items.insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent));
+ nameToItem[0].insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent));
else
bel_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
}
@@ -139,6 +253,9 @@ void DesignWidget::newContext(Context *ctx) for (auto bel : bel_items.toStdMap()) {
bel_root->addChild(bel.second);
}
+ for (auto bel : nameToItem[0].toStdMap()) {
+ bel_root->addChild(bel.second);
+ }
// Add wires to tree
QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget);
@@ -146,6 +263,7 @@ void DesignWidget::newContext(Context *ctx) wire_root->setText(0, "Wires");
treeWidget->insertTopLevelItem(0, wire_root);
if (ctx) {
+ Q_EMIT contextLoadStatus("Configuring wires...");
for (auto wire : ctx->getWires()) {
auto id = ctx->getWireName(wire);
QStringList items = QString(id.c_str(ctx)).split("/");
@@ -157,7 +275,7 @@ void DesignWidget::newContext(Context *ctx) name += items.at(i);
if (!wire_items.contains(name)) {
if (i == items.size() - 1)
- wire_items.insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent));
+ nameToItem[1].insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent));
else
wire_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
}
@@ -168,13 +286,16 @@ void DesignWidget::newContext(Context *ctx) for (auto wire : wire_items.toStdMap()) {
wire_root->addChild(wire.second);
}
-
+ for (auto wire : nameToItem[1].toStdMap()) {
+ wire_root->addChild(wire.second);
+ }
// Add pips to tree
QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget);
QMap<QString, QTreeWidgetItem *> pip_items;
pip_root->setText(0, "Pips");
treeWidget->insertTopLevelItem(0, pip_root);
if (ctx) {
+ Q_EMIT contextLoadStatus("Configuring pips...");
for (auto pip : ctx->getPips()) {
auto id = ctx->getPipName(pip);
QStringList items = QString(id.c_str(ctx)).split("/");
@@ -186,7 +307,7 @@ void DesignWidget::newContext(Context *ctx) name += items.at(i);
if (!pip_items.contains(name)) {
if (i == items.size() - 1)
- pip_items.insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent));
+ nameToItem[2].insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent));
else
pip_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
}
@@ -197,6 +318,9 @@ void DesignWidget::newContext(Context *ctx) for (auto pip : pip_items.toStdMap()) {
pip_root->addChild(pip.second);
}
+ for (auto pip : nameToItem[2].toStdMap()) {
+ pip_root->addChild(pip.second);
+ }
// Add nets to tree
nets_root = new QTreeWidgetItem(treeWidget);
@@ -207,6 +331,8 @@ void DesignWidget::newContext(Context *ctx) cells_root = new QTreeWidgetItem(treeWidget);
cells_root->setText(0, "Cells");
treeWidget->insertTopLevelItem(0, cells_root);
+
+ Q_EMIT finishContextLoad();
}
void DesignWidget::updateTree()
@@ -214,45 +340,48 @@ void DesignWidget::updateTree() clearProperties();
delete nets_root;
delete cells_root;
+ nameToItem[3].clear();
+ nameToItem[4].clear();
// Add nets to tree
nets_root = new QTreeWidgetItem(treeWidget);
- QMap<QString, QTreeWidgetItem *> nets_items;
nets_root->setText(0, "Nets");
treeWidget->insertTopLevelItem(0, nets_root);
if (ctx) {
for (auto &item : ctx->nets) {
auto id = item.first;
QString name = QString(id.c_str(ctx));
- nets_items.insert(name, new IdStringTreeItem(id, ElementType::NET, name, nullptr));
+ IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr);
+ nameToItem[3].insert(name, newItem);
}
}
- for (auto item : nets_items.toStdMap()) {
+ for (auto item : nameToItem[3].toStdMap()) {
nets_root->addChild(item.second);
}
// Add cells to tree
cells_root = new QTreeWidgetItem(treeWidget);
- QMap<QString, QTreeWidgetItem *> cells_items;
cells_root->setText(0, "Cells");
treeWidget->insertTopLevelItem(0, cells_root);
if (ctx) {
for (auto &item : ctx->cells) {
auto id = item.first;
QString name = QString(id.c_str(ctx));
- cells_items.insert(name, new IdStringTreeItem(id, ElementType::CELL, name, nullptr));
+ IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr);
+ nameToItem[4].insert(name, newItem);
}
}
- for (auto item : cells_items.toStdMap()) {
+ for (auto item : nameToItem[4].toStdMap()) {
cells_root->addChild(item.second);
}
}
-
-void DesignWidget::addProperty(QtProperty *property, const QString &id)
+QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
{
- propertyToId[property] = id;
- idToProperty[id] = property;
- propertyEditor->addProperty(property);
+ QtProperty *topItem = groupManager->addProperty(id);
+ propertyToId[topItem] = id;
+ idToProperty[id] = topItem;
+ propertyEditor->addProperty(topItem);
+ return topItem;
}
void DesignWidget::clearProperties()
@@ -266,8 +395,75 @@ void DesignWidget::clearProperties() idToProperty.clear();
}
-void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos)
+QString DesignWidget::getElementTypeName(ElementType type)
+{
+ if (type == ElementType::NONE)
+ return "";
+ if (type == ElementType::BEL)
+ return "BEL";
+ if (type == ElementType::WIRE)
+ return "WIRE";
+ if (type == ElementType::PIP)
+ return "PIP";
+ if (type == ElementType::NET)
+ return "NET";
+ if (type == ElementType::CELL)
+ return "CELL";
+ return "";
+}
+int DesignWidget::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;
+}
+
+ElementType DesignWidget::getElementTypeByName(QString type)
{
+ if (type == "BEL")
+ return ElementType::BEL;
+ if (type == "WIRE")
+ return ElementType::WIRE;
+ if (type == "PIP")
+ return ElementType::PIP;
+ if (type == "NET")
+ return ElementType::NET;
+ if (type == "CELL")
+ return ElementType::CELL;
+ return ElementType::NONE;
+}
+
+void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value,
+ const ElementType &type)
+{
+ QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name);
+ item->setValue(value);
+ item->setPropertyId(getElementTypeName(type));
+ topItem->addSubProperty(item);
+}
+
+QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
+{
+ QtProperty *item = groupManager->addProperty(name);
+ topItem->addSubProperty(item);
+ return item;
+}
+
+void DesignWidget::onItemSelectionChanged()
+{
+ if (treeWidget->selectedItems().size() == 0)
+ return;
+
+ QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
+
if (!clickItem->parent())
return;
@@ -276,305 +472,193 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos) return;
}
+ std::vector<DecalXY> decals;
+
+ addToHistory(clickItem);
+
clearProperties();
if (type == ElementType::BEL) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
BelId bel = ctx->getBelByName(c);
- QtProperty *topItem = groupManager->addProperty("Bel");
- addProperty(topItem, "Bel");
+ decals.push_back(ctx->getBelDecal(bel));
+ Q_EMIT selected(decals);
- QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- nameItem->setValue(c.c_str(ctx));
- topItem->addSubProperty(nameItem);
+ QtProperty *topItem = addTopLevelProperty("Bel");
- QtVariantProperty *typeItem = readOnlyManager->addProperty(QVariant::String, "Type");
- typeItem->setValue(ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx));
- topItem->addSubProperty(typeItem);
-
- QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available");
- availItem->setValue(ctx->checkBelAvail(bel));
- topItem->addSubProperty(availItem);
-
- QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Cell");
- cellItem->setValue(ctx->getBoundBelCell(bel).c_str(ctx));
- topItem->addSubProperty(cellItem);
-
- QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Cell");
- conflictItem->setValue(ctx->getConflictingBelCell(bel).c_str(ctx));
- topItem->addSubProperty(conflictItem);
+ addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
+ addProperty(topItem, QVariant::String, "Type", ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx));
+ addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
+ addProperty(topItem, QVariant::String, "Bound Cell", ctx->getBoundBelCell(bel).c_str(ctx), ElementType::CELL);
+ addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->getConflictingBelCell(bel).c_str(ctx),
+ ElementType::CELL);
} else if (type == ElementType::WIRE) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
WireId wire = ctx->getWireByName(c);
- QtProperty *topItem = groupManager->addProperty("Wire");
- addProperty(topItem, "Wire");
-
- QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- nameItem->setValue(c.c_str(ctx));
- topItem->addSubProperty(nameItem);
+ decals.push_back(ctx->getWireDecal(wire));
+ Q_EMIT selected(decals);
- QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available");
- availItem->setValue(ctx->checkWireAvail(wire));
- topItem->addSubProperty(availItem);
+ QtProperty *topItem = addTopLevelProperty("Wire");
- QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net");
- cellItem->setValue(ctx->getBoundWireNet(wire).c_str(ctx));
- topItem->addSubProperty(cellItem);
-
- QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net");
- conflictItem->setValue(ctx->getConflictingWireNet(wire).c_str(ctx));
- topItem->addSubProperty(conflictItem);
+ addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
+ addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
+ addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET);
+ addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
+ ElementType::NET);
+ QtProperty *belpinItem = addSubGroup(topItem, "BelPin Uphill");
BelPin uphill = ctx->getBelPinUphill(wire);
- QtProperty *belpinItem = groupManager->addProperty("BelPin Uphill");
- topItem->addSubProperty(belpinItem);
-
- QtVariantProperty *belUphillItem = readOnlyManager->addProperty(QVariant::String, "Bel");
if (uphill.bel != BelId())
- belUphillItem->setValue(ctx->getBelName(uphill.bel).c_str(ctx));
+ addProperty(belpinItem, QVariant::String, "Bel", ctx->getBelName(uphill.bel).c_str(ctx), ElementType::BEL);
else
- belUphillItem->setValue("");
- belpinItem->addSubProperty(belUphillItem);
+ addProperty(belpinItem, QVariant::String, "Bel", "", ElementType::BEL);
- QtVariantProperty *portUphillItem = readOnlyManager->addProperty(QVariant::String, "PortPin");
- portUphillItem->setValue(ctx->portPinToId(uphill.pin).c_str(ctx));
- belpinItem->addSubProperty(portUphillItem);
+ addProperty(belpinItem, QVariant::String, "PortPin", ctx->portPinToId(uphill.pin).c_str(ctx), ElementType::BEL);
- QtProperty *downhillItem = groupManager->addProperty("BelPins Downhill");
- topItem->addSubProperty(downhillItem);
+ QtProperty *downhillItem = addSubGroup(topItem, "BelPin Downhill");
for (const auto &item : ctx->getBelPinsDownhill(wire)) {
QString belname = "";
if (item.bel != BelId())
belname = ctx->getBelName(item.bel).c_str(ctx);
QString pinname = ctx->portPinToId(item.pin).c_str(ctx);
- QtProperty *dhItem = groupManager->addProperty(belname + "-" + pinname);
- downhillItem->addSubProperty(dhItem);
-
- QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel");
- belItem->setValue(belname);
- dhItem->addSubProperty(belItem);
-
- QtVariantProperty *portItem = readOnlyManager->addProperty(QVariant::String, "PortPin");
- portItem->setValue(pinname);
- dhItem->addSubProperty(portItem);
+ QtProperty *dhItem = addSubGroup(downhillItem, belname + "-" + pinname);
+ addProperty(dhItem, QVariant::String, "Bel", belname, ElementType::BEL);
+ addProperty(dhItem, QVariant::String, "PortPin", pinname);
}
- QtProperty *pipsDownItem = groupManager->addProperty("Pips Downhill");
- topItem->addSubProperty(pipsDownItem);
+ int counter = 0;
+ QtProperty *pipsDownItem = addSubGroup(downhillItem, "Pips Downhill");
for (const auto &item : ctx->getPipsDownhill(wire)) {
- QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "");
- pipItem->setValue(ctx->getPipName(item).c_str(ctx));
- pipsDownItem->addSubProperty(pipItem);
+ addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
+ counter++;
+ if (counter == 50) {
+ addProperty(pipsDownItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
+ break;
+ }
}
- QtProperty *pipsUpItem = groupManager->addProperty("Pips Uphill");
- topItem->addSubProperty(pipsUpItem);
+ counter = 0;
+ QtProperty *pipsUpItem = addSubGroup(downhillItem, "Pips Uphill");
for (const auto &item : ctx->getPipsUphill(wire)) {
- QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "");
- pipItem->setValue(ctx->getPipName(item).c_str(ctx));
- pipsUpItem->addSubProperty(pipItem);
+ addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
+ counter++;
+ if (counter == 50) {
+ addProperty(pipsUpItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
+ break;
+ }
}
-
} else if (type == ElementType::PIP) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
PipId pip = ctx->getPipByName(c);
- QtProperty *topItem = groupManager->addProperty("Pip");
- addProperty(topItem, "Pip");
-
- QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- nameItem->setValue(c.c_str(ctx));
- topItem->addSubProperty(nameItem);
-
- QtVariantProperty *availItem = readOnlyManager->addProperty(QVariant::Bool, "Available");
- availItem->setValue(ctx->checkPipAvail(pip));
- topItem->addSubProperty(availItem);
-
- QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Bound Net");
- cellItem->setValue(ctx->getBoundPipNet(pip).c_str(ctx));
- topItem->addSubProperty(cellItem);
+ decals.push_back(ctx->getPipDecal(pip));
+ Q_EMIT selected(decals);
- QtVariantProperty *conflictItem = readOnlyManager->addProperty(QVariant::String, "Conflicting Net");
- conflictItem->setValue(ctx->getConflictingPipNet(pip).c_str(ctx));
- topItem->addSubProperty(conflictItem);
+ QtProperty *topItem = addTopLevelProperty("Pip");
- QtVariantProperty *srcWireItem = readOnlyManager->addProperty(QVariant::String, "Src Wire");
- srcWireItem->setValue(ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx));
- topItem->addSubProperty(srcWireItem);
-
- QtVariantProperty *destWireItem = readOnlyManager->addProperty(QVariant::String, "Dest Wire");
- destWireItem->setValue(ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx));
- topItem->addSubProperty(destWireItem);
+ addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
+ addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
+ addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET);
+ addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx),
+ ElementType::NET);
+ addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
+ ElementType::WIRE);
+ addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx),
+ ElementType::WIRE);
DelayInfo delay = ctx->getPipDelay(pip);
- QtProperty *delayItem = groupManager->addProperty("Delay");
- topItem->addSubProperty(delayItem);
-
- QtVariantProperty *raiseDelayItem = readOnlyManager->addProperty(QVariant::Double, "Raise");
- raiseDelayItem->setValue(delay.raiseDelay());
- delayItem->addSubProperty(raiseDelayItem);
-
- QtVariantProperty *fallDelayItem = readOnlyManager->addProperty(QVariant::Double, "Fall");
- fallDelayItem->setValue(delay.fallDelay());
- delayItem->addSubProperty(fallDelayItem);
-
- QtVariantProperty *avgDelayItem = readOnlyManager->addProperty(QVariant::Double, "Average");
- avgDelayItem->setValue(delay.avgDelay());
- delayItem->addSubProperty(avgDelayItem);
+ QtProperty *delayItem = addSubGroup(topItem, "Delay");
+ addProperty(delayItem, QVariant::Double, "Raise", delay.raiseDelay());
+ addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
+ addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
} else if (type == ElementType::NET) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
NetInfo *net = ctx->nets.at(c).get();
- QtProperty *topItem = groupManager->addProperty("Net");
- addProperty(topItem, "Net");
-
- QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- nameItem->setValue(net->name.c_str(ctx));
- topItem->addSubProperty(nameItem);
-
- QtProperty *driverItem = groupManager->addProperty("Driver");
- topItem->addSubProperty(driverItem);
-
- QtVariantProperty *portItem = readOnlyManager->addProperty(QVariant::String, "Port");
- portItem->setValue(net->driver.port.c_str(ctx));
- driverItem->addSubProperty(portItem);
+ QtProperty *topItem = addTopLevelProperty("Net");
- QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget");
- budgetItem->setValue(net->driver.budget);
- driverItem->addSubProperty(budgetItem);
+ addProperty(topItem, QVariant::String, "Name", net->name.c_str(ctx));
- QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Cell");
+ QtProperty *driverItem = addSubGroup(topItem, "Driver");
+ addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx));
+ addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget);
if (net->driver.cell)
- cellNameItem->setValue(net->driver.cell->name.c_str(ctx));
+ addProperty(driverItem, QVariant::String, "Cell", net->driver.cell->name.c_str(ctx), ElementType::CELL);
else
- cellNameItem->setValue("");
- driverItem->addSubProperty(cellNameItem);
+ addProperty(driverItem, QVariant::String, "Cell", "", ElementType::CELL);
- QtProperty *usersItem = groupManager->addProperty("Users");
- topItem->addSubProperty(usersItem);
+ QtProperty *usersItem = addSubGroup(topItem, "Users");
for (auto &item : net->users) {
- QtProperty *portItem = groupManager->addProperty(item.port.c_str(ctx));
- usersItem->addSubProperty(portItem);
+ QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx));
- QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Port");
- nameItem->setValue(item.port.c_str(ctx));
- portItem->addSubProperty(nameItem);
-
- QtVariantProperty *budgetItem = readOnlyManager->addProperty(QVariant::Double, "Budget");
- budgetItem->setValue(item.budget);
- portItem->addSubProperty(budgetItem);
-
- QtVariantProperty *userItem = readOnlyManager->addProperty(QVariant::String, "Cell");
+ addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx));
+ addProperty(portItem, QVariant::Double, "Budget", item.budget);
if (item.cell)
- userItem->setValue(item.cell->name.c_str(ctx));
+ addProperty(portItem, QVariant::String, "Cell", item.cell->name.c_str(ctx), ElementType::CELL);
else
- userItem->setValue("");
- portItem->addSubProperty(userItem);
+ addProperty(portItem, QVariant::String, "Cell", "", ElementType::CELL);
}
- QtProperty *attrsItem = groupManager->addProperty("Attributes");
- topItem->addSubProperty(attrsItem);
+ QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
for (auto &item : net->attrs) {
- QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx));
- attrItem->setValue(item.second.c_str());
- attrsItem->addSubProperty(attrItem);
+ addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
}
- QtProperty *wiresItem = groupManager->addProperty("Wires");
- topItem->addSubProperty(wiresItem);
+ QtProperty *wiresItem = addSubGroup(topItem, "Wires");
for (auto &item : net->wires) {
auto name = ctx->getWireName(item.first).c_str(ctx);
- QtProperty *wireItem = groupManager->addProperty(name);
-
- QtVariantProperty *nameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- nameItem->setValue(name);
- wireItem->addSubProperty(nameItem);
-
- QtVariantProperty *pipItem = readOnlyManager->addProperty(QVariant::String, "Pip");
+ QtProperty *wireItem = addSubGroup(wiresItem, name);
+ addProperty(wireItem, QVariant::String, "Name", name);
if (item.second.pip != PipId())
- pipItem->setValue(ctx->getPipName(item.second.pip).c_str(ctx));
+ addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
+ ElementType::PIP);
else
- pipItem->setValue("");
- wireItem->addSubProperty(pipItem);
+ addProperty(wireItem, QVariant::String, "Pip", "", ElementType::PIP);
- QtVariantProperty *strengthItem = readOnlyManager->addProperty(QVariant::Int, "Strength");
- strengthItem->setValue((int)item.second.strength);
- wireItem->addSubProperty(strengthItem);
-
- wiresItem->addSubProperty(wireItem);
+ addProperty(wireItem, QVariant::Int, "Strength", (int)item.second.strength);
}
} else if (type == ElementType::CELL) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
CellInfo *cell = ctx->cells.at(c).get();
- QtProperty *topItem = groupManager->addProperty("Cell");
- addProperty(topItem, "Cell");
-
- QtVariantProperty *cellNameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- cellNameItem->setValue(cell->name.c_str(ctx));
- topItem->addSubProperty(cellNameItem);
-
- QtVariantProperty *cellTypeItem = readOnlyManager->addProperty(QVariant::String, "Type");
- cellTypeItem->setValue(cell->type.c_str(ctx));
- topItem->addSubProperty(cellTypeItem);
+ QtProperty *topItem = addTopLevelProperty("Cell");
- QtVariantProperty *cellBelItem = readOnlyManager->addProperty(QVariant::String, "Bel");
+ addProperty(topItem, QVariant::String, "Name", cell->name.c_str(ctx));
+ addProperty(topItem, QVariant::String, "Type", cell->type.c_str(ctx));
if (cell->bel != BelId())
- cellBelItem->setValue(ctx->getBelName(cell->bel).c_str(ctx));
+ addProperty(topItem, QVariant::String, "Bel", ctx->getBelName(cell->bel).c_str(ctx), ElementType::BEL);
else
- cellBelItem->setValue("");
- topItem->addSubProperty(cellBelItem);
+ addProperty(topItem, QVariant::String, "Bel", "", ElementType::BEL);
+ addProperty(topItem, QVariant::Int, "Bel strength", int(cell->belStrength));
- QtVariantProperty *cellBelStrItem = readOnlyManager->addProperty(QVariant::Int, "Bel strength");
- cellBelStrItem->setValue(int(cell->belStrength));
- topItem->addSubProperty(cellBelStrItem);
-
- QtProperty *cellPortsItem = groupManager->addProperty("Ports");
- topItem->addSubProperty(cellPortsItem);
+ QtProperty *cellPortsItem = addSubGroup(topItem, "Ports");
for (auto &item : cell->ports) {
PortInfo p = item.second;
- QtProperty *portInfoItem = groupManager->addProperty(p.name.c_str(ctx));
-
- QtVariantProperty *portInfoNameItem = readOnlyManager->addProperty(QVariant::String, "Name");
- portInfoNameItem->setValue(p.name.c_str(ctx));
- portInfoItem->addSubProperty(portInfoNameItem);
-
- QtVariantProperty *portInfoTypeItem = readOnlyManager->addProperty(QVariant::Int, "Type");
- portInfoTypeItem->setValue(int(p.type));
- portInfoItem->addSubProperty(portInfoTypeItem);
-
- QtVariantProperty *portInfoNetItem = readOnlyManager->addProperty(QVariant::String, "Net");
+ QtProperty *portInfoItem = addSubGroup(cellPortsItem, p.name.c_str(ctx));
+ addProperty(portInfoItem, QVariant::String, "Name", p.name.c_str(ctx));
+ addProperty(portInfoItem, QVariant::Int, "Type", int(p.type));
if (p.net)
- portInfoNetItem->setValue(p.net->name.c_str(ctx));
+ addProperty(portInfoItem, QVariant::String, "Net", p.net->name.c_str(ctx), ElementType::NET);
else
- portInfoNetItem->setValue("");
- portInfoItem->addSubProperty(portInfoNetItem);
-
- cellPortsItem->addSubProperty(portInfoItem);
+ addProperty(portInfoItem, QVariant::String, "Net", "", ElementType::NET);
}
- QtProperty *cellAttrItem = groupManager->addProperty("Attributes");
- topItem->addSubProperty(cellAttrItem);
+ QtProperty *cellAttrItem = addSubGroup(topItem, "Attributes");
for (auto &item : cell->attrs) {
- QtVariantProperty *attrItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx));
- attrItem->setValue(item.second.c_str());
- cellAttrItem->addSubProperty(attrItem);
+ addProperty(cellAttrItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
}
- QtProperty *cellParamsItem = groupManager->addProperty("Parameters");
- topItem->addSubProperty(cellParamsItem);
+ QtProperty *cellParamsItem = addSubGroup(topItem, "Parameters");
for (auto &item : cell->params) {
- QtVariantProperty *paramItem = readOnlyManager->addProperty(QVariant::String, item.first.c_str(ctx));
- paramItem->setValue(item.second.c_str());
- cellParamsItem->addSubProperty(paramItem);
+ addProperty(cellParamsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
}
QtProperty *cellPinsItem = groupManager->addProperty("Pins");
@@ -583,38 +667,159 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *clickItem, int pos) std::string cell_port = item.first.c_str(ctx);
std::string bel_pin = item.second.c_str(ctx);
- QtProperty *pinGroupItem = groupManager->addProperty((cell_port + " -> " + bel_pin).c_str());
+ QtProperty *pinGroupItem = addSubGroup(cellPortsItem, (cell_port + " -> " + bel_pin).c_str());
- QtVariantProperty *cellItem = readOnlyManager->addProperty(QVariant::String, "Cell");
- cellItem->setValue(cell_port.c_str());
- pinGroupItem->addSubProperty(cellItem);
+ addProperty(pinGroupItem, QVariant::String, "Cell", cell_port.c_str(), ElementType::CELL);
+ addProperty(pinGroupItem, QVariant::String, "Bel", bel_pin.c_str(), ElementType::BEL);
+ }
+ }
+}
- QtVariantProperty *belItem = readOnlyManager->addProperty(QVariant::String, "Bel");
- belItem->setValue(bel_pin.c_str());
- pinGroupItem->addSubProperty(belItem);
+std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
+{
+ std::vector<DecalXY> decals;
+ switch (type) {
+ case ElementType::BEL: {
+ BelId bel = ctx->getBelByName(value);
+ if (bel != BelId()) {
+ decals.push_back(ctx->getBelDecal(bel));
+ }
+ } break;
+ case ElementType::WIRE: {
+ WireId wire = ctx->getWireByName(value);
+ if (wire != WireId()) {
+ decals.push_back(ctx->getWireDecal(wire));
+ Q_EMIT selected(decals);
+ }
+ } break;
+ case ElementType::PIP: {
+ PipId pip = ctx->getPipByName(value);
+ if (pip != PipId()) {
+ decals.push_back(ctx->getPipDecal(pip));
+ Q_EMIT selected(decals);
+ }
+ } break;
+ case ElementType::NET: {
+ } break;
+ case ElementType::CELL: {
+ } break;
+ default:
+ break;
+ }
+ return decals;
+}
- cellPinsItem->addSubProperty(pinGroupItem);
+void DesignWidget::updateHighlightGroup(QTreeWidgetItem *item, int group)
+{
+ if (highlightSelected.contains(item)) {
+ if (highlightSelected[item] == group) {
+ highlightSelected.remove(item);
+ } else
+ highlightSelected[item] = group;
+ } else
+ highlightSelected.insert(item, group);
+
+ std::vector<DecalXY> decals;
+
+ for (auto it : highlightSelected.toStdMap()) {
+ if (it.second == group) {
+ ElementType type = static_cast<ElementTreeItem *>(it.first)->getType();
+ IdString value = static_cast<IdStringTreeItem *>(it.first)->getData();
+ std::vector<DecalXY> d = getDecals(type, value);
+ std::move(d.begin(), d.end(), std::back_inserter(decals));
}
}
+
+ Q_EMIT highlight(decals, group);
}
-void DesignWidget::prepareMenu(const QPoint &pos)
+void DesignWidget::prepareMenuProperty(const QPoint &pos)
{
- QTreeWidget *tree = treeWidget;
+ QTreeWidget *tree = propertyEditor->treeWidget();
itemContextMenu = tree->itemAt(pos);
+ if (itemContextMenu->parent() == nullptr)
+ return;
- QAction *selectAction = new QAction("&Select", this);
- selectAction->setStatusTip("Select item on view");
+ QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
+ if (!browserItem)
+ return;
+ QtProperty *selectedProperty = browserItem->property();
+ ElementType type = getElementTypeByName(selectedProperty->propertyId());
+ if (type == ElementType::NONE)
+ return;
+ IdString value = ctx->id(selectedProperty->valueText().toStdString());
- connect(selectAction, SIGNAL(triggered()), this, SLOT(selectObject()));
+ QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
QMenu menu(this);
+ QAction *selectAction = new QAction("&Select", this);
+ connect(selectAction, &QAction::triggered, this, [this, type, value] { Q_EMIT selected(getDecals(type, value)); });
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 (highlightSelected.contains(item) && highlightSelected[item] == i)
+ action->setChecked(true);
+ connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); });
+ }
menu.exec(tree->mapToGlobal(pos));
}
-void DesignWidget::selectObject() { Q_EMIT info("selected " + itemContextMenu->text(0).toStdString() + "\n"); }
+void DesignWidget::prepareMenuTree(const QPoint &pos)
+{
+ QTreeWidget *tree = treeWidget;
+
+ itemContextMenu = tree->itemAt(pos);
+
+ ElementType type = static_cast<ElementTreeItem *>(itemContextMenu)->getType();
+ IdString value = static_cast<IdStringTreeItem *>(itemContextMenu)->getData();
+
+ if (type == ElementType::NONE)
+ return;
+
+ QTreeWidgetItem *item = nameToItem[getElementIndex(type)].value(value.c_str(ctx));
+
+ QMenu menu(this);
+ 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 (highlightSelected.contains(item) && highlightSelected[item] == i)
+ action->setChecked(true);
+ connect(action, &QAction::triggered, this, [this, i, item] { updateHighlightGroup(item, i); });
+ }
+ menu.exec(tree->mapToGlobal(pos));
+}
+
+void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
+{
+ QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property();
+ ElementType type = getElementTypeByName(selectedProperty->propertyId());
+ QString value = selectedProperty->valueText();
+ int index = getElementIndex(type);
+ switch (type) {
+ case ElementType::NONE:
+ return;
+ default: {
+ if (nameToItem[index].contains(value))
+ treeWidget->setCurrentItem(nameToItem[index].value(value));
+ } break;
+ }
+}
NEXTPNR_NAMESPACE_END
diff --git a/gui/designwidget.h b/gui/designwidget.h index 7785513a..1afe817d 100644 --- a/gui/designwidget.h +++ b/gui/designwidget.h @@ -21,6 +21,7 @@ #define DESIGNWIDGET_H
#include <QTreeWidget>
+#include <QVariant>
#include "nextpnr.h"
#include "qtgroupboxpropertybrowser.h"
#include "qtpropertymanager.h"
@@ -29,6 +30,16 @@ NEXTPNR_NAMESPACE_BEGIN
+enum class ElementType
+{
+ NONE,
+ BEL,
+ WIRE,
+ PIP,
+ NET,
+ CELL
+};
+
class DesignWidget : public QWidget
{
Q_OBJECT
@@ -38,16 +49,30 @@ class DesignWidget : public QWidget ~DesignWidget();
private:
- void addProperty(QtProperty *property, const QString &id);
void clearProperties();
-
+ QtProperty *addTopLevelProperty(const QString &id);
+ QtProperty *addSubGroup(QtProperty *topItem, const QString &name);
+ void addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value,
+ const ElementType &type = ElementType::NONE);
+ QString getElementTypeName(ElementType type);
+ ElementType getElementTypeByName(QString type);
+ int getElementIndex(ElementType type);
+ void updateButtons();
+ void addToHistory(QTreeWidgetItem *item);
+ std::vector<DecalXY> getDecals(ElementType type, IdString value);
+ void updateHighlightGroup(QTreeWidgetItem *item, int group);
Q_SIGNALS:
void info(std::string text);
+ void selected(std::vector<DecalXY> decal);
+ void highlight(std::vector<DecalXY> decal, int group);
+ void finishContextLoad();
+ void contextLoadStatus(std::string text);
private Q_SLOTS:
- void prepareMenu(const QPoint &pos);
- void onItemClicked(QTreeWidgetItem *item, int);
- void selectObject();
+ void prepareMenuProperty(const QPoint &pos);
+ void prepareMenuTree(const QPoint &pos);
+ void onItemSelectionChanged();
+ void onItemDoubleClicked(QTreeWidgetItem *item, int column);
public Q_SLOTS:
void newContext(Context *ctx);
void updateTree();
@@ -66,8 +91,22 @@ class DesignWidget : public QWidget QMap<QtProperty *, QString> propertyToId;
QMap<QString, QtProperty *> idToProperty;
+
+ QMap<QString, QTreeWidgetItem *> nameToItem[6];
+ std::vector<QTreeWidgetItem *> history;
+ int history_index;
+ bool history_ignore;
+
QTreeWidgetItem *nets_root;
QTreeWidgetItem *cells_root;
+
+ QAction *actionFirst;
+ QAction *actionPrev;
+ QAction *actionNext;
+ QAction *actionLast;
+
+ QColor highlightColors[8];
+ QMap<QTreeWidgetItem *, int> highlightSelected;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/ecp5/family.cmake b/gui/ecp5/family.cmake new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gui/ecp5/family.cmake diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc new file mode 100644 index 00000000..1168a55c --- /dev/null +++ b/gui/ecp5/mainwindow.cc @@ -0,0 +1,51 @@ +/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "mainwindow.h"
+
+static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
+
+NEXTPNR_NAMESPACE_BEGIN
+
+MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : BaseMainWindow(std::move(context), parent)
+{
+ initMainResource();
+
+ std::string title = "nextpnr-ecp5 - [EMPTY]";
+ setWindowTitle(title.c_str());
+
+ createMenu();
+ Q_EMIT contextChanged(ctx.get());
+}
+
+MainWindow::~MainWindow() {}
+
+void MainWindow::createMenu()
+{
+ QMenu *menu_Custom = new QMenu("&Generic", menuBar);
+ menuBar->addAction(menu_Custom->menuAction());
+}
+
+void MainWindow::new_proj() {}
+
+void MainWindow::open_proj() {}
+
+bool MainWindow::save_proj() { return false; }
+
+NEXTPNR_NAMESPACE_END
diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h new file mode 100644 index 00000000..e97bb4e7 --- /dev/null +++ b/gui/ecp5/mainwindow.h @@ -0,0 +1,46 @@ +/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "../basewindow.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+class MainWindow : public BaseMainWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit MainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
+ virtual ~MainWindow();
+
+ public:
+ void createMenu();
+
+ protected Q_SLOTS:
+ virtual void new_proj();
+ virtual void open_proj();
+ virtual bool save_proj();
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif // MAINWINDOW_H
diff --git a/gui/ecp5/nextpnr.qrc b/gui/ecp5/nextpnr.qrc new file mode 100644 index 00000000..03585ec0 --- /dev/null +++ b/gui/ecp5/nextpnr.qrc @@ -0,0 +1,2 @@ +<RCC> +</RCC> diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 0c6b1a98..2d8d4cef 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -195,7 +195,7 @@ bool LineShader::compile(void) return true; } -void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection) +void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection) { auto gl = QOpenGLContext::currentContext()->functions(); vao_.bind(); @@ -214,8 +214,8 @@ void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection) buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size()); program_->setUniformValue(uniforms_.projection, projection); - program_->setUniformValue(uniforms_.thickness, line.thickness); - program_->setUniformValue(uniforms_.color, line.color.r, line.color.g, line.color.b, line.color.a); + program_->setUniformValue(uniforms_.thickness, thickness); + program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF()); buffers_.position.bind(); program_->enableAttributeArray("position"); @@ -241,8 +241,27 @@ void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection) } FPGAViewWidget::FPGAViewWidget(QWidget *parent) - : QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f), lineShader_(this), ctx_(nullptr) + : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), selectedItemsChanged_(false) { + backgroundColor_ = QColor("#000000"); + gridColor_ = QColor("#333"); + gFrameColor_ = QColor("#d0d0d0"); + gHiddenColor_ = QColor("#606060"); + gInactiveColor_ = QColor("#303030"); + gActiveColor_ = QColor("#f0f0f0"); + gSelectedColor_ = QColor("#ff6600"); + frameColor_ = QColor("#0066ba"); + highlightColors[0] = QColor("#6495ed"); + highlightColors[1] = QColor("#7fffd4"); + highlightColors[2] = QColor("#98fb98"); + highlightColors[3] = QColor("#ffd700"); + highlightColors[4] = QColor("#cd5c5c"); + highlightColors[5] = QColor("#fa8072"); + highlightColors[6] = QColor("#ff69b4"); + highlightColors[7] = QColor("#da70d6"); + for (int i = 0; i < 8; i++) + highlightItemsChanged_[i] = false; + auto fmt = format(); fmt.setMajorVersion(3); fmt.setMinorVersion(1); @@ -264,6 +283,7 @@ FPGAViewWidget::~FPGAViewWidget() {} void FPGAViewWidget::newContext(Context *ctx) { ctx_ = ctx; + selectedItems_.clear(); update(); } @@ -271,64 +291,90 @@ QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); } QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); } -void FPGAViewWidget::setXTranslation(float t_x) +void FPGAViewWidget::initializeGL() { - if (t_x == moveX_) - return; - - moveX_ = t_x; - update(); + if (!lineShader_.compile()) { + log_error("Could not compile shader.\n"); + } + initializeOpenGLFunctions(); + glClearColor(backgroundColor_.red() / 255, backgroundColor_.green() / 255, backgroundColor_.blue() / 255, 0.0); } -void FPGAViewWidget::setYTranslation(float t_y) +void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal) { - if (t_y == moveY_) - return; + const float scale = 1.0; + float offsetX = 0.0, offsetY = 0.0; + + for (auto &el : ctx_->getDecalGraphics(decal.decal)) { + offsetX = decal.x; + offsetY = decal.y; + + if (el.type == GraphicElement::G_BOX) { + auto line = PolyLine(true); + line.point(offsetX + scale * el.x1, offsetY + scale * el.y1); + line.point(offsetX + scale * el.x2, offsetY + scale * el.y1); + line.point(offsetX + scale * el.x2, offsetY + scale * el.y2); + line.point(offsetX + scale * el.x1, offsetY + scale * el.y2); + line.build(out); + } - moveY_ = t_y; - update(); + if (el.type == GraphicElement::G_LINE) { + PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2) + .build(out); + } + } } -void FPGAViewWidget::setZoom(float t_z) +void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal) { - if (t_z == zoom_) - return; - zoom_ = t_z; - - if (zoom_ < 1.0f) - zoom_ = 1.0f; - if (zoom_ > 100.f) - zoom_ = 100.0f; - - update(); -} + const float scale = 1.0; + float offsetX = 0.0, offsetY = 0.0; + + for (auto &el : ctx_->getDecalGraphics(decal.decal)) { + offsetX = decal.x; + offsetY = decal.y; + + if (el.type == GraphicElement::G_BOX) { + auto line = PolyLine(true); + line.point(offsetX + scale * el.x1, offsetY + scale * el.y1); + line.point(offsetX + scale * el.x2, offsetY + scale * el.y1); + line.point(offsetX + scale * el.x2, offsetY + scale * el.y2); + line.point(offsetX + scale * el.x1, offsetY + scale * el.y2); + switch (el.style) { + case GraphicElement::G_FRAME: + case GraphicElement::G_INACTIVE: + case GraphicElement::G_ACTIVE: + line.build(out[el.style]); + break; + default: + break; + } + } -void FPGAViewWidget::initializeGL() -{ - if (!lineShader_.compile()) { - log_error("Could not compile shader.\n"); + if (el.type == GraphicElement::G_LINE) { + auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, + offsetY + scale * el.y2); + switch (el.style) { + case GraphicElement::G_FRAME: + case GraphicElement::G_INACTIVE: + case GraphicElement::G_ACTIVE: + line.build(out[el.style]); + break; + default: + break; + } + } } - initializeOpenGLFunctions(); - glClearColor(1.0, 1.0, 1.0, 0.0); } -void FPGAViewWidget::drawElement(LineShaderData &out, const GraphicElement &el) +QMatrix4x4 FPGAViewWidget::getProjection(void) { - const float scale = 1.0, offset = 0.0; - - if (el.type == GraphicElement::G_BOX) { - auto line = PolyLine(true); - line.point(offset + scale * el.x1, offset + scale * el.y1); - line.point(offset + scale * el.x2, offset + scale * el.y1); - line.point(offset + scale * el.x2, offset + scale * el.y2); - line.point(offset + scale * el.x1, offset + scale * el.y2); - line.build(out); - } + QMatrix4x4 matrix; - if (el.type == GraphicElement::G_LINE) { - PolyLine(offset + scale * el.x1, offset + scale * el.y1, offset + scale * el.x2, offset + scale * el.y2) - .build(out); - } + const float aspect = float(width()) / float(height()); + matrix.perspective(3.14 / 2, aspect, zoomNear_, zoomFar_); + matrix.translate(0.0f, 0.0f, -zoom_); + return matrix; } void FPGAViewWidget::paintGL() @@ -338,65 +384,112 @@ void FPGAViewWidget::paintGL() gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale); gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - const float aspect = float(width()) / float(height()); + QMatrix4x4 matrix = getProjection(); - QMatrix4x4 matrix; - matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f)); - matrix.translate(moveX_, moveY_, -0.5); - matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0); + matrix *= viewMove_; + + // Calculate world thickness to achieve a screen 1px/1.1px line. + float thick1Px = mouseToWorldCoordinates(1, 0).x(); + float thick11Px = mouseToWorldCoordinates(1.1, 0).x(); // Draw grid. - auto grid = LineShaderData(0.01f, QColor("#DDD")); + auto grid = LineShaderData(); for (float i = -100.0f; i < 100.0f; i += 1.0f) { PolyLine(-100.0f, i, 100.0f, i).build(grid); PolyLine(i, -100.0f, i, 100.0f).build(grid); } - lineShader_.draw(grid, matrix); + lineShader_.draw(grid, gridColor_, thick1Px, matrix); + + LineShaderData shaders[4] = {[GraphicElement::G_FRAME] = LineShaderData(), + [GraphicElement::G_HIDDEN] = LineShaderData(), + [GraphicElement::G_INACTIVE] = LineShaderData(), + [GraphicElement::G_ACTIVE] = LineShaderData()}; - // Draw Bels. - auto bels = LineShaderData(0.02f, QColor("#b000ba")); if (ctx_) { + // Draw Bels. for (auto bel : ctx_->getBels()) { - for (auto &el : ctx_->getBelGraphics(bel)) - drawElement(bels, el); + drawDecal(shaders, ctx_->getBelDecal(bel)); + } + // Draw Wires. + for (auto wire : ctx_->getWires()) { + drawDecal(shaders, ctx_->getWireDecal(wire)); + } + // Draw Pips. + for (auto pip : ctx_->getPips()) { + drawDecal(shaders, ctx_->getPipDecal(pip)); + } + // Draw Groups. + for (auto group : ctx_->getGroups()) { + drawDecal(shaders, ctx_->getGroupDecal(group)); } - lineShader_.draw(bels, matrix); - } - // Draw Frame Graphics. - auto frames = LineShaderData(0.02f, QColor("#0066ba")); - if (ctx_) { - for (auto &el : ctx_->getFrameGraphics()) { - drawElement(frames, el); + if (selectedItemsChanged_) { + selectedItemsChanged_ = false; + selectedShader_.clear(); + for (auto decal : selectedItems_) { + drawDecal(selectedShader_, decal); + } + } + for (int i = 0; i < 8; i++) { + if (highlightItemsChanged_[i]) { + highlightItemsChanged_[i] = false; + highlightShader_[i].clear(); + for (auto decal : highlightItems_[i]) { + drawDecal(highlightShader_[i], decal); + } + } } - lineShader_.draw(frames, matrix); } + + lineShader_.draw(shaders[0], gFrameColor_, thick11Px, matrix); + lineShader_.draw(shaders[1], gHiddenColor_, thick11Px, matrix); + lineShader_.draw(shaders[2], gInactiveColor_, thick11Px, matrix); + lineShader_.draw(shaders[3], gActiveColor_, thick11Px, matrix); + for (int i = 0; i < 8; i++) + lineShader_.draw(highlightShader_[i], highlightColors[i], thick11Px, matrix); + lineShader_.draw(selectedShader_, gSelectedColor_, thick11Px, matrix); +} + +void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals) +{ + selectedItems_ = decals; + selectedItemsChanged_ = true; + update(); +} + +void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group) +{ + highlightItems_[group] = decals; + highlightItemsChanged_[group] = true; + update(); } void FPGAViewWidget::resizeGL(int width, int height) {} -void FPGAViewWidget::mousePressEvent(QMouseEvent *event) +void FPGAViewWidget::mousePressEvent(QMouseEvent *event) { lastPos_ = event->pos(); } + +// Invert the projection matrix to calculate screen/mouse to world/grid +// coordinates. +QVector4D FPGAViewWidget::mouseToWorldCoordinates(int x, int y) { - startDragX_ = moveX_; - startDragY_ = moveY_; - lastPos_ = event->pos(); + QMatrix4x4 p = getProjection(); + QVector2D unit = p.map(QVector4D(1, 1, 0, 1)).toVector2DAffine(); + + float sx = (((float)x) / (width() / 2)); + float sy = (((float)y) / (height() / 2)); + return QVector4D(sx / unit.x(), sy / unit.y(), 0, 1); } void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event) { const int dx = event->x() - lastPos_.x(); const int dy = event->y() - lastPos_.y(); + lastPos_ = event->pos(); - const qreal retinaScale = devicePixelRatio(); - float aspect = float(width()) / float(height()); - const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect); - const float dy_scale = dy * (1 / (float)height() * retinaScale); - - float xpos = dx_scale + startDragX_; - float ypos = dy_scale + startDragY_; + auto world = mouseToWorldCoordinates(dx, dy); + viewMove_.translate(world.x(), -world.y()); - setXTranslation(xpos); - setYTranslation(ypos); + update(); } void FPGAViewWidget::wheelEvent(QWheelEvent *event) @@ -404,8 +497,19 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event) QPoint degree = event->angleDelta() / 8; if (!degree.isNull()) { - float steps = degree.y() / 15.0; - setZoom(zoom_ + steps); + + if (zoom_ < zoomNear_) { + zoom_ = zoomNear_; + } else if (zoom_ < zoomLvl1_) { + zoom_ -= degree.y() / 10.0; + } else if (zoom_ < zoomLvl2_) { + zoom_ -= degree.y() / 5.0; + } else if (zoom_ < zoomFar_) { + zoom_ -= degree.y(); + } else { + zoom_ = zoomFar_; + } + update(); } } diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index c281fd77..33eb2800 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -41,18 +41,6 @@ NPNR_PACKED_STRUCT(struct Vertex2DPOD { Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {} }); -// Vertex2DPOD is a structure of R, G, B, A values that can be passed to OpenGL -// directly. -NPNR_PACKED_STRUCT(struct ColorPOD { - GLfloat r; - GLfloat g; - GLfloat b; - GLfloat a; - - ColorPOD(GLfloat R, GLfloat G, GLfloat B, GLfloat A) : r(R), g(G), b(B), a(A) {} - ColorPOD(const QColor &color) : r(color.redF()), g(color.greenF()), b(color.blueF()), a(color.alphaF()) {} -}); - // LineShaderData is a built set of vertices that can be rendered by the // LineShader. // Each LineShaderData can have its' own color and thickness. @@ -63,10 +51,15 @@ struct LineShaderData std::vector<GLfloat> miters; std::vector<GLuint> indices; - GLfloat thickness; - ColorPOD color; + LineShaderData(void) {} - LineShaderData(GLfloat Thickness, QColor Color) : thickness(Thickness), color(Color) {} + void clear(void) + { + vertices.clear(); + normals.clear(); + miters.clear(); + indices.clear(); + } }; // PolyLine is a set of segments defined by points, that can be built to a @@ -210,12 +203,20 @@ class LineShader bool compile(void); // Render a LineShaderData with a given M/V/P transformation. - void draw(const LineShaderData &data, const QMatrix4x4 &projection); + void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection); }; class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT + Q_PROPERTY(QColor backgroundColor MEMBER backgroundColor_ DESIGNABLE true) + Q_PROPERTY(QColor gridColor MEMBER gridColor_ DESIGNABLE true) + Q_PROPERTY(QColor gFrameColor MEMBER gFrameColor_ DESIGNABLE true) + Q_PROPERTY(QColor gHiddenColor MEMBER gHiddenColor_ DESIGNABLE true) + Q_PROPERTY(QColor gInactiveColor MEMBER gInactiveColor_ DESIGNABLE true) + Q_PROPERTY(QColor gActiveColor MEMBER gActiveColor_ DESIGNABLE true) + Q_PROPERTY(QColor gSelectedColor MEMBER gSelectedColor_ DESIGNABLE true) + Q_PROPERTY(QColor frameColor MEMBER frameColor_ DESIGNABLE true) public: FPGAViewWidget(QWidget *parent = 0); @@ -239,20 +240,46 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; - void drawElement(LineShaderData &data, const GraphicElement &el); + void drawDecal(LineShaderData &data, const DecalXY &decal); + void drawDecal(LineShaderData out[], const DecalXY &decal); public Q_SLOTS: void newContext(Context *ctx); + void onSelectedArchItem(std::vector<DecalXY> decals); + void onHighlightGroupChanged(std::vector<DecalXY> decals, int group); private: QPoint lastPos_; - float moveX_; - float moveY_; - float zoom_; LineShader lineShader_; + QMatrix4x4 viewMove_; + float zoom_; + QMatrix4x4 getProjection(void); + QVector4D mouseToWorldCoordinates(int x, int y); + + const float zoomNear_ = 1.0f; // do not zoom closer than this + const float zoomFar_ = 10000.0f; // do not zoom further than this + + const float zoomLvl1_ = 100.0f; + const float zoomLvl2_ = 50.0f; - float startDragX_; - float startDragY_; Context *ctx_; + + QColor backgroundColor_; + QColor gridColor_; + QColor gFrameColor_; + QColor gHiddenColor_; + QColor gInactiveColor_; + QColor gActiveColor_; + QColor gSelectedColor_; + QColor frameColor_; + + LineShaderData selectedShader_; + std::vector<DecalXY> selectedItems_; + bool selectedItemsChanged_; + + LineShaderData highlightShader_[8]; + std::vector<DecalXY> highlightItems_[8]; + bool highlightItemsChanged_[8]; + QColor highlightColors[8]; }; NEXTPNR_NAMESPACE_END diff --git a/gui/generic/family.cmake b/gui/generic/family.cmake new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/gui/generic/family.cmake diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc index fef63094..88e291e6 100644 --- a/gui/generic/mainwindow.cc +++ b/gui/generic/mainwindow.cc @@ -23,7 +23,7 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent)
+MainWindow::MainWindow(std::unique_ptr<Context> context, QWidget *parent) : BaseMainWindow(std::move(context), parent)
{
initMainResource();
@@ -31,6 +31,7 @@ MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent) setWindowTitle(title.c_str());
createMenu();
+ Q_EMIT contextChanged(ctx.get());
}
MainWindow::~MainWindow() {}
diff --git a/gui/generic/mainwindow.h b/gui/generic/mainwindow.h index fd9812cd..e97bb4e7 100644 --- a/gui/generic/mainwindow.h +++ b/gui/generic/mainwindow.h @@ -29,7 +29,7 @@ class MainWindow : public BaseMainWindow Q_OBJECT
public:
- explicit MainWindow(QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, QWidget *parent = 0);
virtual ~MainWindow();
public:
diff --git a/gui/ice40/family.cmake b/gui/ice40/family.cmake new file mode 100644 index 00000000..ede5b805 --- /dev/null +++ b/gui/ice40/family.cmake @@ -0,0 +1,3 @@ +if(ICE40_HX1K_ONLY) + target_compile_definitions(gui_${family} PRIVATE ICE40_HX1K_ONLY=1) +endif() diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc index f423ee37..4b1f2c57 100644 --- a/gui/ice40/mainwindow.cc +++ b/gui/ice40/mainwindow.cc @@ -27,16 +27,14 @@ #include "design_utils.h"
#include "jsonparse.h"
#include "log.h"
-#include "pack.h"
#include "pcf.h"
-#include "place_sa.h"
-#include "route.h"
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
-MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent), timing_driven(false)
+MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
+ : BaseMainWindow(std::move(context), parent), timing_driven(false), chipArgs(args)
{
initMainResource();
@@ -62,6 +60,8 @@ MainWindow::MainWindow(QWidget *parent) : BaseMainWindow(parent), timing_driven( connect(this, SIGNAL(contextChanged(Context *)), task, SIGNAL(contextChanged(Context *)));
createMenu();
+
+ Q_EMIT contextChanged(ctx.get());
}
MainWindow::~MainWindow() { delete task; }
@@ -72,60 +72,46 @@ void MainWindow::createMenu() menuBar->addAction(menu_Design->menuAction());
actionLoadJSON = new QAction("Open JSON", this);
- QIcon iconLoadJSON;
- iconLoadJSON.addFile(QStringLiteral(":/icons/resources/open_json.png"));
- actionLoadJSON->setIcon(iconLoadJSON);
+ actionLoadJSON->setIcon(QIcon(":/icons/resources/open_json.png"));
actionLoadJSON->setStatusTip("Open an existing JSON file");
+ actionLoadJSON->setEnabled(true);
connect(actionLoadJSON, SIGNAL(triggered()), this, SLOT(open_json()));
- actionLoadJSON->setEnabled(false);
actionLoadPCF = new QAction("Open PCF", this);
- QIcon iconLoadPCF;
- iconLoadPCF.addFile(QStringLiteral(":/icons/resources/open_pcf.png"));
- actionLoadPCF->setIcon(iconLoadPCF);
+ actionLoadPCF->setIcon(QIcon(":/icons/resources/open_pcf.png"));
actionLoadPCF->setStatusTip("Open PCF file");
- connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf()));
actionLoadPCF->setEnabled(false);
+ connect(actionLoadPCF, SIGNAL(triggered()), this, SLOT(open_pcf()));
actionPack = new QAction("Pack", this);
- QIcon iconPack;
- iconPack.addFile(QStringLiteral(":/icons/resources/pack.png"));
- actionPack->setIcon(iconPack);
+ actionPack->setIcon(QIcon(":/icons/resources/pack.png"));
actionPack->setStatusTip("Pack current design");
- connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack()));
actionPack->setEnabled(false);
+ connect(actionPack, SIGNAL(triggered()), task, SIGNAL(pack()));
actionAssignBudget = new QAction("Assign Budget", this);
- QIcon iconAssignBudget;
- iconAssignBudget.addFile(QStringLiteral(":/icons/resources/time_add.png"));
- actionAssignBudget->setIcon(iconAssignBudget);
+ actionAssignBudget->setIcon(QIcon(":/icons/resources/time_add.png"));
actionAssignBudget->setStatusTip("Assign time budget for current design");
- connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget()));
actionAssignBudget->setEnabled(false);
+ connect(actionAssignBudget, SIGNAL(triggered()), this, SLOT(budget()));
actionPlace = new QAction("Place", this);
- QIcon iconPlace;
- iconPlace.addFile(QStringLiteral(":/icons/resources/place.png"));
- actionPlace->setIcon(iconPlace);
+ actionPlace->setIcon(QIcon(":/icons/resources/place.png"));
actionPlace->setStatusTip("Place current design");
- connect(actionPlace, SIGNAL(triggered()), this, SLOT(place()));
actionPlace->setEnabled(false);
+ connect(actionPlace, SIGNAL(triggered()), this, SLOT(place()));
actionRoute = new QAction("Route", this);
- QIcon iconRoute;
- iconRoute.addFile(QStringLiteral(":/icons/resources/route.png"));
- actionRoute->setIcon(iconRoute);
+ actionRoute->setIcon(QIcon(":/icons/resources/route.png"));
actionRoute->setStatusTip("Route current design");
- connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
actionRoute->setEnabled(false);
+ connect(actionRoute, SIGNAL(triggered()), task, SIGNAL(route()));
actionSaveAsc = new QAction("Save ASC", this);
- QIcon iconSaveAsc;
- iconSaveAsc.addFile(QStringLiteral(":/icons/resources/save_asc.png"));
- actionSaveAsc->setIcon(iconSaveAsc);
+ actionSaveAsc->setIcon(QIcon(":/icons/resources/save_asc.png"));
actionSaveAsc->setStatusTip("Save ASC file");
- connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
actionSaveAsc->setEnabled(false);
+ connect(actionSaveAsc, SIGNAL(triggered()), this, SLOT(save_asc()));
QToolBar *taskFPGABar = new QToolBar();
addToolBar(Qt::TopToolBarArea, taskFPGABar);
@@ -147,28 +133,22 @@ void MainWindow::createMenu() menu_Design->addAction(actionSaveAsc);
actionPlay = new QAction("Play", this);
- QIcon iconPlay;
- iconPlay.addFile(QStringLiteral(":/icons/resources/control_play.png"));
- actionPlay->setIcon(iconPlay);
+ actionPlay->setIcon(QIcon(":/icons/resources/control_play.png"));
actionPlay->setStatusTip("Continue running task");
- connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread()));
actionPlay->setEnabled(false);
+ connect(actionPlay, SIGNAL(triggered()), task, SLOT(continue_thread()));
actionPause = new QAction("Pause", this);
- QIcon iconPause;
- iconPause.addFile(QStringLiteral(":/icons/resources/control_pause.png"));
- actionPause->setIcon(iconPause);
+ actionPause->setIcon(QIcon(":/icons/resources/control_pause.png"));
actionPause->setStatusTip("Pause running task");
- connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread()));
actionPause->setEnabled(false);
+ connect(actionPause, SIGNAL(triggered()), task, SLOT(pause_thread()));
actionStop = new QAction("Stop", this);
- QIcon iconStop;
- iconStop.addFile(QStringLiteral(":/icons/resources/control_stop.png"));
- actionStop->setIcon(iconStop);
+ actionStop->setIcon(QIcon(":/icons/resources/control_stop.png"));
actionStop->setStatusTip("Stop running task");
- connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
actionStop->setEnabled(false);
+ connect(actionStop, SIGNAL(triggered()), task, SLOT(terminate_thread()));
QToolBar *taskToolBar = new QToolBar();
addToolBar(Qt::TopToolBarArea, taskToolBar);
@@ -220,12 +200,16 @@ QStringList getSupportedPackages(ArchArgs::ArchArgsTypes chip) void MainWindow::new_proj()
{
QMap<QString, int> arch;
+#ifdef ICE40_HX1K_ONLY
+ arch.insert("Lattice HX1K", ArchArgs::HX1K);
+#else
arch.insert("Lattice LP384", ArchArgs::LP384);
arch.insert("Lattice LP1K", ArchArgs::LP1K);
arch.insert("Lattice HX1K", ArchArgs::HX1K);
arch.insert("Lattice UP5K", ArchArgs::UP5K);
arch.insert("Lattice LP8K", ArchArgs::LP8K);
arch.insert("Lattice HX8K", ArchArgs::HX8K);
+#endif
bool ok;
QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
if (ok && !item.isEmpty()) {
@@ -237,18 +221,30 @@ void MainWindow::new_proj() if (ok && !item.isEmpty()) {
disableActions();
+ preload_pcf = "";
chipArgs.package = package.toStdString().c_str();
- if (ctx)
- delete ctx;
- ctx = new Context(chipArgs);
-
- Q_EMIT contextChanged(ctx);
-
+ ctx = std::unique_ptr<Context>(new Context(chipArgs));
actionLoadJSON->setEnabled(true);
+
+ Q_EMIT displaySplash();
+ Q_EMIT contextChanged(ctx.get());
}
}
}
+void MainWindow::load_json(std::string filename, std::string pcf)
+{
+ preload_pcf = pcf;
+ disableActions();
+ Q_EMIT task->loadfile(filename);
+}
+
+void MainWindow::load_pcf(std::string filename)
+{
+ disableActions();
+ Q_EMIT task->loadpcf(filename);
+}
+
void MainWindow::newContext(Context *ctx)
{
std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + chipArgs.package + " )";
@@ -259,8 +255,6 @@ void MainWindow::open_proj() {
QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
if (!fileName.isEmpty()) {
- tabWidget->setCurrentWidget(info);
-
std::string fn = fileName.toStdString();
disableActions();
}
@@ -270,12 +264,7 @@ void MainWindow::open_json() {
QString fileName = QFileDialog::getOpenFileName(this, QString("Open JSON"), QString(), QString("*.json"));
if (!fileName.isEmpty()) {
- tabWidget->setCurrentWidget(info);
-
- std::string fn = fileName.toStdString();
- disableActions();
- timing_driven = false;
- Q_EMIT task->loadfile(fn);
+ load_json(fileName.toStdString(), "");
}
}
@@ -283,11 +272,7 @@ void MainWindow::open_pcf() {
QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf"));
if (!fileName.isEmpty()) {
- tabWidget->setCurrentWidget(info);
-
- std::string fn = fileName.toStdString();
- disableActions();
- Q_EMIT task->loadpcf(fn);
+ load_pcf(fileName.toStdString());
}
}
@@ -328,9 +313,12 @@ void MainWindow::loadfile_finished(bool status) log("Loading design successful.\n");
actionLoadPCF->setEnabled(true);
actionPack->setEnabled(true);
+ if (!preload_pcf.empty())
+ load_pcf(preload_pcf);
Q_EMIT updateTreeView();
} else {
log("Loading design failed.\n");
+ preload_pcf = "";
}
}
@@ -440,4 +428,4 @@ void MainWindow::budget() void MainWindow::place() { Q_EMIT task->place(timing_driven); }
-NEXTPNR_NAMESPACE_END
\ No newline at end of file +NEXTPNR_NAMESPACE_END
diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h index 8e847adc..cfd938f8 100644 --- a/gui/ice40/mainwindow.h +++ b/gui/ice40/mainwindow.h @@ -30,12 +30,13 @@ class MainWindow : public BaseMainWindow Q_OBJECT
public:
- explicit MainWindow(QWidget *parent = 0);
+ explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
virtual ~MainWindow();
public:
void createMenu();
-
+ void load_json(std::string filename, std::string pcf);
+ void load_pcf(std::string filename);
protected Q_SLOTS:
virtual void new_proj();
virtual void open_proj();
@@ -78,6 +79,7 @@ class MainWindow : public BaseMainWindow bool timing_driven;
ArchArgs chipArgs;
+ std::string preload_pcf;
};
NEXTPNR_NAMESPACE_END
diff --git a/gui/ice40/worker.cc b/gui/ice40/worker.cc index ab82b6bb..09093ec8 100644 --- a/gui/ice40/worker.cc +++ b/gui/ice40/worker.cc @@ -23,10 +23,7 @@ #include "design_utils.h" #include "jsonparse.h" #include "log.h" -#include "pack.h" #include "pcf.h" -#include "place_sa.h" -#include "route.h" #include "timing.h" NEXTPNR_NAMESPACE_BEGIN @@ -99,7 +96,7 @@ void Worker::pack() { Q_EMIT taskStarted(); try { - bool res = pack_design(ctx); + bool res = ctx->pack(); print_utilisation(ctx); Q_EMIT pack_finished(res); } catch (WorkerInterruptionRequested) { @@ -124,7 +121,7 @@ void Worker::place(bool timing_driven) Q_EMIT taskStarted(); try { ctx->timing_driven = timing_driven; - Q_EMIT place_finished(place_design_sa(ctx)); + Q_EMIT place_finished(ctx->place()); } catch (WorkerInterruptionRequested) { Q_EMIT taskCanceled(); } @@ -134,7 +131,7 @@ void Worker::route() { Q_EMIT taskStarted(); try { - Q_EMIT route_finished(route_design(ctx)); + Q_EMIT route_finished(ctx->route()); } catch (WorkerInterruptionRequested) { Q_EMIT taskCanceled(); } diff --git a/gui/infotab.h b/gui/infotab.h index 0116755e..41529973 100644 --- a/gui/infotab.h +++ b/gui/infotab.h @@ -33,9 +33,10 @@ class InfoTab : public QWidget public:
explicit InfoTab(QWidget *parent = 0);
void info(std::string str);
+ public Q_SLOTS:
+ void clearBuffer();
private Q_SLOTS:
void showContextMenu(const QPoint &pt);
- void clearBuffer();
private:
QPlainTextEdit *plainTextEdit;
diff --git a/gui/line_editor.cc b/gui/line_editor.cc index 9d9dac25..425f2876 100644 --- a/gui/line_editor.cc +++ b/gui/line_editor.cc @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> + * Copyright (C) 2018 Alex Tsui * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,10 +20,14 @@ #include "line_editor.h" #include <QKeyEvent> +#include <QToolTip> +#include "ColumnFormatter.h" +#include "Utils.h" +#include "pyinterpreter.h" NEXTPNR_NAMESPACE_BEGIN -LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0) +LineEditor::LineEditor(ParseHelper *helper, QWidget *parent) : QLineEdit(parent), index(0), parseHelper(helper) { setContextMenuPolicy(Qt::CustomContextMenu); QAction *clearAction = new QAction("Clear &history", this); @@ -38,10 +43,12 @@ LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0) void LineEditor::keyPressEvent(QKeyEvent *ev) { + if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { + QToolTip::hideText(); if (lines.empty()) return; - + printf("Key_Up\n"); if (ev->key() == Qt::Key_Up) index--; if (ev->key() == Qt::Key_Down) @@ -56,12 +63,21 @@ void LineEditor::keyPressEvent(QKeyEvent *ev) } setText(lines[index]); } else if (ev->key() == Qt::Key_Escape) { + QToolTip::hideText(); clear(); return; + } else if (ev->key() == Qt::Key_Tab) { + autocomplete(); + return; } + QToolTip::hideText(); + QLineEdit::keyPressEvent(ev); } +// This makes TAB work +bool LineEditor::focusNextPrevChild(bool next) { return false; } + void LineEditor::textInserted() { if (lines.empty() || lines.back() != text()) @@ -82,4 +98,34 @@ void LineEditor::clearHistory() clear(); } -NEXTPNR_NAMESPACE_END
\ No newline at end of file +void LineEditor::autocomplete() +{ + QString line = text(); + const std::list<std::string> &suggestions = pyinterpreter_suggest(line.toStdString()); + if (suggestions.size() == 1) { + line = suggestions.back().c_str(); + } else { + // try to complete to longest common prefix + std::string prefix = LongestCommonPrefix(suggestions.begin(), suggestions.end()); + if (prefix.size() > (size_t)line.size()) { + line = prefix.c_str(); + } else { + ColumnFormatter fmt; + fmt.setItems(suggestions.begin(), suggestions.end()); + fmt.format(width() / 5); + QString out = ""; + for (auto &it : fmt.formattedOutput()) { + if (!out.isEmpty()) + out += "\n"; + out += it.c_str(); + } + QToolTip::setFont(font()); + if (!out.trimmed().isEmpty()) + QToolTip::showText(mapToGlobal(QPoint(0, 0)), out); + } + } + // set up the next line on the console + setText(line); +} + +NEXTPNR_NAMESPACE_END diff --git a/gui/line_editor.h b/gui/line_editor.h index 91837182..a779072f 100644 --- a/gui/line_editor.h +++ b/gui/line_editor.h @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> + * Copyright (C) 2018 Alex Tsui * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +23,7 @@ #include <QLineEdit> #include <QMenu> +#include "ParseHelper.h" #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN @@ -31,7 +33,7 @@ class LineEditor : public QLineEdit Q_OBJECT public: - explicit LineEditor(QWidget *parent = 0); + explicit LineEditor(ParseHelper *helper, QWidget *parent = 0); private Q_SLOTS: void textInserted(); @@ -43,11 +45,14 @@ class LineEditor : public QLineEdit protected: void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; + bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE; + void autocomplete(); private: int index; QStringList lines; QMenu *contextMenu; + ParseHelper *parseHelper; }; NEXTPNR_NAMESPACE_END diff --git a/gui/pyconsole.cc b/gui/pyconsole.cc new file mode 100644 index 00000000..0ee393ce --- /dev/null +++ b/gui/pyconsole.cc @@ -0,0 +1,79 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> + * Copyright (C) 2018 Alex Tsui + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "pyconsole.h" +#include "pyinterpreter.h" + +NEXTPNR_NAMESPACE_BEGIN + +const QColor PythonConsole::NORMAL_COLOR = QColor::fromRgbF(0, 0, 0); +const QColor PythonConsole::ERROR_COLOR = QColor::fromRgbF(1.0, 0, 0); +const QColor PythonConsole::OUTPUT_COLOR = QColor::fromRgbF(0, 0, 1.0); + +PythonConsole::PythonConsole(QWidget *parent) : QTextEdit(parent) {} + +void PythonConsole::parseEvent(const ParseMessage &message) +{ + // handle invalid user input + if (message.errorCode) { + setTextColor(ERROR_COLOR); + append(message.message.c_str()); + + setTextColor(NORMAL_COLOR); + append(""); + return; + } + // interpret valid user input + int errorCode = 0; + std::string res; + if (message.message.size()) + res = pyinterpreter_execute(message.message, &errorCode); + if (errorCode) { + setTextColor(ERROR_COLOR); + } else { + setTextColor(OUTPUT_COLOR); + } + + if (res.size()) { + append(res.c_str()); + } + setTextColor(NORMAL_COLOR); + append(""); + moveCursorToEnd(); +} + +void PythonConsole::displayString(QString text) +{ + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::End); + setTextColor(NORMAL_COLOR); + cursor.insertText(text); + cursor.movePosition(QTextCursor::EndOfLine); + moveCursorToEnd(); +} + +void PythonConsole::moveCursorToEnd() +{ + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::End); + setTextCursor(cursor); +} + +NEXTPNR_NAMESPACE_END diff --git a/gui/pyconsole.h b/gui/pyconsole.h new file mode 100644 index 00000000..9dbd3b95 --- /dev/null +++ b/gui/pyconsole.h @@ -0,0 +1,55 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> + * Copyright (C) 2018 Alex Tsui + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef PYCONSOLE_H +#define PYCONSOLE_H + +#include <QColor> +#include <QMimeData> +#include <QTextEdit> +#include "ParseHelper.h" +#include "ParseListener.h" +#include "nextpnr.h" + +class QWidget; +class QKeyEvent; + +NEXTPNR_NAMESPACE_BEGIN + +class PythonConsole : public QTextEdit, public ParseListener +{ + Q_OBJECT + + public: + PythonConsole(QWidget *parent = 0); + + void displayString(QString text); + void moveCursorToEnd(); + virtual void parseEvent(const ParseMessage &message); + + protected: + static const QColor NORMAL_COLOR; + static const QColor ERROR_COLOR; + static const QColor OUTPUT_COLOR; +}; + +NEXTPNR_NAMESPACE_END + +#endif // PYCONSOLE_H diff --git a/gui/pythontab.cc b/gui/pythontab.cc index 897f87b3..e761128d 100644 --- a/gui/pythontab.cc +++ b/gui/pythontab.cc @@ -16,7 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
-#ifndef NO_PYTHON
#include "pythontab.h"
#include <QGridLayout>
@@ -25,12 +24,20 @@ NEXTPNR_NAMESPACE_BEGIN
+const QString PythonTab::PROMPT = ">>> ";
+const QString PythonTab::MULTILINE_PROMPT = "... ";
+
PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
{
+ QFont f("unexistent");
+ f.setStyleHint(QFont::Monospace);
+
// Add text area for Python output and input line
console = new PythonConsole();
console->setMinimumHeight(100);
- console->setEnabled(false);
+ console->setReadOnly(true);
+ console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+ console->setFont(f);
console->setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &buffer", this);
@@ -41,9 +48,21 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false) contextMenu->addAction(clearAction);
connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
+ lineEdit = new LineEditor(&parseHelper);
+ lineEdit->setMinimumHeight(30);
+ lineEdit->setMaximumHeight(30);
+ lineEdit->setFont(f);
+ lineEdit->setPlaceholderText(PythonTab::PROMPT);
+ connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
+
QGridLayout *mainLayout = new QGridLayout();
mainLayout->addWidget(console, 0, 0);
+ mainLayout->addWidget(lineEdit, 1, 0);
setLayout(mainLayout);
+
+ parseHelper.subscribe(console);
+
+ prompt = PythonTab::PROMPT;
}
PythonTab::~PythonTab()
@@ -54,13 +73,26 @@ PythonTab::~PythonTab() }
}
+void PythonTab::editLineReturnPressed(QString text)
+{
+ console->displayString(prompt + text + "\n");
+
+ parseHelper.process(text.toStdString());
+
+ if (parseHelper.buffered())
+ prompt = PythonTab::MULTILINE_PROMPT;
+ else
+ prompt = PythonTab::PROMPT;
+
+ lineEdit->setPlaceholderText(prompt);
+}
+
void PythonTab::newContext(Context *ctx)
{
if (initialized) {
pyinterpreter_finalize();
deinit_python();
}
- console->setEnabled(true);
console->clear();
pyinterpreter_preinit();
@@ -74,13 +106,12 @@ void PythonTab::newContext(Context *ctx) QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform());
console->displayString(version);
- console->displayPrompt();
}
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
void PythonTab::clearBuffer() { console->clear(); }
-NEXTPNR_NAMESPACE_END
+void PythonTab::info(std::string str) { console->displayString(str.c_str()); }
-#endif
\ No newline at end of file +NEXTPNR_NAMESPACE_END
diff --git a/gui/pythontab.h b/gui/pythontab.h index 4b22e6a9..134874b6 100644 --- a/gui/pythontab.h +++ b/gui/pythontab.h @@ -20,11 +20,10 @@ #ifndef PYTHONTAB_H
#define PYTHONTAB_H
-#ifndef NO_PYTHON
-
#include <QLineEdit>
#include <QMenu>
#include <QPlainTextEdit>
+#include "ParseHelper.h"
#include "line_editor.h"
#include "nextpnr.h"
#include "pyconsole.h"
@@ -41,17 +40,24 @@ class PythonTab : public QWidget private Q_SLOTS:
void showContextMenu(const QPoint &pt);
- void clearBuffer();
+ void editLineReturnPressed(QString text);
public Q_SLOTS:
void newContext(Context *ctx);
+ void info(std::string str);
+ void clearBuffer();
private:
PythonConsole *console;
+ LineEditor *lineEdit;
QMenu *contextMenu;
bool initialized;
+ ParseHelper parseHelper;
+ QString prompt;
+
+ static const QString PROMPT;
+ static const QString MULTILINE_PROMPT;
};
NEXTPNR_NAMESPACE_END
-#endif // NO_PYTHON
#endif // PYTHONTAB_H
diff --git a/gui/resources/resultset_first.png b/gui/resources/resultset_first.png Binary files differnew file mode 100644 index 00000000..b03eaf8b --- /dev/null +++ b/gui/resources/resultset_first.png diff --git a/gui/resources/resultset_last.png b/gui/resources/resultset_last.png Binary files differnew file mode 100644 index 00000000..8ec89478 --- /dev/null +++ b/gui/resources/resultset_last.png diff --git a/gui/resources/resultset_next.png b/gui/resources/resultset_next.png Binary files differnew file mode 100644 index 00000000..e252606d --- /dev/null +++ b/gui/resources/resultset_next.png diff --git a/gui/resources/resultset_previous.png b/gui/resources/resultset_previous.png Binary files differnew file mode 100644 index 00000000..18f9cc10 --- /dev/null +++ b/gui/resources/resultset_previous.png diff --git a/gui/resources/splash.png b/gui/resources/splash.png Binary files differnew file mode 100644 index 00000000..14d2842b --- /dev/null +++ b/gui/resources/splash.png diff --git a/gui/resources/zoom.png b/gui/resources/zoom.png Binary files differnew file mode 100644 index 00000000..908612e3 --- /dev/null +++ b/gui/resources/zoom.png |