# HG changeset patch # User Teemu Piippo # Date 1647175994 -7200 # Node ID 7b9b85b459de64a7d046be4b5cb79a7da67a841c # Parent a23024fc98e0f54e1d6f4c4486dc301ffdc3307b# Parent 41628bdab71c3618fcf04ea7570731605342e458 merge diff -r 41628bdab71c -r 7b9b85b459de CMakeLists.txt --- a/CMakeLists.txt Fri Mar 11 20:19:26 2022 +0200 +++ b/CMakeLists.txt Sun Mar 13 14:53:14 2022 +0200 @@ -54,7 +54,6 @@ src/gl/gridprogram.cpp src/gl/partrenderer.cpp src/gl/vertexprogram.cpp - src/linetypes/comment.cpp src/linetypes/conditionaledge.cpp src/linetypes/edge.cpp src/linetypes/errorline.cpp @@ -72,6 +71,7 @@ src/ui/objecteditor.cpp src/ui/polygonobjecteditor.cpp src/widgets/colorbutton.cpp + src/widgets/colorindexinput.cpp src/widgets/colorselectdialog.cpp src/widgets/doublespinbox.cpp src/widgets/matrixeditor.cpp @@ -112,7 +112,6 @@ src/gl/gridprogram.h src/gl/partrenderer.h src/gl/vertexprogram.h - src/linetypes/comment.h src/linetypes/conditionaledge.h src/linetypes/edge.h src/linetypes/errorline.h @@ -132,6 +131,7 @@ src/ui/objecteditor.h src/ui/polygonobjecteditor.h src/widgets/colorbutton.h + src/widgets/colorindexinput.h src/widgets/colorselectdialog.h src/widgets/doublespinbox.h src/widgets/matrixeditor.h @@ -148,6 +148,7 @@ src/settingseditor/settingseditor.ui src/ui/multiplyfactordialog.ui src/widgets/colorselectdialog.ui + src/widgets/colorindexinput.ui src/widgets/matrixeditor.ui src/widgets/vec3editor.ui ) diff -r 41628bdab71c -r 7b9b85b459de src/colors.cpp --- a/src/colors.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/colors.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -178,6 +178,19 @@ } } +QColor ldraw::colorEdge(ldraw::Color color, const ldraw::ColorTable& colorTable) +{ + if (isDirectColor(color)) + { + QColor const faceColor = directColorFace(color); + return (luma(faceColor) < 0.4) ? Qt::white : Qt::black; + } + else + { + return colorTable[color].faceColor; + } +} + /** * @brief Writes a color index into a @c QDataStream * @param stream @@ -199,3 +212,15 @@ { return stream >> color.index; } + +QString ldraw::colorDisplayName(ldraw::Color color, const ldraw::ColorTable &colorTable) +{ + if (isDirectColor(color)) + { + return directColorFace(color).name(); + } + else + { + return colorTable[color].displayName; + } +} diff -r 41628bdab71c -r 7b9b85b459de src/colors.h --- a/src/colors.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/colors.h Sun Mar 13 14:53:14 2022 +0200 @@ -29,6 +29,8 @@ bool isDirectColor(Color color); QColor directColorFace(Color color); QColor colorFace(Color color, const ColorTable& colorTable); + QColor colorEdge(Color color, const ColorTable& colorTable); + QString colorDisplayName(Color color, const ColorTable& colorTable); } /** diff -r 41628bdab71c -r 7b9b85b459de src/document.cpp --- a/src/document.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/document.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -33,9 +33,9 @@ const ldraw::ColorTable& colorTable, QWidget* parent) : QWidget{parent}, + colorTable{colorTable}, model{model}, documents{documents}, - colorTable{colorTable}, vertexMap{model}, renderer{new Canvas{model, documents, colorTable, this}}, ui{*new Ui::Document}, @@ -234,7 +234,7 @@ this->renderer->adjustGridToView(); } -const Model &Document::getModel() +const Model &Document::getModel() const { return *this->model; } diff -r 41628bdab71c -r 7b9b85b459de src/document.h --- a/src/document.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/document.h Sun Mar 13 14:53:14 2022 +0200 @@ -48,8 +48,9 @@ void applyToVertices(VertexMap::ApplyFunction fn) const; void handleKeyPress(QKeyEvent* event); void adjustGridToView(); - const Model& getModel(); + const Model& getModel() const; const QSet selectedObjects() const; + const ldraw::ColorTable& colorTable; Q_SIGNALS: void newStatusText(const QString& newStatusText); void splitterChanged(); @@ -62,7 +63,6 @@ void selectTool(class BaseTool* tool); Model* model; DocumentManager* const documents; - const ldraw::ColorTable& colorTable; VertexMap vertexMap; Canvas* renderer; Ui::Document& ui; diff -r 41628bdab71c -r 7b9b85b459de src/documentmanager.cpp --- a/src/documentmanager.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/documentmanager.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -22,7 +22,6 @@ #include #include "documentmanager.h" #include "modeleditor.h" -#include "linetypes/comment.h" #include "linetypes/subfilereference.h" #include "parser.h" diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/comment.cpp --- a/src/linetypes/comment.cpp Fri Mar 11 20:19:26 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#include -#include "comment.h" - -QFont ldraw::Comment::textRepresentationFont() const -{ - QFont font; - font.setItalic(true); - return font; -} - -ldraw::Object::Type ldraw::Comment::typeIdentifier() const -{ - return Type::Comment; -} - -QString ldraw::Comment::toLDrawCode() const -{ - return ("0 // " + this->storedText).trimmed(); -} - diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/comment.h --- a/src/linetypes/comment.h Fri Mar 11 20:19:26 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -#pragma once -#include "object.h" -#include "metacommand.h" - -namespace ldraw -{ - class Comment; -} - -class ldraw::Comment : public MetaCommand -{ - using MetaCommand::MetaCommand; - QFont textRepresentationFont() const override; - Type typeIdentifier() const override; - QString toLDrawCode() const override; -}; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/conditionaledge.cpp --- a/src/linetypes/conditionaledge.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/conditionaledge.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -29,3 +29,8 @@ { return ":/icons/linetype-conditionaledge.png"; } + +QString ldraw::ConditionalEdge::typeName() const +{ + return QObject::tr("conditional edge"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/conditionaledge.h --- a/src/linetypes/conditionaledge.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/conditionaledge.h Sun Mar 13 14:53:14 2022 +0200 @@ -14,4 +14,5 @@ Type typeIdentifier() const override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; }; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/edge.cpp --- a/src/linetypes/edge.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/edge.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -34,3 +34,8 @@ { return ":/icons/linetype-edgeline.png"; } + +QString ldraw::Edge::typeName() const +{ + return QObject::tr("edge"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/edge.h --- a/src/linetypes/edge.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/edge.h Sun Mar 13 14:53:14 2022 +0200 @@ -15,4 +15,5 @@ Type typeIdentifier() const override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; }; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/errorline.cpp --- a/src/linetypes/errorline.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/errorline.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -66,3 +66,8 @@ { return ":/icons/linetype-errorline.png"; } + +QString ldraw::ErrorLine::typeName() const +{ + return QObject::tr("error line"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/errorline.h --- a/src/linetypes/errorline.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/errorline.h Sun Mar 13 14:53:14 2022 +0200 @@ -20,6 +20,7 @@ QDataStream& deserialize(QDataStream& stream) override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; QString text; QString message; protected: diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/metacommand.cpp --- a/src/linetypes/metacommand.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/metacommand.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -61,3 +61,8 @@ return ":/icons/chatbubble-ellipses-outline.png"; } +QString ldraw::MetaCommand::typeName() const +{ + return QObject::tr("comment"); +} + diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/metacommand.h --- a/src/linetypes/metacommand.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/metacommand.h Sun Mar 13 14:53:14 2022 +0200 @@ -19,6 +19,7 @@ QDataStream& deserialize(QDataStream& stream) override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; protected: void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; }; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/object.cpp --- a/src/linetypes/object.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/object.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -161,3 +161,8 @@ { return ""; } + +QString ldraw::Empty::typeName() const +{ + return QObject::tr("empty"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/object.h --- a/src/linetypes/object.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/object.h Sun Mar 13 14:53:14 2022 +0200 @@ -80,6 +80,7 @@ virtual Type typeIdentifier() const = 0; virtual QString toLDrawCode() const = 0; virtual QString iconName() const; + virtual QString typeName() const = 0; protected: template @@ -146,4 +147,5 @@ QString textRepresentation() const override; Type typeIdentifier() const override; QString toLDrawCode() const override; + QString typeName() const override; }; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/quadrilateral.cpp --- a/src/linetypes/quadrilateral.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/quadrilateral.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -50,3 +50,8 @@ { return ":/icons/linetype-quadrilateral.png"; } + +QString ldraw::Quadrilateral::typeName() const +{ + return QObject::tr("quadrilateral"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/quadrilateral.h --- a/src/linetypes/quadrilateral.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/quadrilateral.h Sun Mar 13 14:53:14 2022 +0200 @@ -16,4 +16,5 @@ Type typeIdentifier() const override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; }; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/subfilereference.cpp --- a/src/linetypes/subfilereference.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/subfilereference.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -143,3 +143,8 @@ { return ":/icons/linetype-subfile.png"; } + +QString ldraw::SubfileReference::typeName() const +{ + return QObject::tr("subfile reference"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/subfilereference.h --- a/src/linetypes/subfilereference.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/subfilereference.h Sun Mar 13 14:53:14 2022 +0200 @@ -27,6 +27,7 @@ QDataStream& deserialize(QDataStream& stream) override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; glm::mat4 transformation; QString referenceName; bool isInverted = false; diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/triangle.cpp --- a/src/linetypes/triangle.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/triangle.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -47,3 +47,8 @@ { return ":/icons/linetype-triangle.png"; } + +QString ldraw::Triangle::typeName() const +{ + return QObject::tr("triangle"); +} diff -r 41628bdab71c -r 7b9b85b459de src/linetypes/triangle.h --- a/src/linetypes/triangle.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/linetypes/triangle.h Sun Mar 13 14:53:14 2022 +0200 @@ -16,5 +16,6 @@ Type typeIdentifier() const override; QString toLDrawCode() const override; QString iconName() const override; + QString typeName() const override; }; diff -r 41628bdab71c -r 7b9b85b459de src/mainwindow.cpp --- a/src/mainwindow.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/mainwindow.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -224,6 +224,11 @@ return qobject_cast(this->ui->tabs->currentWidget()); } +const Document* MainWindow::currentDocument() const +{ + return qobject_cast(this->ui->tabs->currentWidget()); +} + void MainWindow::handleDocumentSplitterChange() { Document* currentDocument = this->currentDocument(); @@ -285,24 +290,27 @@ { if (this->currentDocument() != nullptr) { - const ModelId modelId = {0}; - const QString* path = this->documents.modelPath(modelId); - if (path == nullptr or path->isEmpty()) + const std::optional modelId = this->findCurrentModelId(); + if (modelId.has_value()) { - this->actionSaveAs(); - } - else - { - QString error; - QTextStream errorStream{&error}; - const bool succeeded = this->documents.saveModel(modelId, errorStream); - if (not succeeded) + const QString* path = this->documents.modelPath(*modelId); + if (path == nullptr or path->isEmpty()) { - QMessageBox::critical(this, tr("Save error"), error); + this->actionSaveAs(); } else { - this->addRecentlyOpenedFile(*path); + QString error; + QTextStream errorStream{&error}; + const bool succeeded = this->documents.saveModel(*modelId, errorStream); + if (not succeeded) + { + QMessageBox::critical(this, tr("Save error"), error); + } + else + { + this->addRecentlyOpenedFile(*path); + } } } } @@ -315,22 +323,25 @@ { if (this->currentDocument() != nullptr) { - const ModelId modelId = {0}; - const QString* pathPtr = this->documents.modelPath(modelId); - QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; - const QString newPath = QFileDialog::getSaveFileName( - this, - tr("Save as…"), - QFileInfo{defaultPath}.absoluteDir().path(), - tr("LDraw files (*.ldr *dat);;All files (*)") - ); - if (not newPath.isEmpty()) + const std::optional modelId = this->findCurrentModelId(); + if (modelId.has_value()) { - QString error; - QTextStream errorStream{&error}; - this->documents.setModelPath(modelId, newPath, this->libraries, errorStream); - this->ui->tabs->setTabText(this->ui->tabs->currentIndex(), QFileInfo{newPath}.fileName()); - this->actionSave(); + const QString* pathPtr = this->documents.modelPath(*modelId); + QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; + const QString newPath = QFileDialog::getSaveFileName( + this, + tr("Save as…"), + QFileInfo{defaultPath}.absoluteDir().path(), + tr("LDraw files (*.ldr *dat);;All files (*)") + ); + if (not newPath.isEmpty()) + { + QString error; + QTextStream errorStream{&error}; + this->documents.setModelPath(*modelId, newPath, this->libraries, errorStream); + this->ui->tabs->setTabText(this->ui->tabs->currentIndex(), QFileInfo{newPath}.fileName()); + this->actionSave(); + } } } } @@ -397,6 +408,19 @@ } } +std::optional MainWindow::findCurrentModelId() const +{ + const Document* document = this->currentDocument(); + if (document != nullptr) + { + return this->documents.findIdForModel(&document->getModel()); + } + else + { + return {}; + } +} + void MainWindow::changeEvent(QEvent* event) { if (event != nullptr) diff -r 41628bdab71c -r 7b9b85b459de src/mainwindow.h --- a/src/mainwindow.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/mainwindow.h Sun Mar 13 14:53:14 2022 +0200 @@ -78,5 +78,7 @@ static QString pathToTranslation(const QString& localeCode); void loadColors(); Document *currentDocument(); + const Document *currentDocument() const; void closeDocument(Document* document); + std::optional findCurrentModelId() const; }; diff -r 41628bdab71c -r 7b9b85b459de src/parser.cpp --- a/src/parser.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/parser.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -18,7 +18,6 @@ #include "model.h" #include "parser.h" -#include "linetypes/comment.h" #include "linetypes/conditionaledge.h" #include "linetypes/edge.h" #include "linetypes/errorline.h" @@ -48,187 +47,6 @@ return QString::fromUtf8(this->device.readLine()).trimmed(); } -/* - * Parses a single line of the header. - * Possible parse results: - * · ParseSuccess: the header line was parsed successfully. - * · ParseFailure: the header line was parsed incorrectly and needs to be handled otherwise. - * · StopParsing: the line does not belong in the header and header parsing needs to stop. - */ -Parser::HeaderParseResult Parser::parseHeaderLine( - LDHeader& header, - Winding& winding, - const QString& line -) { - if (line.isEmpty()) - { - return ParseSuccess; - } - else if (not line.startsWith("0") or line.startsWith("0 //")) - { - return StopParsing; - } - else if (line.startsWith("0 !LDRAW_ORG ")) - { - QStringList tokens = line - .mid(strlen("0 !LDRAW_ORG ")) - .split(" ", Qt::SkipEmptyParts); - if (not tokens.isEmpty()) - { - QString partTypeString = tokens[0]; - // Anything that enters LDForge becomes unofficial in any case if saved. - // Therefore we don't need to give the Unofficial type any special - // consideration. - if (partTypeString.startsWith("Unofficial_")) - partTypeString = partTypeString.mid(strlen("Unofficial_")); - header.type = headerTypeFromString(partTypeString); - header.qualfiers = {}; - if (tokens.contains("Alias")) - header.qualfiers |= LDHeader::Alias; - if (tokens.contains("Physical_Color")) - header.qualfiers |= LDHeader::PhysicalColour; - if (tokens.contains("Flexible_Section")) - header.qualfiers |= LDHeader::FlexibleSection; - return ParseSuccess; - } - else - { - return ParseFailure; - } - } - else if (line == "0 BFC CERTIFY CCW") - { - winding = Anticlockwise; - return ParseSuccess; - } - else if (line == "0 BFC CERTIFY CW") - { - winding = Clockwise; - return ParseSuccess; - } - else if (line == "0 BFC NOCERTIFY") - { - winding = NoWinding; - return ParseSuccess; - } - else if (line.startsWith("0 !HISTORY ")) - { - static const QRegExp historyRegexp { - R"(0 !HISTORY\s+(\d{4}-\d{2}-\d{2})\s+)" - R"((\{[^}]+|\[[^]]+)[\]}]\s+(.+))" - }; - if (historyRegexp.exactMatch(line)) - { - QString dateString = historyRegexp.capturedTexts().value(1); - QString authorWithPrefix = historyRegexp.capturedTexts().value(2); - QString description = historyRegexp.capturedTexts().value(3); - LDHeader::HistoryEntry historyEntry; - historyEntry.date = QDate::fromString(dateString, Qt::ISODate); - historyEntry.description = description; - - if (authorWithPrefix[0] == '{') - historyEntry.author = authorWithPrefix + "}"; - else - historyEntry.author = authorWithPrefix.mid(1); - - header.history.append(historyEntry); - return ParseSuccess; - } - else - { - return ParseFailure; - } - } - else if (line.startsWith("0 Author: ")) - { - header.author = line.mid(strlen("0 Author: ")); - return ParseSuccess; - } - else if (line.startsWith("0 Name: ")) - { - header.name = line.mid(strlen("0 Name: ")); - return ParseSuccess; - } - else if (line.startsWith("0 !HELP ")) - { - if (not header.help.isEmpty()) - header.help += "\n"; - header.help += line.mid(strlen("0 !HELP ")); - return ParseSuccess; - } - else if (line.startsWith("0 !KEYWORDS ")) - { - if (not header.keywords.isEmpty()) - header.keywords += "\n"; - header.keywords += line.mid(strlen("0 !KEYWORDS ")); - return ParseSuccess; - } - else if (line.startsWith("0 !CATEGORY ")) - { - header.category = line.mid(strlen("0 !CATEGORY ")); - return ParseSuccess; - } - else if (line.startsWith("0 !CMDLINE ")) - { - header.cmdline = line.mid(strlen("0 !CMDLINE ")); - return ParseSuccess; - } - else if (line.startsWith(LDHeader::caLicenseString)) - { - header.license = LDHeader::CaLicense; - return ParseSuccess; - } - else if (line.startsWith(LDHeader::nonCaLicenseString)) - { - header.license = LDHeader::NonCaLicense; - return ParseSuccess; - } - else - { - return ParseFailure; - } -} - -/* - * Parses the header from the device given at construction and returns - * the resulting header structure. - */ -LDHeader Parser::parseHeader(Winding& winding) -{ - LDHeader header = {}; - if (not this->device.atEnd()) - { - // Parse the description - QString descriptionLine = this->readLine(); - if (descriptionLine.startsWith("0 ")) - { - header.description = descriptionLine.mid(strlen("0 ")).trimmed(); - // Parse the rest of the header - while (not this->device.atEnd()) - { - const QString& line = this->readLine(); - auto result = parseHeaderLine(header, winding, line); - if (result == ParseFailure) - { - // Failed to parse this header line, add it as a comment into the body later. - this->bag.append(line); - } - else if (result == StopParsing) - { - // Header parsing stops, add this line to the body. - this->bag.append(line); - break; - } - } - } - else - { - this->bag.append(descriptionLine); - } - } - return header; -} - /** * @brief Parses the model body into the given model. * @param editor Handle to model edit context @@ -237,42 +55,22 @@ { bool invertNext = false; while (not this->device.atEnd()) - this->bag.append(this->readLine()); - for (const QString& line : this->bag) { // Some LDraw parts such as 53588.dat can contain "BFC INVERTNEXT" with multiple inner whitespaces. // So we need to pass the string through QString::simplified to catch these cases. - const QString simplifiedLine = line.simplified(); - if (simplifiedLine == "0 BFC INVERTNEXT" or simplifiedLine == "0 BFC CERTIFY INVERTNEXT") + const QString line = this->readLine().simplified(); + if (line == "0 BFC INVERTNEXT" or line == "0 BFC CERTIFY INVERTNEXT") { invertNext = true; continue; } - std::unique_ptr object = parseFromString(line); - model.append(std::move(object)); + model.append(parseFromString(line)); if (invertNext) { model[model.size() - 1]->invert(); } invertNext = false; } - /* - // Test quadrilateral splitting by splitting all the quadrilaterals - QVector quadrilateral_ids; - for (int i = 0; i < editor.model().size(); i += 1) - { - const ldraw::id_t id = editor.model().resolve(editor.model().index(i)); - const ldraw::quadrilateralid_t quad_id = editor.model().checkType(id); - if (not(quad_id == ldraw::NULL_ID)) - { - quadrilateral_ids.push_back(quad_id); - } - } - for (const ldraw::quadrilateralid_t id : quadrilateral_ids) - { - ldraw::splitQuadrilateral(editor, id); - } - */ } static ldraw::Color colorFromString(const QString& colorString) @@ -347,15 +145,7 @@ const QStringList& tokens) { Q_UNUSED(tokens) - if (line.startsWith("0 //")) - { - // lol wut - return std::make_unique(line.mid(std::strlen("0 //")).trimmed()); - } - else - { - return std::make_unique(line.mid(1).trimmed()); - } + return std::make_unique(line.mid(1).trimmed()); } static std::unique_ptr parseType1Line( diff -r 41628bdab71c -r 7b9b85b459de src/parser.h --- a/src/parser.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/parser.h Sun Mar 13 14:53:14 2022 +0200 @@ -29,13 +29,9 @@ public: enum { EndOfModel = -1 }; Parser(QIODevice& device, QObject* parent = nullptr); - LDHeader parseHeader(Winding& winding); void parseBody(Model &model); static std::unique_ptr parseFromString(QString line); private: - enum HeaderParseResult {ParseSuccess, ParseFailure, StopParsing}; QString readLine(); - HeaderParseResult parseHeaderLine(LDHeader& header, Winding& winding, const QString& line); QIODevice& device; - QStringList bag; }; diff -r 41628bdab71c -r 7b9b85b459de src/ui/objecteditor.cpp --- a/src/ui/objecteditor.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/ui/objecteditor.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -1,33 +1,108 @@ #include +#include +#include +#include +#include #include "objecteditor.h" #include "document.h" +#include "modeleditor.h" +#include "widgets/colorbutton.h" +#include "widgets/colorindexinput.h" + +template +static void makeColorEditor() +{ + QString propertyName = ldraw::PropertyTraits::name; +} ObjectEditor::ObjectEditor(Document* document, const ldraw::id_t id) : QWidget{document}, - document{document} + document{document}, + formContainer{new QWidget{this}}, + objectTypeNameLabel{new QLabel{this}}, + objectTypeIconLabel{new QLabel{this}} { + this->polygonEditor.emplace(this->document, id); + this->setLayout(new QVBoxLayout{this}); + QWidget* objectTitleLayoutContainer = new QWidget{this}; + QLayout* objectTitleLayout = new QHBoxLayout{objectTitleLayoutContainer}; + objectTitleLayoutContainer->setLayout(objectTitleLayout); + objectTitleLayout->addWidget(this->objectTypeIconLabel); + objectTitleLayout->addWidget(this->objectTypeNameLabel); + objectTitleLayout->addWidget(new QSplitter{Qt::Horizontal, this}); + this->layout()->addWidget(objectTitleLayoutContainer); + this->layout()->addWidget(&*this->polygonEditor); + this->layout()->addWidget(formContainer); this->setObjectId(id); - this->setLayout(new QVBoxLayout{this}); + + QWidget* const parent = this->formContainer; + QFormLayout* formLayout = new QFormLayout{parent}; + this->formContainer->setLayout(formLayout); + QLabel* colorLabel = new QLabel{"Color", parent}; + ColorIndexInput* colorWidget = new ColorIndexInput{document, {0}, parent}; + formLayout->addRow(colorLabel, colorWidget); + connect(colorWidget, &ColorIndexInput::colorChanged, [this](ldraw::Color color) + { + const QModelIndex index = this->document->getModel().find(this->objectId); + if (index.isValid()) + { + this->document->editModel()->modifyObjectAt( + index.row(), + [color](ldraw::ColoredObject* object) + { + object->colorIndex = color; + } + ); + } + }); + this->propertyWidgets[ldraw::Property::Color] = {colorLabel, colorWidget}; +} + +QString titleCase(const QString& string) +{ + return string.left(1).toUpper() + string.mid(1); } void ObjectEditor::setObjectId(const ldraw::id_t id) { this->objectId = id; const ldraw::Object* object = this->document->getModel().get(id); - if (object != nullptr and object->numPoints() > 0) + this->clear(); + if (object != nullptr) { - if (not this->polygonEditor.has_value()) + this->objectTypeNameLabel->setText("" + titleCase(object->typeName()) + ""); + this->objectTypeIconLabel->setPixmap(QPixmap{object->iconName()}.scaledToWidth(24)); + if (object->numPoints() > 0) { - this->polygonEditor.emplace(this->document, id); - this->layout()->addWidget(&*this->polygonEditor); + this->polygonEditor->setObjectId(id); } else { - this->polygonEditor->setObjectId(id); + this->polygonEditor->clear(); + } + constexpr ldraw::Property prop = ldraw::Property::Color; + QVariant color = object->getProperty(prop); + this->propertyWidgets[prop].first->setVisible(not color.isNull()); + this->propertyWidgets[prop].second->setVisible(not color.isNull()); + if (not color.isNull()) + { + static_cast(this->propertyWidgets[prop].second)->setSelectedColor(color.value()); } } else { - this->polygonEditor.reset(); + for (auto& pair : this->propertyWidgets) + { + pair.first->setVisible(false); + pair.second->setVisible(false); + } } } + +void ObjectEditor::clear() +{ + this->objectTypeNameLabel->clear(); + this->objectTypeIconLabel->clear(); + this->polygonEditor->clear(); + delete this->formContainer->layout(); +} diff -r 41628bdab71c -r 7b9b85b459de src/ui/objecteditor.h --- a/src/ui/objecteditor.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/ui/objecteditor.h Sun Mar 13 14:53:14 2022 +0200 @@ -12,8 +12,13 @@ public: explicit ObjectEditor(Document* document, ldraw::id_t id = ldraw::NULL_ID); void setObjectId(ldraw::id_t id); + void clear(); private: Document* const document; ldraw::id_t objectId = ldraw::NULL_ID; std::optional polygonEditor; + QWidget* formContainer; + class QLabel* objectTypeNameLabel; + class QLabel* objectTypeIconLabel; + QMap> propertyWidgets; }; diff -r 41628bdab71c -r 7b9b85b459de src/ui/polygonobjecteditor.cpp --- a/src/ui/polygonobjecteditor.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/ui/polygonobjecteditor.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -14,6 +14,7 @@ storedObjectId{ldraw::NULL_ID.value} { this->splitter.emplace(Qt::Vertical, this); + this->buildWidgets(); this->setObjectId(id); } @@ -30,44 +31,60 @@ void PolygonObjectEditor::setObjectId(ldraw::id_t id) { this->storedObjectId = id; - this->buildWidgets(); + const QModelIndex index = this->document->getModel().find(this->objectId()); + if (index.isValid()) + { + for (int n : {0, 1, 2, 3}) + { + const ldraw::Object* const object = this->document->getModel()[index.row()]; + const ldraw::Property property = ldraw::pointProperty(n); + const QVariant value = object->getProperty(property); + this->widgets[n]->setVisible(value.isValid()); + this->formLayout->itemAt(n, QFormLayout::LabelRole)->widget()->setVisible(value.isValid()); + if (value.isValid()) + { + this->widgets[n]->setValue(value.value()); + } + } + } + else + { + for (int n : {0, 1, 2, 3}) + { + this->widgets[n]->setVisible(false); + this->formLayout->itemAt(n, QFormLayout::LabelRole)->widget()->setVisible(false); + } + } +} + +void PolygonObjectEditor::clear() +{ + this->setObjectId(ldraw::NULL_ID); } void PolygonObjectEditor::buildWidgets() { - this->widgets.clear(); - delete this->layout(); - QFormLayout* layout = new QFormLayout{this}; - this->setLayout(layout); + this->formLayout = new QFormLayout{this}; + this->setLayout(this->formLayout); for (int n : {0, 1, 2, 3}) { this->setupPointWidget(n); } - for (std::unique_ptr& widget : this->widgets) + for (std::unique_ptr& widget : this->widgets) { const QString label = widget->property(LABEL_NAME).toString(); - layout->addRow(label, widget.get()); + this->formLayout->addRow(label, widget.get()); } - layout->addRow("", &*this->splitter); + this->formLayout->addRow("", &*this->splitter); } void PolygonObjectEditor::setupPointWidget(int n) { - const QModelIndex index = this->document->getModel().find(this->objectId()); - if (index.isValid()) - { - const ldraw::Object* const object = this->document->getModel()[index.row()]; - const ldraw::Property property = ldraw::pointProperty(n); - const QVariant value = object->getProperty(property); - if (value.isValid()) - { - std::unique_ptr editor = std::make_unique(value.value(), this); - QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged); - editor->setProperty(INDEX_NAME, QVariant::fromValue(n)); - editor->setProperty(LABEL_NAME, &ldraw::traits(property).name[0]); - this->widgets.push_back(std::move(editor)); - } - } + std::unique_ptr editor = std::make_unique(glm::vec3{}, this); + QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged); + editor->setProperty(INDEX_NAME, QVariant::fromValue(n)); + editor->setProperty(LABEL_NAME, &ldraw::traits(ldraw::pointProperty(n)).name[0]); + this->widgets.push_back(std::move(editor)); } void PolygonObjectEditor::pointChanged(const glm::vec3& value) diff -r 41628bdab71c -r 7b9b85b459de src/ui/polygonobjecteditor.h --- a/src/ui/polygonobjecteditor.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/ui/polygonobjecteditor.h Sun Mar 13 14:53:14 2022 +0200 @@ -13,12 +13,14 @@ ~PolygonObjectEditor(); ldraw::id_t objectId() const; void setObjectId(ldraw::id_t id); + void clear(); private: void buildWidgets(); void setupPointWidget(int n); Q_SLOT void pointChanged(const glm::vec3& value); Document* document; ldraw::id_t storedObjectId; - std::vector> widgets; + std::vector> widgets; std::optional splitter; + class QFormLayout* formLayout = nullptr; }; diff -r 41628bdab71c -r 7b9b85b459de src/uiutilities.cpp --- a/src/uiutilities.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/uiutilities.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include "uiutilities.h" QVector uiutilities::collectActions(QObject* subject) @@ -42,3 +43,12 @@ } return result; } + +void uiutilities::colorizeWidget(QWidget* widget, const QColor& color) +{ + QPalette pal{color}; + widget->setPalette(pal); + widget->setAutoFillBackground(true); + widget->setStyle(QStyleFactory::create("Fusion")); + widget->update(); +} \ No newline at end of file diff -r 41628bdab71c -r 7b9b85b459de src/uiutilities.h --- a/src/uiutilities.h Fri Mar 11 20:19:26 2022 +0200 +++ b/src/uiutilities.h Sun Mar 13 14:53:14 2022 +0200 @@ -24,4 +24,5 @@ using KeySequenceMap = QMap; QVector collectActions(QObject* subject); KeySequenceMap makeKeySequenceMap(const QVector& actions); + void colorizeWidget(QWidget* widget, const QColor& color); } diff -r 41628bdab71c -r 7b9b85b459de src/widgets/colorindexinput.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/colorindexinput.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -0,0 +1,43 @@ +#include "colorindexinput.h" +#include "ui_colorindexinput.h" +#include "colorselectdialog.h" +#include "uiutilities.h" + +ColorIndexInput::ColorIndexInput(Document *document, ldraw::Color color, QWidget *parent) : + QWidget{parent}, + document{document}, + ui{*new Ui_ColorIndexInput} +{ + this->ui.setupUi(this); + connect(this->ui.button, &QPushButton::clicked, [this]() + { + ColorSelectDialog dialog{this->document->colorTable, this->document}; + const int result = dialog.exec(); + if (result == QDialog::Accepted) + { + this->ui.index->setValue(dialog.currentColor().index); + } + }); + connect(this->ui.index, qOverload(&QSpinBox::valueChanged), [this](int value) + { + this->ui.button->setText(ldraw::colorDisplayName({value}, this->document->colorTable)); + uiutilities::colorizeWidget(this->ui.button, ldraw::colorFace({value}, this->document->colorTable)); + Q_EMIT this->colorChanged({value}); + }); + this->ui.index->setValue(color.index); +} + +ColorIndexInput::~ColorIndexInput() +{ + delete &this->ui; +} + +ldraw::Color ColorIndexInput::selectedColor() const +{ + return {this->ui.index->value()}; +} + +void ColorIndexInput::setSelectedColor(ldraw::Color color) +{ + this->ui.index->setValue(color.index); +} diff -r 41628bdab71c -r 7b9b85b459de src/widgets/colorindexinput.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/colorindexinput.h Sun Mar 13 14:53:14 2022 +0200 @@ -0,0 +1,17 @@ +#pragma once +#include "document.h" + +class ColorIndexInput : public QWidget +{ + Q_OBJECT +public: + ColorIndexInput(Document *document, ldraw::Color color = ldraw::MAIN_COLOR, QWidget *parent = nullptr); + ~ColorIndexInput(); + ldraw::Color selectedColor() const; + void setSelectedColor(ldraw::Color color); +Q_SIGNALS: + void colorChanged(ldraw::Color color); +private: + Document* const document; + class Ui_ColorIndexInput& ui; +}; diff -r 41628bdab71c -r 7b9b85b459de src/widgets/colorindexinput.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/colorindexinput.ui Sun Mar 13 14:53:14 2022 +0200 @@ -0,0 +1,51 @@ + + + ColorIndexInput + + + + 0 + 0 + 370 + 47 + + + + Form + + + + + + + + + 2147483647 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff -r 41628bdab71c -r 7b9b85b459de src/widgets/colorselectdialog.cpp --- a/src/widgets/colorselectdialog.cpp Fri Mar 11 20:19:26 2022 +0200 +++ b/src/widgets/colorselectdialog.cpp Sun Mar 13 14:53:14 2022 +0200 @@ -1,7 +1,9 @@ #include #include +#include #include "colorselectdialog.h" #include "ui_colorselectdialog.h" +#include "uiutilities.h" ColorSelectDialog::ColorSelectDialog(const ldraw::ColorTable& colorTable, QWidget *parent) : QDialog{parent}, @@ -29,14 +31,6 @@ return {button->property("_colorIndex").value()}; } -QString styleSheetForColor(const QColor& color) -{ - QColor const textColor = (luma(color) < 0.4) ? Qt::white : Qt::black; - return QString{"background-color: %1; color: %2;"} - .arg(color.name()) - .arg(textColor.name()); -} - void ColorSelectDialog::makeColorButtons() { this->buttons.reserve(this->colorTable.size()); @@ -48,8 +42,9 @@ const qint32 index = iterator->first; const ldraw::ColorDefinition& colordef = iterator->second; QPushButton* const button = new QPushButton{QString::number(index), this}; + button->setMinimumSize({40, 40}); button->setToolTip(colordef.displayName); - button->setStyleSheet(styleSheetForColor(colordef.faceColor)); + uiutilities::colorizeWidget(button, ldraw::colorFace({index}, colorTable)); button->setProperty("_colorIndex", index); button->setCheckable(true); connect(button, &QAbstractButton::clicked, this, &ColorSelectDialog::handleButtonClick); @@ -95,17 +90,8 @@ void ColorSelectDialog::updateSelectedColorTexts() { - if (ldraw::isDirectColor(this->selectedColor)) - { - this->ui.selectedColorName->setText(ldraw::directColorFace(this->selectedColor).name()); - } - else - { - const ldraw::ColorDefinition& colordef = this->colorTable[this->selectedColor]; - this->ui.selectedColorName->setText(colordef.displayName); - } - const QColor colorFace = ldraw::colorFace(this->selectedColor, this->colorTable); - this->ui.selectedColorName->setStyleSheet(styleSheetForColor(colorFace)); + this->ui.selectedColorName->setText(ldraw::colorDisplayName(this->selectedColor, this->colorTable)); + uiutilities::colorizeWidget(this->ui.selectedColorName, ldraw::colorFace(this->selectedColor, colorTable)); this->ui.colorIndex->setValue(this->selectedColor.index); for (QPushButton* button : this->buttons) { @@ -157,3 +143,8 @@ this->selectedColor = color; this->updateSelectedColorTexts(); } + +ldraw::Color ColorSelectDialog::currentColor() const +{ + return this->selectedColor; +}