forked from qt-creator/qt-creator
Scxmleditor: Add showing onEntry and on Exit events
This commit enhances the SCXML Editor by adding a new feature that enables the display of onEntry and onExit events in the state item. This feature provides developers with a more comprehensive view of the state machine's behavior, allowing them to better understand the transitions that occur when entering or exiting a particular state. Fixes: QTCREATORBUG-17378 Change-Id: Iaf84af209d9acf13bd4183f7da9acbcd6c1c8214 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -42,6 +42,7 @@ add_qtc_plugin(ScxmlEditor
|
||||
plugin_interface/baseitem.cpp plugin_interface/baseitem.h
|
||||
plugin_interface/connectableitem.cpp plugin_interface/connectableitem.h
|
||||
plugin_interface/cornergrabberitem.cpp plugin_interface/cornergrabberitem.h
|
||||
plugin_interface/eventitem.cpp plugin_interface/eventitem.h
|
||||
plugin_interface/finalstateitem.cpp plugin_interface/finalstateitem.h
|
||||
plugin_interface/genericscxmlplugin.cpp plugin_interface/genericscxmlplugin.h
|
||||
plugin_interface/graphicsitemprovider.h
|
||||
|
||||
@@ -41,7 +41,8 @@ BaseItem::~BaseItem()
|
||||
void BaseItem::checkParentBoundingRect()
|
||||
{
|
||||
BaseItem *parentBaseItem = this->parentBaseItem();
|
||||
if (parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates()) {
|
||||
if ((parentBaseItem && type() >= InitialStateType && !parentBaseItem->blockUpdates())
|
||||
|| (parentBaseItem && type() == StateWarningType)) {
|
||||
auto parentStateItem = qgraphicsitem_cast<StateItem*>(parentBaseItem);
|
||||
if (parentStateItem && (parentStateItem->type() >= StateType))
|
||||
parentStateItem->updateBoundingRect();
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
ScxmlUiFactory *uiFactory() const;
|
||||
|
||||
virtual void updateUIProperties();
|
||||
virtual void addChild(ScxmlTag */*tag*/) {};
|
||||
|
||||
protected:
|
||||
virtual void updatePolygon();
|
||||
|
||||
108
src/plugins/scxmleditor/plugin_interface/eventitem.cpp
Normal file
108
src/plugins/scxmleditor/plugin_interface/eventitem.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "eventitem.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
#include <QGraphicsItem>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
namespace ScxmlEditor {
|
||||
|
||||
namespace PluginInterface {
|
||||
|
||||
EventItem::EventItem(const QPointF &pos, BaseItem *parent)
|
||||
: BaseItem(parent)
|
||||
{
|
||||
m_eventNameItem = new TextItem(this);
|
||||
m_eventNameItem->setParentItem(this);
|
||||
QFont serifFont("Times", 13, QFont::Normal);
|
||||
m_eventNameItem->setFont(serifFont);
|
||||
|
||||
QString color = editorInfo("fontColor");
|
||||
m_eventNameItem->setDefaultTextColor(color.isEmpty() ? QColor(Qt::black) : QColor(color));
|
||||
|
||||
setPos(pos);
|
||||
m_eventNameItem->setTextInteractionFlags(Qt::NoTextInteraction);
|
||||
setItemBoundingRect(m_eventNameItem->boundingRect());
|
||||
}
|
||||
|
||||
void EventItem::updateAttributes()
|
||||
{
|
||||
QString text = " " + tag()->tagName();
|
||||
if (tag()->attributeNames().size() > 0) {
|
||||
for (int i = 0; i < tag()->attributeNames().size(); ++i)
|
||||
if (tag()->attributeNames().at(i) == "event") {
|
||||
if (tag()->attributeValues().size() > i)
|
||||
text += " / " + tag()->attributeValues().at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_eventNameItem->setText(text);
|
||||
setItemBoundingRect(m_eventNameItem->boundingRect());
|
||||
}
|
||||
|
||||
OnEntryExitItem::OnEntryExitItem(BaseItem *parent)
|
||||
: BaseItem(parent)
|
||||
{
|
||||
m_eventNameItem = new TextItem(this);
|
||||
m_eventNameItem->setParentItem(this);
|
||||
QFont serifFont("Times", 13, QFont::Normal);
|
||||
m_eventNameItem->setFont(serifFont);
|
||||
m_eventNameItem->setDefaultTextColor(Qt::black);
|
||||
m_eventNameItem->setTextInteractionFlags(Qt::NoTextInteraction);
|
||||
}
|
||||
|
||||
void OnEntryExitItem::updateAttributes()
|
||||
{
|
||||
QString text = tag()->tagName();
|
||||
|
||||
m_eventNameItem->setText(text);
|
||||
setItemBoundingRect(childBoundingRect());
|
||||
checkParentBoundingRect();
|
||||
}
|
||||
|
||||
void OnEntryExitItem::finalizeCreation()
|
||||
{
|
||||
auto children = tag()->allChildren();
|
||||
auto pos = m_eventNameItem->boundingRect().bottomLeft();
|
||||
for (auto child : children) {
|
||||
EventItem *item = new EventItem(pos, this);
|
||||
item->setTag(child);
|
||||
item->updateAttributes();
|
||||
pos = item->pos() + item->boundingRect().bottomLeft();
|
||||
}
|
||||
|
||||
setItemBoundingRect(childBoundingRect());
|
||||
}
|
||||
|
||||
void OnEntryExitItem::addChild(ScxmlTag *tag)
|
||||
{
|
||||
auto pos = childBoundingRect().bottomLeft();
|
||||
EventItem *item = new EventItem(pos, this);
|
||||
item->setTag(tag);
|
||||
item->updateAttributes();
|
||||
|
||||
setItemBoundingRect(childBoundingRect());
|
||||
checkParentBoundingRect();
|
||||
}
|
||||
|
||||
QRectF OnEntryExitItem::childBoundingRect() const
|
||||
{
|
||||
QRectF r = m_eventNameItem->boundingRect();
|
||||
|
||||
const QList<QGraphicsItem *> children = childItems();
|
||||
|
||||
for (const QGraphicsItem *child : children) {
|
||||
QRectF br = child->boundingRect();
|
||||
QPointF p = child->pos() + br.topLeft();
|
||||
br.moveTopLeft(p);
|
||||
r = r.united(br);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace PluginInterface
|
||||
} // namespace ScxmlEditor
|
||||
41
src/plugins/scxmleditor/plugin_interface/eventitem.h
Normal file
41
src/plugins/scxmleditor/plugin_interface/eventitem.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "baseitem.h"
|
||||
#include "textitem.h"
|
||||
|
||||
namespace ScxmlEditor::PluginInterface {
|
||||
|
||||
class EventItem : public BaseItem
|
||||
{
|
||||
public:
|
||||
explicit EventItem(const QPointF &pos = QPointF(), BaseItem *parent = nullptr);
|
||||
|
||||
void updateAttributes() override;
|
||||
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
|
||||
|
||||
private:
|
||||
TextItem *m_eventNameItem;
|
||||
};
|
||||
|
||||
class OnEntryExitItem : public BaseItem
|
||||
{
|
||||
public:
|
||||
explicit OnEntryExitItem(BaseItem *parent = nullptr);
|
||||
|
||||
int type() const override { return StateWarningType; }
|
||||
|
||||
void updateAttributes() override;
|
||||
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override {}
|
||||
void finalizeCreation() override;
|
||||
void addChild(ScxmlTag *tag) override;
|
||||
|
||||
QRectF childBoundingRect() const;
|
||||
|
||||
private:
|
||||
TextItem *m_eventNameItem;
|
||||
};
|
||||
|
||||
} // namespace ScxmlEditor::PluginInterface
|
||||
@@ -467,8 +467,7 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
|
||||
break;
|
||||
}
|
||||
case ScxmlDocument::TagChangeParent: {
|
||||
auto childItem = qobject_cast<ConnectableItem*>(findItem(tag));
|
||||
|
||||
auto childItem = findItem(tag);
|
||||
if (childItem) {
|
||||
QTC_ASSERT(tag, break);
|
||||
BaseItem *newParentItem = findItem(tag->parentTag());
|
||||
@@ -485,8 +484,11 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
|
||||
|
||||
childItem->setParentItem(newParentItem);
|
||||
childItem->updateUIProperties();
|
||||
childItem->updateTransitions(true);
|
||||
childItem->updateTransitionAttributes(true);
|
||||
if (auto childConItem = qobject_cast<ConnectableItem*>(findItem(tag))) {
|
||||
childConItem->updateTransitions(true);
|
||||
childConItem->updateTransitionAttributes(true);
|
||||
}
|
||||
|
||||
childItem->checkWarnings();
|
||||
childItem->checkInitial();
|
||||
if (newParentItem) {
|
||||
@@ -495,6 +497,8 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
|
||||
newParentItem->checkWarnings();
|
||||
newParentItem->checkOverlapping();
|
||||
newParentItem->updateUIProperties();
|
||||
if (auto newConItem = qobject_cast<StateItem*>(newParentItem))
|
||||
newConItem->updateBoundingRect();
|
||||
}
|
||||
|
||||
if (oldParentItem)
|
||||
@@ -549,6 +553,9 @@ void GraphicsScene::endTagChange(ScxmlDocument::TagChange change, ScxmlTag *tag,
|
||||
}
|
||||
|
||||
if (parentItem) {
|
||||
if (childItem == nullptr)
|
||||
parentItem->addChild(childTag);
|
||||
|
||||
parentItem->updateAttributes();
|
||||
parentItem->updateUIProperties();
|
||||
parentItem->checkInitial();
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "finalstateitem.h"
|
||||
#include "stateitem.h"
|
||||
|
||||
#include "eventitem.h"
|
||||
#include "graphicsitemprovider.h"
|
||||
#include "graphicsscene.h"
|
||||
#include "idwarningitem.h"
|
||||
#include "imageprovider.h"
|
||||
#include "initialstateitem.h"
|
||||
#include "parallelitem.h"
|
||||
#include "sceneutils.h"
|
||||
#include "scxmleditorconstants.h"
|
||||
#include "scxmleditortr.h"
|
||||
#include "scxmltagutils.h"
|
||||
#include "scxmluifactory.h"
|
||||
#include "stateitem.h"
|
||||
#include "statewarningitem.h"
|
||||
#include "textitem.h"
|
||||
#include "transitionitem.h"
|
||||
@@ -28,6 +27,7 @@
|
||||
#include <QTextOption>
|
||||
#include <QUndoStack>
|
||||
#include <QtMath>
|
||||
#include <QRubberBand>
|
||||
|
||||
using namespace ScxmlEditor::PluginInterface;
|
||||
|
||||
@@ -171,6 +171,7 @@ void StateItem::updateBoundingRect()
|
||||
|
||||
// Check if we need to increase parent boundingrect
|
||||
if (!r2.isNull()) {
|
||||
positionOnExitItems();
|
||||
QRectF r = boundingRect();
|
||||
QRectF r3 = r.united(r2);
|
||||
|
||||
@@ -244,7 +245,6 @@ void StateItem::transitionCountChanged()
|
||||
QRectF StateItem::childItemsBoundingRect() const
|
||||
{
|
||||
QRectF r;
|
||||
QRectF rr = boundingRect();
|
||||
|
||||
QList<QGraphicsItem*> children = childItems();
|
||||
for (int i = 0; i < children.count(); ++i) {
|
||||
@@ -256,15 +256,26 @@ QRectF StateItem::childItemsBoundingRect() const
|
||||
}
|
||||
}
|
||||
|
||||
if (m_onEntryItem) {
|
||||
QRectF br = m_onEntryItem->childBoundingRect();
|
||||
QPointF p = m_onEntryItem->pos() + br.topLeft();
|
||||
br.moveTopLeft(p);
|
||||
r = r.united(br);
|
||||
}
|
||||
|
||||
if (m_onExitItem) {
|
||||
QRectF br = m_onExitItem->childBoundingRect();
|
||||
QPointF p = m_onExitItem->pos() + br.topLeft();
|
||||
br.moveTopLeft(p);
|
||||
r = r.united(br);
|
||||
}
|
||||
|
||||
if (m_transitionRect.isValid()) {
|
||||
r.setLeft(r.left() - m_transitionRect.width());
|
||||
r.setHeight(qMax(r.height(), m_transitionRect.height()));
|
||||
r.moveBottom(qMax(r.bottom(), m_transitionRect.bottom()));
|
||||
}
|
||||
|
||||
if (!r.isNull())
|
||||
r.adjust(-20, -(rr.height() * 0.06 + 40), 20, 20);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -418,11 +429,18 @@ void StateItem::updatePolygon()
|
||||
<< m_drawingRect.bottomLeft()
|
||||
<< m_drawingRect.topLeft();
|
||||
|
||||
m_titleRect = QRectF(m_drawingRect.left(), m_drawingRect.top(), m_drawingRect.width(), TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
|
||||
m_titleRect = QRectF(m_drawingRect.left(),
|
||||
m_drawingRect.top(),
|
||||
m_drawingRect.width(),
|
||||
TEXT_ITEM_HEIGHT + m_drawingRect.height() * 0.06);
|
||||
QFont f = m_stateNameItem->font();
|
||||
f.setPixelSize(m_titleRect.height() * 0.65);
|
||||
m_stateNameItem->setFont(f);
|
||||
|
||||
if (m_onEntryItem)
|
||||
m_onEntryItem->setPos(m_titleRect.x(), m_titleRect.bottom());
|
||||
positionOnExitItems();
|
||||
|
||||
updateTextPositions();
|
||||
}
|
||||
|
||||
@@ -517,13 +535,41 @@ void StateItem::init(ScxmlTag *tag, BaseItem *parentItem, bool initChildren, boo
|
||||
if (newItem) {
|
||||
newItem->init(child, this, initChildren, blockUpdates);
|
||||
newItem->finalizeCreation();
|
||||
}
|
||||
} else
|
||||
addChild(child);
|
||||
}
|
||||
}
|
||||
if (blockUpdates)
|
||||
setBlockUpdates(false);
|
||||
}
|
||||
|
||||
|
||||
void StateItem::addChild(ScxmlTag *child)
|
||||
{
|
||||
if (child->tagName() == "onentry") {
|
||||
OnEntryExitItem *item = new OnEntryExitItem(this);
|
||||
m_onEntryItem = item;
|
||||
item->setTag(child);
|
||||
item->finalizeCreation();
|
||||
item->updateAttributes();
|
||||
m_onEntryItem->setPos(m_titleRect.x(), m_titleRect.bottom());
|
||||
} else if (child->tagName() == "onexit") {
|
||||
OnEntryExitItem *item = new OnEntryExitItem(this);
|
||||
m_onExitItem = item;
|
||||
item->setTag(child);
|
||||
item->finalizeCreation();
|
||||
item->updateAttributes();
|
||||
positionOnExitItems();
|
||||
}
|
||||
}
|
||||
|
||||
void StateItem::positionOnExitItems()
|
||||
{
|
||||
int offset = m_onEntryItem ? m_onEntryItem->boundingRect().height() : 0;
|
||||
if (m_onExitItem)
|
||||
m_onExitItem->setPos(m_titleRect.x(), m_titleRect.bottom() + offset);
|
||||
}
|
||||
|
||||
QString StateItem::itemId() const
|
||||
{
|
||||
return m_stateNameItem ? m_stateNameItem->toPlainText() : QString();
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "connectableitem.h"
|
||||
#include "textitem.h"
|
||||
#include <QPen>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent)
|
||||
|
||||
@@ -16,6 +18,7 @@ class TransitionItem;
|
||||
class TextItem;
|
||||
class IdWarningItem;
|
||||
class StateWarningItem;
|
||||
class OnEntryExitItem;
|
||||
|
||||
/**
|
||||
* @brief The StateItem class represents the SCXML-State.
|
||||
@@ -49,6 +52,8 @@ public:
|
||||
QRectF childItemsBoundingRect() const;
|
||||
void connectToParent(BaseItem *parentItem) override;
|
||||
|
||||
void addChild(ScxmlTag *child) override;
|
||||
|
||||
protected:
|
||||
void updatePolygon() override;
|
||||
void transitionsChanged() override;
|
||||
@@ -69,6 +74,7 @@ private:
|
||||
void updateTextPositions();
|
||||
void checkParentBoundingRect();
|
||||
void checkWarningItems();
|
||||
void positionOnExitItems();
|
||||
|
||||
TextItem *m_stateNameItem;
|
||||
StateWarningItem *m_stateWarningItem = nullptr;
|
||||
@@ -76,6 +82,8 @@ private:
|
||||
QPen m_pen;
|
||||
bool m_initial = false;
|
||||
bool m_parallelState = false;
|
||||
QPointer<OnEntryExitItem> m_onEntryItem;
|
||||
QPointer<OnEntryExitItem> m_onExitItem;
|
||||
QImage m_backgroundImage;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user