From 752b1e9d6a24f885db64589850b1630128328e03 Mon Sep 17 00:00:00 2001 From: Anastasia Viktorova Date: Wed, 17 Sep 2025 13:00:50 +0300 Subject: [PATCH 1/4] fix transition synchronization add polyline mode --- cyberiadasm_editor_transition_item.cpp | 61 +++++++++++++++++-------- cyberiadasm_editor_transition_item.h | 2 +- cyberiadasm_model.cpp | 2 +- cyberiadasm_properties_widget.cpp | 6 ++- dialogs/preferences_dialog.cpp | 2 + dialogs/preferences_dialog.ui | 16 ++++++- images/polyline-mode.png | Bin 0 -> 805 bytes settings_manager.cpp | 11 +++++ settings_manager.h | 4 ++ smeditor.qrc | 1 + smeditor_window.cpp | 7 ++- smeditor_window.h | 1 + smeditor_window.ui | 30 ++++++++++++ 13 files changed, 117 insertions(+), 26 deletions(-) create mode 100755 images/polyline-mode.png diff --git a/cyberiadasm_editor_transition_item.cpp b/cyberiadasm_editor_transition_item.cpp index 6604dac..73dcab4 100644 --- a/cyberiadasm_editor_transition_item.cpp +++ b/cyberiadasm_editor_transition_item.cpp @@ -510,15 +510,20 @@ void CyberiadaSMEditorTransitionItem::updateAction() } void CyberiadaSMEditorTransitionItem::updateActionPosition() { - if (!actionItem) return; - - QPointF lastPoint = sourcePoint(); - if(transition->has_polyline() && transition->get_geometry_polyline().size() > 0) { + QPointF firstPoint = sourcePoint(); + QPointF lastPoint = targetPoint() + targetCenter(); + if (isSourceTraking) { + firstPoint = prevPosition; + } + if (source() != target() && transition->has_polyline() && transition->get_geometry_polyline().size() > 0) { Cyberiada::Polyline polyline = transition->get_geometry_polyline(); Cyberiada::Point lastPolylinePoint = *(std::next(polyline.begin(), polyline.size() - 1)); - lastPoint = QPointF(lastPolylinePoint.x, lastPolylinePoint.y); + firstPoint = QPointF(lastPolylinePoint.x, lastPolylinePoint.y); } - QPointF textPos = (lastPoint + (targetPoint() + targetCenter() - sourceCenter())) / 2 + sourceCenter() - actionItem->boundingRect().center(); + if (isTargetTraking) { + lastPoint = prevPosition; + } + QPointF textPos = (firstPoint + (lastPoint - sourceCenter())) / 2 + sourceCenter() - actionItem->boundingRect().center(); actionItem->setPos(textPos); update(); @@ -531,9 +536,7 @@ void CyberiadaSMEditorTransitionItem::setActionVisibility(bool visible) { void CyberiadaSMEditorTransitionItem::syncFromModel() { prepareGeometryChange(); - if (transition->has_action()) { - updateAction(); - } + updateAction(); updateDots(); setDotsPosition(); CyberiadaSMEditorAbstractItem::syncFromModel(); @@ -591,7 +594,6 @@ void CyberiadaSMEditorTransitionItem::slotMoveDot(QGraphicsItem *signalOwner, qr // loop if (cItem == target()) { - qDebug() << "1"; setSource(cItem); hasIntersections = false; QPointF newPoint = findIntersectionWithItem(source(), p, sourceCenter(), &hasIntersections) - @@ -603,7 +605,6 @@ void CyberiadaSMEditorTransitionItem::slotMoveDot(QGraphicsItem *signalOwner, qr // if (cItem == source()) { - qDebug() << "2"; hasIntersections = false; QPointF newPoint = findIntersectionWithItem(source(), p, nextPoint, &hasIntersections) - sourceCenter(); @@ -618,14 +619,11 @@ void CyberiadaSMEditorTransitionItem::slotMoveDot(QGraphicsItem *signalOwner, qr cItem->sceneBoundingRect().center(); // intersectoin with item under cursor if (hasIntersections) { - qDebug() << "3"; isSourceTraking = false; setSource(cItem); setSourcePoint(newPoint); return; } - - qDebug() << "3.5"; return; } bool hasIntersections = false; @@ -640,16 +638,14 @@ void CyberiadaSMEditorTransitionItem::slotMoveDot(QGraphicsItem *signalOwner, qr // intersection with source in adjusted bounding rect if (hasIntersections) { isSourceTraking = false; - qDebug() << "4" << isSourceTraking << isTargetTraking; setSourcePoint(newPoint); return; } - qDebug() << "5"; - // mouse traking isSourceTraking = true; setDotsPosition(); + updateActionPosition(); break; } // last point @@ -715,6 +711,7 @@ void CyberiadaSMEditorTransitionItem::slotMoveDot(QGraphicsItem *signalOwner, qr isTargetTraking = true; setDotsPosition(); + updateActionPosition(); break; } @@ -732,8 +729,15 @@ void CyberiadaSMEditorTransitionItem::slotMoveDot(QGraphicsItem *signalOwner, qr void CyberiadaSMEditorTransitionItem::slotMouseReleaseDot() { - isSourceTraking = false; - isTargetTraking = false; + if (isSourceTraking || isTargetTraking) { + isSourceTraking = false; + isTargetTraking = false; + prepareGeometryChange(); + updateActionPosition(); + } + if (source() == target() && transition->has_polyline() && transition->get_geometry_polyline().size() > 0) { + model->updateGeometry(model->elementToIndex(element), Cyberiada::Polyline()); + } } void CyberiadaSMEditorTransitionItem::slotDeleteDot(QGraphicsItem *signalOwner) @@ -759,6 +763,15 @@ void CyberiadaSMEditorTransitionItem::slotDeleteDot(QGraphicsItem *signalOwner) void CyberiadaSMEditorTransitionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { + if (!SettingsManager::instance().getPolylineMode()) { + if (actionText().isEmpty()) { + actionItem->setTextInteractionFlags(Qt::TextEditorInteraction); + actionItem->setFocus(Qt::MouseFocusReason); + } + QGraphicsItem::mouseDoubleClickEvent(event); + return; + } + if (source() == target()) { return; } QPointF clickPos = event->pos(); @@ -972,12 +985,20 @@ void CyberiadaSMEditorTransitionItem::hideDots() void CyberiadaSMEditorTransitionItem::setDotsPosition() { - QPainterPath linePath = path(); + if (isSourceTraking) { + listDots.first()->setPos(prevPosition); + return; + } + if (isTargetTraking) { + listDots.last()->setPos(prevPosition); + return; + } if (source() == target()) { listDots.at(0)->setPos(sourcePoint() + sourceCenter()); listDots.at(1)->setPos(targetPoint() + targetCenter()); return; } + QPainterPath linePath = path(); for(int i = 0; i < linePath.elementCount(); i++) { QPointF point = linePath.elementAt(i); listDots.at(i)->setPos(point); diff --git a/cyberiadasm_editor_transition_item.h b/cyberiadasm_editor_transition_item.h index 8afeb83..073ccca 100644 --- a/cyberiadasm_editor_transition_item.h +++ b/cyberiadasm_editor_transition_item.h @@ -131,7 +131,7 @@ private slots: const Cyberiada::Transition* transition; - TransitionAction *actionItem = nullptr; + TransitionAction *actionItem; QPointF textPosition; QPointF prevPosition; diff --git a/cyberiadasm_model.cpp b/cyberiadasm_model.cpp index 6cd2edb..4ae4740 100644 --- a/cyberiadasm_model.cpp +++ b/cyberiadasm_model.cpp @@ -298,7 +298,7 @@ bool CyberiadaSMModel::deleteAction(const QModelIndex& index, int action_index) return false; } actions.erase(actions.begin() + static_cast(action_index)); - } else if (element->get_type() == Cyberiada::elementTransition) { + } else if (element->get_type() == Cyberiada::elementTransition) { Cyberiada::Transition* trans = static_cast(element); if (!trans->has_action()) { return false; diff --git a/cyberiadasm_properties_widget.cpp b/cyberiadasm_properties_widget.cpp index 1c26666..cd944c1 100644 --- a/cyberiadasm_properties_widget.cpp +++ b/cyberiadasm_properties_widget.cpp @@ -281,7 +281,11 @@ void CyberiadaSMPropertiesWidget::slotPropertyChanged(QtProperty* p) const Cyberiada::Action& a = trans->get_action(); if (cp.name == propTrigger) { - model->updateAction(i, 0, stringManager->value(p), a.get_guard().c_str(), a.get_behavior().c_str()); + if (stringManager->value(p).isEmpty()) { + model->deleteAction(i); + } else { + model->updateAction(i, 0, stringManager->value(p), a.get_guard().c_str(), a.get_behavior().c_str()); + } } if (cp.name == propGuard) { model->updateAction(i, 0, a.get_trigger().c_str(), stringManager->value(p), a.get_behavior().c_str()); diff --git a/dialogs/preferences_dialog.cpp b/dialogs/preferences_dialog.cpp index f8b952a..18706c3 100644 --- a/dialogs/preferences_dialog.cpp +++ b/dialogs/preferences_dialog.cpp @@ -66,6 +66,7 @@ void PreferencesDialog::loadFromSettings() ui->inspectorModeCheckBox->setChecked(sm.getInspectorMode()); ui->printModeCheckBox->setChecked(sm.getPrintMode()); ui->snapModeCheckBox->setChecked(sm.getSnapMode()); + ui->polylineModeCheckBox->setChecked(sm.getPolylineMode()); ui->showTransTextCheckBox->setChecked(sm.getShowTransitionText()); @@ -87,6 +88,7 @@ void PreferencesDialog::saveSettings() sm.setInspectorMode(ui->inspectorModeCheckBox->isChecked()); sm.setPrintMode(ui->printModeCheckBox->isChecked()); sm.setSnapMode(ui->snapModeCheckBox->isChecked()); + sm.setPolylineMode(ui->polylineModeCheckBox->isChecked()); // visualization sm.setShowTransitionText(ui->showTransTextCheckBox->isChecked()); diff --git a/dialogs/preferences_dialog.ui b/dialogs/preferences_dialog.ui index 5c1a675..6aead38 100644 --- a/dialogs/preferences_dialog.ui +++ b/dialogs/preferences_dialog.ui @@ -29,7 +29,7 @@ - 2 + 0 @@ -84,6 +84,20 @@ + + + + Polyline mode + + + + + + + + + + diff --git a/images/polyline-mode.png b/images/polyline-mode.png new file mode 100755 index 0000000000000000000000000000000000000000..2788f14ee17eef1e7c0b8a793879965800d56494 GIT binary patch literal 805 zcmV+=1KRwFP)zd~iDAg;NnP>=Um6vmnhIuV_xvG+vW@ za9S%sd}sZwUIniavU6R}Is0!T1T6Dfvj-f!uBrgxA?=U$ zMw|-ec!n@ENMw94pWnZ}n(>6;`;=oPJAo7-A{MmImclV{xzkngvvom)i&j87(<%fk z9)^EXhK5Jpv>%TK;k9zUME$UTbPPRQ`d&{EG!l`piSy2k@VYpubgS}m9DP_~ROzI2 zLL{Ab7oQP^3p9^T10zrru!e9vsWSG+>+$w2gyU0`;pau;I97Tij%5Pk-HxL1;=RB~ z2*cOQ0tiEQ)X(TU+#(E&hwO7&0T)ERFw_Z*Cd+pnPzm@Ul5-{ba6K{z_93ZwVHY>bYzZKNFiSr8$k9l&f*hMpPL zl3x%M}XO&47nEb02gu-$O+5_W$3NyUpN8G24N_!DyPGs83>m)uc&|17s(I5 z#=2|+l{T*zInN$3xQW>(-t8zv1GX!M@3xZ8<~205?H6GvOBlLGI8G9dPg3SNl!!Sq z4c~45W%Ck~q~6%9H+4q5@G~%ySO9+4og`N%K=Q#)h6=bn==!KNDt6X3kYl^I6NdLu zj*k(JLX@K$w{C8g8@}6CgUFta^1dpzu4n+xRGTE5ZtA2QKV4`}96thPg2pDSLKq}<}F=ZlH00000NkvXXu0mjfTr**N literal 0 HcmV?d00001 diff --git a/settings_manager.cpp b/settings_manager.cpp index c88af01..a58e0b5 100644 --- a/settings_manager.cpp +++ b/settings_manager.cpp @@ -21,6 +21,7 @@ void SettingsManager::load() { inspectorMode = s.value("display/inspectorMode", false).toBool(); printMode = s.value("display/printMode", false).toBool(); snapMode = s.value("display/snapMode", false).toBool(); + polylineMode = s.value("display/polylineMode", false).toBool(); selectionColor = QColor(s.value("display/selectionColor", QColor(Qt::darkGray).name()).toString()); selectionBorderWidth = s.value("display/selectionBorderWidth", 2).toInt(); @@ -37,6 +38,7 @@ void SettingsManager::loadDefaults() setInspectorMode(false); setPrintMode(false); setSnapMode(false); + setPolylineMode(false); setSelectionColor(QColor(Qt::red)); setSelectionBorderWidth(2); @@ -94,6 +96,15 @@ void SettingsManager::setSnapMode(bool value) } } +void SettingsManager::setPolylineMode(bool value) +{ + if (polylineMode != value) { + polylineMode = value; + QSettings().setValue("display/polylineMode", value); + emit PolylineModeChanged(value); + } +} + void SettingsManager::setSelectionColor(QColor value) { if (selectionColor != value) { diff --git a/settings_manager.h b/settings_manager.h index 4d512af..6a1882a 100644 --- a/settings_manager.h +++ b/settings_manager.h @@ -29,6 +29,8 @@ class SettingsManager : public QObject { void setPrintMode(bool value); bool getSnapMode() const { return snapMode; } void setSnapMode(bool value); + bool getPolylineMode() const { return polylineMode; } + void setPolylineMode(bool value); QColor getSelectionColor() const { return selectionColor; } void setSelectionColor(QColor value); @@ -45,6 +47,7 @@ class SettingsManager : public QObject { void inspectorModeChanged(bool); void printModeChanged(bool); void snapModeChanged(bool); + void PolylineModeChanged(bool); void selectionSettingsChanged(); @@ -64,6 +67,7 @@ class SettingsManager : public QObject { bool inspectorMode; bool printMode; bool snapMode; + bool polylineMode; // selection QColor selectionColor; diff --git a/smeditor.qrc b/smeditor.qrc index c29dbf9..51433c9 100644 --- a/smeditor.qrc +++ b/smeditor.qrc @@ -29,6 +29,7 @@ images/inspector-mode.png images/snap-mode.png images/grid.png + images/polyline-mode.png fonts/courier.ttf diff --git a/smeditor_window.cpp b/smeditor_window.cpp index fb8e26f..89f42e3 100644 --- a/smeditor_window.cpp +++ b/smeditor_window.cpp @@ -189,6 +189,7 @@ void CyberiadaSMEditorWindow::initializeTools() actionTransitionText->setChecked(sm.getShowTransitionText()); actionInspectorMode->setChecked(sm.getInspectorMode()); actionSnapMode->setChecked(sm.getSnapMode()); + actionPolylineMode->setChecked(sm.getPolylineMode()); } void CyberiadaSMEditorWindow::slotFontTriggered() @@ -231,7 +232,6 @@ void CyberiadaSMEditorWindow::slotGridVisibilityTriggered(bool on) { SettingsManager& sm = SettingsManager::instance(); sm.setShowGrid(on); - // scene->enableGrid(on); } void CyberiadaSMEditorWindow::slotNewSM() @@ -319,4 +319,7 @@ void CyberiadaSMEditorWindow::slotSnapModeTriggered(bool on) SettingsManager::instance().setSnapMode(on); } - +void CyberiadaSMEditorWindow::slotPolylineModeTriggered(bool on) +{ + SettingsManager::instance().setPolylineMode(on); +} diff --git a/smeditor_window.h b/smeditor_window.h index 18c969f..530e803 100644 --- a/smeditor_window.h +++ b/smeditor_window.h @@ -45,6 +45,7 @@ public slots: void slotInspectorModeTriggered(bool on); void slotShowTransitionActionTriggered(bool on); void slotSnapModeTriggered(bool on); + void slotPolylineModeTriggered(bool on); void slotFontTriggered(); void slotToolSelected(QAction *action); void slotFitContent(); diff --git a/smeditor_window.ui b/smeditor_window.ui index dc4086a..cc5e84c 100644 --- a/smeditor_window.ui +++ b/smeditor_window.ui @@ -147,6 +147,7 @@ + @@ -435,6 +436,18 @@ Grid + + + true + + + + :/Icons/images/polyline-mode.png:/Icons/images/polyline-mode.png + + + Режим полилинии + + @@ -800,6 +813,22 @@ + + actionPolylineMode + triggered(bool) + SMEditorWindow + slotPolylineModeTriggered(bool) + + + -1 + -1 + + + 511 + 383 + + + slotFileOpen() @@ -821,5 +850,6 @@ slotGridVisibilityTriggered(bool) slotPreferences() slotDeleteElement() + slotPolylineModeTriggered(bool) From b6c61c46b5b71a0bc824d07d808cba46b3b9875f Mon Sep 17 00:00:00 2001 From: Anastasia Viktorova Date: Fri, 3 Oct 2025 20:54:37 +0300 Subject: [PATCH 2/4] add transition creation for vertexes --- CMakeLists.txt | 2 + cyberiadasm_editor_scene.cpp | 29 +- cyberiadasm_editor_scene.h | 4 +- cyberiadasm_editor_state_item.cpp | 16 +- cyberiadasm_editor_state_item.h | 4 +- cyberiadasm_editor_vertex_item.cpp | 37 ++ cyberiadasm_editor_vertex_item.h | 9 +- cyberiadasm_properties_widget.cpp | 21 +- dotsignal.cpp | 2 +- temporary_transition.cpp | 591 +++++++++++++++++++++++++++++ temporary_transition.h | 86 +++++ 11 files changed, 778 insertions(+), 23 deletions(-) create mode 100644 temporary_transition.cpp create mode 100644 temporary_transition.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 765cd84..e18c48d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,8 @@ add_executable(CyberiadaInspector dialogs/open_file_dialog.h dialogs/open_file_dialog.cpp dialogs/open_file_dialog.ui dialogs/preferences_dialog.h dialogs/preferences_dialog.cpp dialogs/preferences_dialog.ui settings_manager.h settings_manager.cpp + temporary_transition.h temporary_transition.cpp + ) target_include_directories(CyberiadaInspector PUBLIC diff --git a/cyberiadasm_editor_scene.cpp b/cyberiadasm_editor_scene.cpp index cd05c10..20c5b09 100644 --- a/cyberiadasm_editor_scene.cpp +++ b/cyberiadasm_editor_scene.cpp @@ -562,27 +562,40 @@ void CyberiadaSMEditorScene::addSMItem(Cyberiada::ElementType type) } } -CyberiadaSMEditorTransitionItem* CyberiadaSMEditorScene::addTransition(CyberiadaSMEditorAbstractItem *source, - CyberiadaSMEditorAbstractItem *target) +void CyberiadaSMEditorScene::addTransitionFromTempopary(TemporaryTransition* ttrans, bool valid) { + if (!valid) { + delete ttrans; + return; + } + try { Cyberiada::Element* element = model->newTransition(currentSM, Cyberiada::transitionExternal, - source->getElement(), target->getElement(), - Cyberiada::Action(Cyberiada::actionTransition)); - CyberiadaSMEditorTransitionItem* transition = new CyberiadaSMEditorTransitionItem( - this, model, element, NULL, elementIdToItemMap); + ttrans->getSourceElement(), ttrans->getTargetElement(), + Cyberiada::Action(Cyberiada::actionTransition), Cyberiada::Polyline(), + ttrans->getSourcePoint(), ttrans->getTargetPoint()); + delete ttrans; + CyberiadaSMEditorTransitionItem* transition = + new CyberiadaSMEditorTransitionItem(this, model, element, NULL, elementIdToItemMap); elementIdToItemMap.insert(element->get_id(), transition); addItem(transition); transition->setSelected(true); - return transition; } catch (const Cyberiada::ParametersException& e){ + delete ttrans; QMessageBox::critical(NULL, tr("Create new transition"), tr("Parameters error:\n") + QString(e.str().c_str())); // error = true; - return nullptr; } } +TemporaryTransition *CyberiadaSMEditorScene::addTemporaryTransition(CyberiadaSMEditorAbstractItem* source, QPointF targetPoint) +{ + TemporaryTransition* ttrans = new TemporaryTransition(this, model, NULL, elementIdToItemMap, source, targetPoint); + addItem(ttrans); + connect(ttrans, &TemporaryTransition::signalReady, this, &CyberiadaSMEditorScene::addTransitionFromTempopary); + return ttrans; +} + void CyberiadaSMEditorScene::drawBackground(QPainter* painter, const QRectF &) { SettingsManager& sm = SettingsManager::instance(); diff --git a/cyberiadasm_editor_scene.h b/cyberiadasm_editor_scene.h index 7c2bb82..082c3a1 100644 --- a/cyberiadasm_editor_scene.h +++ b/cyberiadasm_editor_scene.h @@ -41,6 +41,7 @@ #include "cyberiadasm_editor_items.h" #include "cyberiadasm_editor_state_item.h" #include "cyberiadasm_editor_transition_item.h" +#include "temporary_transition.h" #include "cyberiada_constants.h" class CyberiadaSMEditorScene: public QGraphicsScene { @@ -69,7 +70,8 @@ Q_OBJECT ToolType getCurrentTool() { return currentTool; } void addSMItem(Cyberiada::ElementType type); - CyberiadaSMEditorTransitionItem* addTransition(CyberiadaSMEditorAbstractItem* source, CyberiadaSMEditorAbstractItem* target); + void addTransitionFromTempopary(TemporaryTransition* ttrans, bool valid); + TemporaryTransition* addTemporaryTransition(CyberiadaSMEditorAbstractItem* source, QPointF targetPoint); void deleteItemsRecursively(Cyberiada::Element* element); diff --git a/cyberiadasm_editor_state_item.cpp b/cyberiadasm_editor_state_item.cpp index c551961..fbba8e0 100644 --- a/cyberiadasm_editor_state_item.cpp +++ b/cyberiadasm_editor_state_item.cpp @@ -78,7 +78,7 @@ CyberiadaSMEditorStateItem::CyberiadaSMEditorStateItem(QObject *parent_object, isHighlighted = false; creatingOfTrans = false; - trans = nullptr; + ttrans = nullptr; initializeDots(); setDotsPosition(); @@ -493,13 +493,13 @@ void CyberiadaSMEditorStateItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { // TODO create transition if (creatingOfTrans) { - if (!trans) { + if (!ttrans) { CyberiadaSMEditorScene* cScene = dynamic_cast(scene()); if (!cScene) return; - trans = cScene->addTransition(this, this); - trans->setSelected(true); - trans->getDot(1)->setVisible(true); - trans->getDot(1)->grabMouse(); + ttrans = cScene->addTemporaryTransition(this, event->pos()); + ttrans->setSelected(true); + ttrans->getDot(1)->setVisible(true); + ttrans->getDot(1)->grabMouse(); } return; } @@ -524,9 +524,9 @@ void CyberiadaSMEditorStateItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void CyberiadaSMEditorStateItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { // TODO create transition - if (creatingOfTrans && trans) { + if (creatingOfTrans && ttrans) { creatingOfTrans = false; - trans = nullptr; + ttrans = nullptr; CyberiadaSMEditorAbstractItem::mouseReleaseEvent(event); return; diff --git a/cyberiadasm_editor_state_item.h b/cyberiadasm_editor_state_item.h index ee613f9..67deb0d 100644 --- a/cyberiadasm_editor_state_item.h +++ b/cyberiadasm_editor_state_item.h @@ -34,7 +34,7 @@ #include "dotsignal.h" #include "editable_text_item.h" #include "cyberiadasm_editor_items.h" -#include "cyberiadasm_editor_transition_item.h" +#include "temporary_transition.h" /* ----------------------------------------------------------------------------- * State Item @@ -120,7 +120,7 @@ private slots: std::vector actions; bool creatingOfTrans; - CyberiadaSMEditorTransitionItem* trans; + TemporaryTransition* ttrans; void updateParent(CyberiadaSMEditorAbstractItem* newParent); diff --git a/cyberiadasm_editor_vertex_item.cpp b/cyberiadasm_editor_vertex_item.cpp index 5a57b14..fa61f06 100644 --- a/cyberiadasm_editor_vertex_item.cpp +++ b/cyberiadasm_editor_vertex_item.cpp @@ -48,6 +48,9 @@ CyberiadaSMEditorVertexItem::CyberiadaSMEditorVertexItem(CyberiadaSMModel* model setAcceptHoverEvents(true); setFlags(ItemIsSelectable | ItemSendsGeometryChanges); + trans = nullptr; + creatingOfTrans = false; + initializeDots(); setDotsPosition(); hideDots(); @@ -122,6 +125,15 @@ void CyberiadaSMEditorVertexItem::paint(QPainter* painter, const QStyleOptionGra } } +void CyberiadaSMEditorVertexItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (cornerFlags == 0 && !isSelected()) { + creatingOfTrans = true; + return; + } + CyberiadaSMEditorAbstractItem::mousePressEvent(event); +} + void CyberiadaSMEditorVertexItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (!element->has_geometry() || @@ -131,6 +143,19 @@ void CyberiadaSMEditorVertexItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event return; } + Cyberiada::ElementType type = element->get_type(); + if (type == Cyberiada::elementInitial && creatingOfTrans) { + if (!trans) { + CyberiadaSMEditorScene* cScene = dynamic_cast(scene()); + if (!cScene) return; + trans = cScene->addTemporaryTransition(this, event->pos()); + trans->setSelected(true); + trans->getDot(1)->setVisible(true); + trans->getDot(1)->grabMouse(); + } + return; + } + if (isLeftMouseButtonPressed) { setFlag(ItemIsMovable); model->updateGeometry(model->elementToIndex(element), @@ -141,6 +166,18 @@ void CyberiadaSMEditorVertexItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event emit geometryChanged(); } +void CyberiadaSMEditorVertexItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (creatingOfTrans && trans) { + creatingOfTrans = false; + trans = nullptr; + CyberiadaSMEditorAbstractItem::mouseReleaseEvent(event); + return; + } + + CyberiadaSMEditorAbstractItem::mouseReleaseEvent(event); +} + void CyberiadaSMEditorVertexItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { if (!isSelected() || diff --git a/cyberiadasm_editor_vertex_item.h b/cyberiadasm_editor_vertex_item.h index 950e9dc..7e26669 100644 --- a/cyberiadasm_editor_vertex_item.h +++ b/cyberiadasm_editor_vertex_item.h @@ -25,6 +25,7 @@ #define CYBERIADASMEDITORVERTEXITEM_H #include "cyberiadasm_editor_items.h" +#include "temporary_transition.h" #include "dotsignal.h" /* ----------------------------------------------------------------------------- @@ -44,12 +45,18 @@ class CyberiadaSMEditorVertexItem: public CyberiadaSMEditorAbstractItem { protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; - void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; private: QRectF fullCircle() const; QRectF partialCircle() const; + + TemporaryTransition* trans; + bool creatingOfTrans; }; diff --git a/cyberiadasm_properties_widget.cpp b/cyberiadasm_properties_widget.cpp index cd944c1..450adee 100644 --- a/cyberiadasm_properties_widget.cpp +++ b/cyberiadasm_properties_widget.cpp @@ -268,14 +268,31 @@ void CyberiadaSMPropertiesWidget::slotPropertyChanged(QtProperty* p) const Cyberiada::Element* sourceElement = getElementByNumber(true, enumManager->value(p)); MY_ASSERT(sourceElement); model->updateGeometry(i, sourceElement->get_id(), trans->target_element_id()); - // TODO set new source point + Cyberiada::ElementType sourceType = sourceElement->get_type(); + Cyberiada::Point newSourcePoint = Cyberiada::Point(0, 0); + if (sourceType == Cyberiada::elementSimpleState || + sourceType == Cyberiada::elementCompositeState) { + const Cyberiada::ElementCollection* coll = static_cast(sourceElement); + Cyberiada::Rect rect = coll->get_geometry_rect(); + newSourcePoint = Cyberiada::Point(rect.width / 2, 0); + } + model->updateGeometry(i, newSourcePoint, trans->get_target_point()); + } if (cp.name == propTarget) { const Cyberiada::Element* targetElement = getElementByNumber(false, enumManager->value(p)); MY_ASSERT(targetElement); model->updateGeometry(i, trans->source_element_id(), targetElement->get_id()); - // TODO set new target point + Cyberiada::ElementType targetType = targetElement->get_type(); + Cyberiada::Point newTargetPoint = Cyberiada::Point(0, 0); + if (targetType == Cyberiada::elementSimpleState || + targetType == Cyberiada::elementCompositeState) { + const Cyberiada::ElementCollection* coll = static_cast(targetElement); + Cyberiada::Rect rect = coll->get_geometry_rect(); + newTargetPoint = Cyberiada::Point(rect.width / 2, 0); + } + model->updateGeometry(i, trans->get_source_point(), newTargetPoint); } const Cyberiada::Action& a = trans->get_action(); diff --git a/dotsignal.cpp b/dotsignal.cpp index d3d4635..27092eb 100644 --- a/dotsignal.cpp +++ b/dotsignal.cpp @@ -114,9 +114,9 @@ void DotSignal::mousePressEvent(QGraphicsSceneMouseEvent *event) void DotSignal::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - emit signalMouseRelease(); ungrabMouse(); QGraphicsItem::mouseReleaseEvent(event); + emit signalMouseRelease(); } void DotSignal::hoverEnterEvent(QGraphicsSceneHoverEvent *event) diff --git a/temporary_transition.cpp b/temporary_transition.cpp new file mode 100644 index 0000000..cd5258a --- /dev/null +++ b/temporary_transition.cpp @@ -0,0 +1,591 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada State Machine Editor + * ----------------------------------------------------------------------------- + * + * The Temporary Transition Item + * + * Copyright (C) 2025 Anastasia Viktorova + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/ + * + * ----------------------------------------------------------------------------- */ + +#include "temporary_transition.h" +#include +#include +#include +#include +#include +#include +#include + +#include "myassert.h" +#include "cyberiadasm_editor_transition_item.h" +#include "cyberiada_constants.h" +#include "cyberiadasm_editor_scene.h" +#include "settings_manager.h" + +/* ----------------------------------------------------------------------------- + * Temporary Transition Item + * ----------------------------------------------------------------------------- */ + +TemporaryTransition::TemporaryTransition(QObject *parent_object, + CyberiadaSMModel *model, + QGraphicsItem *parent, + QMap& elementItem, + CyberiadaSMEditorAbstractItem* source, + QPointF& targetPoint) : + // QObject(parent_object), + QGraphicsItem(parent), + model(model), + elementIdToItemMap(elementItem), + source(source), + target(source), + targetPoint(targetPoint) +{ + setAcceptHoverEvents(true); + setFlags(ItemIsSelectable|ItemSendsGeometryChanges); + + isSourceTraking = false; + isTargetTraking = true; + + // TODO start source point + sourcePoint = QPointF(); + + initializeDots(); +} + +TemporaryTransition::~TemporaryTransition() +{ + if(!listDots.isEmpty()){ + foreach (DotSignal *dot, listDots) { + dot->deleteLater(); + } + listDots.clear(); + } +} + +QRectF TemporaryTransition::boundingRect() const +{ + MY_ASSERT(model); + MY_ASSERT(model->rootDocument()); + return path().boundingRect().adjusted(-10, -10, 10, 10); +} + +void TemporaryTransition::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + QPen pen = QPen(Qt::black, 2, Qt::SolidLine); + if (isSelected()) { + SettingsManager& sm = SettingsManager::instance(); + pen.setColor(sm.getSelectionColor()); + pen.setWidth(sm.getSelectionBorderWidth()); + } + + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawPath(path()); + + drawArrow(painter); +} + +CyberiadaSMEditorAbstractItem *TemporaryTransition::getSource() const +{ + return source; +} + +void TemporaryTransition::setSource(CyberiadaSMEditorAbstractItem *newSource) +{ + source = newSource; +} + +Cyberiada::Element *TemporaryTransition::getSourceElement() const +{ + return source->getElement(); +} + +Cyberiada::Point TemporaryTransition::getSourcePoint() const +{ + return Cyberiada::Point(sourcePoint.x(), sourcePoint.y()); +} + +void TemporaryTransition::setSourcePoint(const QPointF &point) +{ + sourcePoint = point; + setDotsPosition(); +} + +QPointF TemporaryTransition::sourceCenter() const +{ + Cyberiada::ID id = source->getId(); + if(!elementIdToItemMap.value(id)) return QPoint(); + MY_ASSERT(elementIdToItemMap.value(id)); + return (elementIdToItemMap.value(id))->sceneBoundingRect().center(); +} + +CyberiadaSMEditorAbstractItem *TemporaryTransition::getTarget() const +{ + return target; +} + +void TemporaryTransition::setTarget(CyberiadaSMEditorAbstractItem *newTarget) +{ + target = newTarget; +} + +Cyberiada::Element *TemporaryTransition::getTargetElement() const +{ + return target->getElement(); +} + +Cyberiada::Point TemporaryTransition::getTargetPoint() const +{ + return Cyberiada::Point(targetPoint.x(), targetPoint.y()); +} + +void TemporaryTransition::setTargetPoint(const QPointF &point) +{ + targetPoint = point; + setDotsPosition(); +} + +QPointF TemporaryTransition::targetCenter() const +{ + Cyberiada::ID id = target->getId(); + if(!elementIdToItemMap.value(id)) return QPoint(); + MY_ASSERT(elementIdToItemMap.value(id)); + return (elementIdToItemMap.value(id))->sceneBoundingRect().center(); +} + +QPainterPath TemporaryTransition::path() const +{ + MY_ASSERT(model); + QPainterPath path = QPainterPath(); + + // loop + if (source == target && !(isSourceTraking || isTargetTraking)) { + QPointF p1 = sourcePoint + sourceCenter(); + QPointF p2 = targetPoint + targetCenter(); + + QPointF v1 = p1 - sourceCenter(); + QPointF v2 = p2 - sourceCenter(); + + double cross = v1.x() * v2.y() - v1.y() * v2.x(); + + bool clockwise = (cross > 0); + // clockwise = !clockwise; + + QPointF center = (p1 + p2) / 2; + qreal radius = QLineF(p1, p2).length() / 2; + + qreal MIN_RADIUS = 30; + + if (radius <= MIN_RADIUS) { + QPointF dir = p2 - p1; + if (dir.isNull()) { + dir = QPointF(1, 0); + } + + QLineF norm(dir, QPointF()); + norm.setLength(1.0); + + QPointF normal(-norm.dy(), norm.dx()); + + qreal offset = std::sqrt(MIN_RADIUS * MIN_RADIUS - radius * radius); + + center += normal * offset; + radius = MIN_RADIUS; + } + + QRectF rect(center.x() - radius, center.y() - radius, + radius * 2, radius * 2); + + + qreal angle1 = QLineF(center, p1).angle(); + qreal angle2 = QLineF(center, p2).angle(); + + if(angle1 == angle2) { + angle2 += 360; + } + + qreal spanAngle = angle2 - angle1; + if (clockwise) { + if (spanAngle > 0) spanAngle -= 360; + } else { + if (spanAngle < 0) spanAngle += 360; + } + + path.moveTo(p1); + path.arcTo(rect, angle1, spanAngle); + + return path; + } + + if (isSourceTraking) { + path.moveTo(prevPosition); + } else { + path.moveTo(sourcePoint + sourceCenter()); + } + + if (isTargetTraking) { + path.lineTo(prevPosition); + } else { + path.lineTo(targetPoint + targetCenter()); + } + + return path; +} + +DotSignal *TemporaryTransition::getDot(int index) +{ + if (index < 0) return nullptr; + if (listDots.size() <= index) return nullptr; + return listDots.at(index); +} + +void TemporaryTransition::drawArrow(QPainter* painter) +{ + SettingsManager& sm = SettingsManager::instance(); + + QPen pen(Qt::black, 1); + if (isSelected()) { + pen.setColor(sm.getSelectionColor()); + pen.setWidth(sm.getSelectionBorderWidth()); + } + painter->setPen(pen); + + double angle; + + QPointF p1; + QPointF p2; + if (isSourceTraking) { + p1 = prevPosition; + } else { + p1 = sourcePoint + sourceCenter(); + } + if (isTargetTraking) { + p2 = prevPosition; + } else { + p2 = targetPoint + targetCenter(); + } + + if (source == target && !(isTargetTraking || isSourceTraking)) { + QPointF center = (p1 + p2) / 2; + angle = qDegreesToRadians(QLineF(center, p2).angle()); + QPointF v1 = p1 - sourceCenter(); + QPointF v2 = p2 - sourceCenter(); + + double cross = v1.x() * v2.y() - v1.y() * v2.x(); + + bool clockwise = (cross > 0); + + if (clockwise) { + angle -= M_PI / 2; + } else { + angle += M_PI / 2; + } + } else { + QLineF line(p1, p2); + angle = std::atan2(-line.dy(), line.dx()); + } + + qreal arrowSize = 10.0; + QPointF arrowP1 = p2 + QPointF(-arrowSize * std::cos(angle - M_PI / 6), + arrowSize * std::sin(angle - M_PI / 6)); + QPointF arrowP2 = p2 + QPointF(-arrowSize * std::cos(angle + M_PI / 6), + arrowSize * std::sin(angle + M_PI / 6)); + + QPolygonF arrowHead; + arrowHead << p2 << arrowP1 << arrowP2; + + painter->setBrush(QBrush(pen.color())); + painter->drawPolygon(arrowHead); +} + +CyberiadaSMEditorAbstractItem *TemporaryTransition::itemUnderCursor() +{ + QList items = scene()->items(prevPosition); + + CyberiadaSMEditorAbstractItem* cItem = nullptr; + for (QGraphicsItem* item : items) { + cItem = dynamic_cast(item); + if (!cItem) { continue; } + + if (cItem->type() == CyberiadaSMEditorAbstractItem::StateItem || + cItem->type() == CyberiadaSMEditorAbstractItem::CompositeStateItem || + cItem->type() == CyberiadaSMEditorAbstractItem::VertexItem) { + return cItem; + } + } + return nullptr; +} + +QPointF TemporaryTransition::findIntersectionWithItem(const CyberiadaSMEditorAbstractItem *item, + const QPointF& start, const QPointF& end, + bool* hasIntersections) +{ + if (!item) return QPointF(); + + int margin = 60; + QRectF adjustedRect = item->sceneBoundingRect().adjusted(-margin, -margin, margin, margin); + + if (!adjustedRect.contains(start)) { + *hasIntersections = false; + return QPointF(); + } + + QPointF dir = end - start; + if (dir.isNull()) { + return QPointF(); + } + + if (item->type() == CyberiadaSMEditorAbstractItem::VertexItem) { + *hasIntersections = true; + return item->sceneBoundingRect().center(); + } + + QLineF rayForward(start, start + dir * 1e5); + QLineF rayBackward(start, start - dir * 1e5); + + QPainterPath shape = item->shape(); + QPainterPath sceneShape = item->sceneTransform().map(shape); + QPolygonF polygon = sceneShape.toFillPolygon(); + + QPointF closestIntersection; + qreal minDist = std::numeric_limits::max(); + + for (int i = 0; i < polygon.size(); ++i) { + QPointF p1 = polygon[i]; + QPointF p2 = polygon[(i + 1) % polygon.size()]; + + QLineF edge(p1, p2); + QPointF intersectionPoint; + + // find closest intersection to start point + if (rayForward.intersect(edge, &intersectionPoint) == QLineF::BoundedIntersection) { + qreal dist = QLineF(start, intersectionPoint).length(); + if (dist < minDist) { + minDist = dist; + closestIntersection = intersectionPoint; + } + } + if (rayBackward.intersect(edge, &intersectionPoint) == QLineF::BoundedIntersection) { + qreal dist = QLineF(start, intersectionPoint).length(); + if (dist < minDist) { + minDist = dist; + closestIntersection = intersectionPoint; + } + } + } + + if (minDist < std::numeric_limits::max()) { + *hasIntersections = true; + return closestIntersection; + } else { + *hasIntersections = false; + return QPointF(); + } +} + +void TemporaryTransition::slotMoveDot(QGraphicsItem *signalOwner, qreal dx, qreal dy, QPointF p) +{ + prepareGeometryChange(); + prevPosition = p; + + for(int i = 0; i < listDots.size(); i++){ + if(listDots.at(i) == signalOwner){ + // first point + // if(i == 0) { + // QPointF nextPoint = targetPoint + targetCenter(); + + // CyberiadaSMEditorAbstractItem* cItem = itemUnderCursor(); + // if (cItem) { + // QPointF newPoint; + // bool hasIntersections; + + // // loop + // if (cItem == target) { + // setSource(cItem); + // hasIntersections = false; + // QPointF newPoint = findIntersectionWithItem(source, p, sourceCenter(), &hasIntersections) - + // sourceCenter(); + // isSourceTraking = false; + // setSourcePoint(newPoint); + // return; + // } + + // if (cItem == source) { + // hasIntersections = false; + // QPointF newPoint = findIntersectionWithItem(source, p, nextPoint, &hasIntersections) - + // sourceCenter(); + // if (hasIntersections) { + // isSourceTraking = false; + // setSourcePoint(newPoint); + // return; + // } + // } + + // newPoint = findIntersectionWithItem(cItem, p, nextPoint, &hasIntersections) - + // cItem->sceneBoundingRect().center(); + // // intersectoin with item under cursor + // if (hasIntersections) { + // isSourceTraking = false; + // setSource(cItem); + // setSourcePoint(newPoint); + // return; + // } + // return; + // } + // bool hasIntersections = false; + + // if (source == target) { + // // loop + // nextPoint = sourceCenter(); + // } + + // QPointF newPoint = findIntersectionWithItem(source, p, nextPoint, &hasIntersections) - + // sourceCenter(); + // // intersection with source in adjusted bounding rect + // if (hasIntersections) { + // isSourceTraking = false; + // setSourcePoint(newPoint); + // return; + // } + + // // mouse traking + // isSourceTraking = true; + // setDotsPosition(); + // break; + // } + // last point + if(i == listDots.size() - 1) { + QPointF nextPoint = sourcePoint + sourceCenter(); + + CyberiadaSMEditorAbstractItem* cItem = itemUnderCursor(); + if (cItem) { + QPointF newPoint; + bool hasIntersections; + if (cItem == source) { + // loop + setTarget(cItem); + hasIntersections = false; + QPointF newPoint = findIntersectionWithItem(target, p, targetCenter(), &hasIntersections) - + targetCenter(); + isTargetTraking = false; + setTargetPoint(newPoint); + return; + } + + if (cItem == target) { + hasIntersections = false; + QPointF newPoint = findIntersectionWithItem(target, p, nextPoint, &hasIntersections) - + targetCenter(); + if (hasIntersections) { + isTargetTraking = false; + setTargetPoint(newPoint); + return; + } + } + + newPoint = findIntersectionWithItem(cItem, p, nextPoint, &hasIntersections) - + cItem->sceneBoundingRect().center(); + if (hasIntersections) { + isTargetTraking = false; + setTarget(cItem); + setTargetPoint(newPoint); + return; + } + return; + } + + bool hasIntersections = false; + + if (source == target) { + // loop + nextPoint = sourceCenter(); + } + + QPointF newPoint = findIntersectionWithItem(target, p, nextPoint, &hasIntersections) - + targetCenter(); + + if (hasIntersections) { + isTargetTraking = false; + setTargetPoint(newPoint); + return; + } + + isTargetTraking = true; + setDotsPosition(); + break; + } + break; + } + } +} + +void TemporaryTransition::slotMouseReleaseDot() +{ + if (isSourceTraking || isTargetTraking) { + emit signalReady(this, false); + prepareGeometryChange(); + return; + } + emit signalReady(this, true); +} + +void TemporaryTransition::initializeDots() +{ + // source + DotSignal *dotS = new DotSignal(sourcePoint + sourceCenter(), this); + connect(dotS, &DotSignal::signalMove, this, &TemporaryTransition::slotMoveDot); + connect(dotS, &DotSignal::signalMouseRelease, this, &TemporaryTransition::slotMouseReleaseDot); + dotS->setDotFlags(DotSignal::Movable); + dotS->setDeleteable(true); + listDots.append(dotS); + // target + DotSignal *dotT = new DotSignal(QPointF(), this); + connect(dotT, &DotSignal::signalMove, this, &TemporaryTransition::slotMoveDot); + connect(dotT, &DotSignal::signalMouseRelease, this, &TemporaryTransition::slotMouseReleaseDot); + dotT->setDotFlags(DotSignal::Movable); + dotT->setDeleteable(true); + listDots.append(dotT); +} + +void TemporaryTransition::showDots() +{ + foreach( DotSignal* dot, listDots ) { + dot->setVisible(true); + } +} + +void TemporaryTransition::hideDots() +{ + foreach( DotSignal* dot, listDots ) { + dot->setVisible(false); + } +} + +void TemporaryTransition::setDotsPosition() +{ + if (isSourceTraking) { + listDots.first()->setPos(prevPosition); + return; + } + if (isTargetTraking) { + listDots.last()->setPos(prevPosition); + return; + } + listDots.at(0)->setPos(sourcePoint + sourceCenter()); + listDots.at(1)->setPos(targetPoint + targetCenter()); +} + diff --git a/temporary_transition.h b/temporary_transition.h new file mode 100644 index 0000000..738e305 --- /dev/null +++ b/temporary_transition.h @@ -0,0 +1,86 @@ +#ifndef TEMPORARY_TRANSITION_H +#define TEMPORARY_TRANSITION_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cyberiadasm_editor_items.h" +#include "dotsignal.h" + + + +class TemporaryTransition : public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsItem) + +public: + explicit TemporaryTransition(QObject *parent_object, + CyberiadaSMModel *model, + QGraphicsItem *parent, + QMap &elementItem, + CyberiadaSMEditorAbstractItem* source, + QPointF& targetPoint); + ~TemporaryTransition(); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr); + + CyberiadaSMEditorAbstractItem *getSource() const; + void setSource(CyberiadaSMEditorAbstractItem *newSource); + Cyberiada::Element* getSourceElement() const; + Cyberiada::Point getSourcePoint() const; + void setSourcePoint(const QPointF& point); + QPointF sourceCenter() const; + + CyberiadaSMEditorAbstractItem *getTarget() const; + void setTarget(CyberiadaSMEditorAbstractItem *newTarget); + Cyberiada::Element* getTargetElement() const; + Cyberiada::Point getTargetPoint() const; + void setTargetPoint(const QPointF& point); + QPointF targetCenter() const; + + QPainterPath path() const; + + DotSignal* getDot(int index); + +signals: + void signalReady(TemporaryTransition* ttrans, bool valid); + +private slots: + void slotMoveDot(QGraphicsItem *signalOwner, qreal dx, qreal dy, QPointF p); + void slotMouseReleaseDot(); + +private: + void drawArrow(QPainter* painter); + CyberiadaSMEditorAbstractItem* itemUnderCursor(); + QPointF findIntersectionWithItem(const CyberiadaSMEditorAbstractItem *item, + const QPointF& start, const QPointF& end, + bool* hasIntersections); + + CyberiadaSMModel* model; + QMap& elementIdToItemMap; + + CyberiadaSMEditorAbstractItem* source; + CyberiadaSMEditorAbstractItem* target; + QPointF sourcePoint; + QPointF targetPoint; + + QPointF prevPosition; + bool isSourceTraking; + bool isTargetTraking; + + QList listDots; + void initializeDots(); + void showDots(); + void hideDots(); + void setDotsPosition(); +}; + +#endif // TEMPORARY_TRANSITION_H From bad80d41f26b1fe31a145fa4d227aa1db3720625 Mon Sep 17 00:00:00 2001 From: Anastasia Viktorova Date: Wed, 26 Nov 2025 12:24:01 +0300 Subject: [PATCH 3/4] fix fit content button --- smeditor_window.cpp | 1 + smeditor_window.ui | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/smeditor_window.cpp b/smeditor_window.cpp index 89f42e3..bb357d8 100644 --- a/smeditor_window.cpp +++ b/smeditor_window.cpp @@ -219,6 +219,7 @@ void CyberiadaSMEditorWindow::slotToolSelected(QAction *action) } void CyberiadaSMEditorWindow::slotFitContent() { + qDebug() << "here"; sceneView->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio); } diff --git a/smeditor_window.ui b/smeditor_window.ui index cc5e84c..9c53ba8 100644 --- a/smeditor_window.ui +++ b/smeditor_window.ui @@ -829,6 +829,22 @@ + + actionFitContent + triggered() + SMEditorWindow + slotFitContent() + + + -1 + -1 + + + 511 + 383 + + + slotFileOpen() @@ -851,5 +867,6 @@ slotPreferences() slotDeleteElement() slotPolylineModeTriggered(bool) + slotFitContent() From 94993c3b74a553435c45ffb060d3f19c1875ab31 Mon Sep 17 00:00:00 2001 From: Anastasia Viktorova Date: Wed, 26 Nov 2025 14:42:03 +0300 Subject: [PATCH 4/4] add comment editing, fix text width editing --- cyberiadasm_editor_comment_item.cpp | 27 ++++++++++++++++----------- cyberiadasm_editor_comment_item.h | 6 ++++-- cyberiadasm_editor_state_item.cpp | 6 ++++++ cyberiadasm_model.cpp | 7 +++++-- editable_text_item.cpp | 2 +- editable_text_item.h | 9 ++++----- smeditor_window.cpp | 1 - 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/cyberiadasm_editor_comment_item.cpp b/cyberiadasm_editor_comment_item.cpp index c29f50d..1385628 100644 --- a/cyberiadasm_editor_comment_item.cpp +++ b/cyberiadasm_editor_comment_item.cpp @@ -52,8 +52,10 @@ CyberiadaSMEditorCommentItem::CyberiadaSMEditorCommentItem(QObject *parent_objec } body = new EditableTextItem(comment->get_body().c_str(), this); - body->setPos(-boundingRect().width() / 2 + 15, - boundingRect().height() / 2); - // connect(body, EditableTextItem::editingFinished, this, CyberiadaSMEditorCommentItem::onBodyChanged); + body->setPos(-boundingRect().width() / 2 + margin, - boundingRect().height() / 2); + body->setTextAlignment(Qt::AlignLeft); + body->setTextMargin(2 * margin); + connect(body, &EditableTextItem::editingFinished, this, &CyberiadaSMEditorCommentItem::onBodyChanged); if (element->get_type() == Cyberiada::elementFormalComment) { int fontId = QFontDatabase::addApplicationFont(":/Fonts/fonts/courier.ttf"); @@ -72,8 +74,6 @@ CyberiadaSMEditorCommentItem::CyberiadaSMEditorCommentItem(QObject *parent_objec setAcceptHoverEvents(true); setFlags(ItemIsSelectable | ItemSendsGeometryChanges); - setTextPosition(); - initializeDots(); setDotsPosition(); hideDots(); @@ -143,22 +143,27 @@ void CyberiadaSMEditorCommentItem::paint(QPainter* painter, const QStyleOptionGr painter->drawConvexPolygon(triangle, 3); } -// TODO -void CyberiadaSMEditorCommentItem::onBodyChanged() +void CyberiadaSMEditorCommentItem::updateBody() { - model->updateCommentBody(model->elementToIndex(element), body->toPlainText()); + if (body->toPlainText() != comment->get_body().c_str()) { + body->setPlainText(comment->get_body().c_str()); + } + body->updateTextWidth(); + body->setPos(-boundingRect().width() / 2 + margin, - boundingRect().height() / 2); } -void CyberiadaSMEditorCommentItem::setTextPosition() +// TODO +void CyberiadaSMEditorCommentItem::onBodyChanged() { - QRectF oldRect = boundingRect(); - QRectF titleRect = body->boundingRect(); - body->setPos(oldRect.x() + (oldRect.width() - titleRect.width()) / 2 , oldRect.y()); + model->updateCommentBody(model->elementToIndex(element), body->toPlainText()); } void CyberiadaSMEditorCommentItem::syncFromModel() { // TODO + Cyberiada::Rect model_rect = comment->get_geometry_rect(); + setPos(QPointF(model_rect.x, model_rect.y)); + updateBody(); CyberiadaSMEditorAbstractItem::syncFromModel(); } diff --git a/cyberiadasm_editor_comment_item.h b/cyberiadasm_editor_comment_item.h index 64e6c83..1bf1df4 100644 --- a/cyberiadasm_editor_comment_item.h +++ b/cyberiadasm_editor_comment_item.h @@ -50,19 +50,21 @@ class CyberiadaSMEditorCommentItem : public CyberiadaSMEditorAbstractItem //, pu QRectF boundingRect() const override; - void setTextPosition(); - void syncFromModel() override; protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; +private: + void updateBody(); + private slots: void onBodyChanged(); private: EditableTextItem* body; QBrush commentBrush; + int margin = 10; const Cyberiada::Comment* comment; QMap& elementIdToItemMap; diff --git a/cyberiadasm_editor_state_item.cpp b/cyberiadasm_editor_state_item.cpp index fbba8e0..410dc02 100644 --- a/cyberiadasm_editor_state_item.cpp +++ b/cyberiadasm_editor_state_item.cpp @@ -221,6 +221,7 @@ void CyberiadaSMEditorStateItem::setTextPosition() { // TODO refactor QRectF oldRect = rect(); + title->updateTextWidth(); QRectF titleRect = title->boundingRect(); title->setPos(oldRect.x() + (oldRect.width() - titleRect.width()) / 2 , oldRect.y()); @@ -272,6 +273,11 @@ void CyberiadaSMEditorStateItem::syncFromModel() } updateRegion(); } + // else { + // if (region != nullptr) { + // // TODO set actions on center (change parent from region to state) + // } + // } CyberiadaSMEditorAbstractItem* cParent = dynamic_cast(parentItem()); if (cParent == nullptr) { diff --git a/cyberiadasm_model.cpp b/cyberiadasm_model.cpp index 4ae4740..28316a2 100644 --- a/cyberiadasm_model.cpp +++ b/cyberiadasm_model.cpp @@ -395,8 +395,11 @@ bool CyberiadaSMModel::updateParent(const QModelIndex &index, const Cyberiada::I bool CyberiadaSMModel::updateCommentBody(const QModelIndex& index, const QString& body) { Cyberiada::Element* element = indexToElement(index); - if (!element) return false; - // TODO + if (!element) return false; + if (element->get_type() != Cyberiada::elementComment && + element->get_type() != Cyberiada::elementFormalComment) return false; + Cyberiada::Comment* comment = static_cast(element); + comment->set_body(body.toStdString()); emit dataChanged(index, index); return true; } diff --git a/editable_text_item.cpp b/editable_text_item.cpp index 5d148ba..7096857 100644 --- a/editable_text_item.cpp +++ b/editable_text_item.cpp @@ -102,7 +102,7 @@ void EditableTextItem::focusOutEvent(QFocusEvent *event) { isEdit = false; setPlainText(toPlainText().trimmed()); QGraphicsTextItem::focusOutEvent(event); - // emit editingFinished(); + emit editingFinished(); } void EditableTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { diff --git a/editable_text_item.h b/editable_text_item.h index f1b1cd2..20a3436 100644 --- a/editable_text_item.h +++ b/editable_text_item.h @@ -36,6 +36,9 @@ class EditableTextItem : public QGraphicsTextItem { void setFontStyleChangeable(bool isChangeable); void setFontBoldness(bool isBold); void setTextMargin(double newTextMargin); + void setTextAlignment(Qt::Alignment alignment); + void setTextWidthEnabled(bool enabled); + void updateTextWidth(); protected: void focusOutEvent(QFocusEvent *event) override; @@ -46,18 +49,14 @@ class EditableTextItem : public QGraphicsTextItem { void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - void setTextAlignment(Qt::Alignment alignment); - void setTextWidthEnabled(bool enabled); - signals: void sizeChanged(); - // void editingFinished(); + void editingFinished(); protected slots: void onFontChanged(const QFont &newFont) ; protected: - void updateTextWidth(); bool isEdit; bool align; bool isFontStyleChangeable = true; diff --git a/smeditor_window.cpp b/smeditor_window.cpp index bb357d8..89f42e3 100644 --- a/smeditor_window.cpp +++ b/smeditor_window.cpp @@ -219,7 +219,6 @@ void CyberiadaSMEditorWindow::slotToolSelected(QAction *action) } void CyberiadaSMEditorWindow::slotFitContent() { - qDebug() << "here"; sceneView->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio); }