diff --git a/sources/editor/elementscene.cpp b/sources/editor/elementscene.cpp index 393d280b9..a2c8146d3 100644 --- a/sources/editor/elementscene.cpp +++ b/sources/editor/elementscene.cpp @@ -317,6 +317,10 @@ void ElementScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) */ void ElementScene::drawForeground(QPainter *p, const QRectF &) { + if (!m_hotspot_visible) { + return; + } + p -> save(); // desactive tout antialiasing, sauf pour le texte diff --git a/sources/editor/elementscene.h b/sources/editor/elementscene.h index 97ec4812a..a53afff6f 100644 --- a/sources/editor/elementscene.h +++ b/sources/editor/elementscene.h @@ -90,6 +90,8 @@ class ElementScene : public QGraphicsScene m_y_grid; QPointer m_single_selected_item; + + bool m_hotspot_visible = true; // methods public: @@ -132,6 +134,14 @@ class ElementScene : public QGraphicsScene QETElementEditor* editor() const; void addItems(QVector items); void removeItems(QVector items); + + /// Whether drawForeground() draws the red hotspot cross. On by + /// default (the normal editing view); turned off around a + /// render() call meant to capture the element's actual content + /// only, e.g. exporting to SVG -- the cross is an editing aid, + /// not part of the drawn symbol. + void setHotspotVisible(bool visible) {m_hotspot_visible = visible;} + bool hotspotVisible() const {return m_hotspot_visible;} protected: void mouseMoveEvent (QGraphicsSceneMouseEvent *) override; diff --git a/sources/editor/ui/qetelementeditor.cpp b/sources/editor/ui/qetelementeditor.cpp index ba8d80f19..9a4eb2ffb 100644 --- a/sources/editor/ui/qetelementeditor.cpp +++ b/sources/editor/ui/qetelementeditor.cpp @@ -53,6 +53,8 @@ #include #include +#include +#include /** * @brief QETElementEditor::QETElementEditor @@ -1377,6 +1379,94 @@ bool QETElementEditor::on_m_save_as_file_action_triggered() return false; } +/** + @brief QETElementEditor::on_m_export_svg_action_triggered + Export the element currently open in this editor to a standalone SVG + file. + + Renders the live ElementScene directly, the same way + ExportDialog::generateSvg() renders the live Diagram for the diagram + editor's own SVG export -- not ElementPictureFactory's cached picture. + That cache is keyed by the element's saved-to-disk uuid and is never + invalidated on edit (nothing in this editor ever tells it to), so it + would silently export stale content for any element already previewed + once in the elements panel, and nothing at all for one that has never + been saved. Rendering the scene directly has neither problem and + always reflects exactly what is currently on screen, saved or not. + @return true if the file was written +*/ +bool QETElementEditor::on_m_export_svg_action_triggered() +{ + //Suggest the element's own filename (without its .elmt extension) as + //the default export name, per plc-user's review on #637 -- the + //directory-only default below is unchanged for an element that has + //never been saved, since there is no filename to derive one from. + QString suggested_path = QETApp::customElementsDir(); + if (!m_file_name.isEmpty()) { + QFileInfo file_info(m_file_name); + suggested_path = QDir(file_info.absolutePath()).filePath(file_info.completeBaseName()); + } + + QString fn = QFileDialog::getSaveFileName( + this, + tr("Exporter en SVG", "dialog title"), + suggested_path, + tr("Image SVG (*.svg)", "filetypes allowed when exporting an element to SVG")); + + if (fn.isEmpty()) { + return false; + } + if (!fn.endsWith(".svg", Qt::CaseInsensitive)) { + fn += ".svg"; + } + + QFile file(fn); + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::critical(this, tr("Échec de l'export"), + tr("Impossible d'écrire dans le fichier « %1 ».").arg(fn)); + return false; + } + + //Margin-less bounding rect of the element's own drawn content + //(lines, rects, terminals, text...), excluding the origin cross + //and other editor-only decoration -- see + //ElementScene::elementSceneGeometricRect()'s own doc comment. + //Falls back to itemsBoundingRect() for the rare element made up + //only of item types that helper deliberately excludes. + QRectF source_rect = m_elmt_scene->elementSceneGeometricRect(); + if (source_rect.isEmpty()) { + source_rect = m_elmt_scene->itemsBoundingRect(); + } + constexpr qreal margin = 5.0; + source_rect.adjust(-margin, -margin, margin, margin); + + QSize target_size = source_rect.size().toSize(); + if (target_size.isEmpty()) { + target_size = QSize(1, 1); + } + + QSvgGenerator svg_engine; + svg_engine.setSize(target_size); + svg_engine.setViewBox(QRect(QPoint(0, 0), target_size)); + svg_engine.setOutputDevice(&file); + + QPainter svg_painter(&svg_engine); + svg_painter.setRenderHint(QPainter::Antialiasing, true); + svg_painter.setRenderHint(QPainter::TextAntialiasing, true); + + //The hotspot cross is ElementScene::drawForeground()'s editing aid, + //drawn unconditionally on every render() call including this one + //unless told not to -- it is not part of the element being + //exported. + m_elmt_scene->setHotspotVisible(false); + m_elmt_scene->render(&svg_painter, QRectF(QPointF(0, 0), target_size), source_rect); + m_elmt_scene->setHotspotVisible(true); + + svg_painter.end(); + + return true; +} + void QETElementEditor::on_m_reload_action_triggered() { //If user already edit the element, ask confirmation to reload diff --git a/sources/editor/ui/qetelementeditor.h b/sources/editor/ui/qetelementeditor.h index 160986d82..579d9bab4 100644 --- a/sources/editor/ui/qetelementeditor.h +++ b/sources/editor/ui/qetelementeditor.h @@ -87,6 +87,7 @@ class QETElementEditor : public QMainWindow void on_m_open_action_triggered(); void on_m_open_from_file_action_triggered(); bool on_m_save_as_file_action_triggered(); + bool on_m_export_svg_action_triggered(); void on_m_reload_action_triggered(); void on_m_quit_action_triggered(); void on_m_deselect_all_action_triggered(); diff --git a/sources/editor/ui/qetelementeditor.ui b/sources/editor/ui/qetelementeditor.ui index 90cf36118..65a55aa8b 100644 --- a/sources/editor/ui/qetelementeditor.ui +++ b/sources/editor/ui/qetelementeditor.ui @@ -40,6 +40,7 @@ + @@ -262,6 +263,15 @@ Enregistrer dans un fichier + + + + :/ico/22x22/document-export.png:/ico/22x22/document-export.png + + + Exporter en SVG + +