KDStateMachineEditor API Documentation 2.1
Loading...
Searching...
No Matches
quicksceneitem.cpp
Go to the documentation of this file.
1/*
2 This file is part of the KDAB State Machine Editor Library.
3
4 SPDX-FileCopyrightText: 2014 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: Kevin Funk <kevin.funk@kdab.com>
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LicenseRef-KDAB-KDStateMachineEditor
8
9 Licensees holding valid commercial KDAB State Machine Editor Library
10 licenses may use this file in accordance with the KDAB State Machine Editor
11 Library License Agreement provided with the Software.
12
13 Contact info@kdab.com if any conditions of this licensing are not clear to you.
14*/
15
16#include "quicksceneitem_p.h"
17
18#include "abstractscene_p.h"
19#include "debug.h"
20#include "objecttreemodel.h"
21#include "state.h"
22#include "statemachinescene_p.h"
23#include "transition.h"
24
25#include <QPainterPath>
26
27using namespace KDSME;
28
29namespace {
30
31QLineF::IntersectionType intersects(const QLineF &line1, const QLineF &line2, QPointF *intersectionPoint)
32{
33 return line1.intersects(line2, intersectionPoint);
34}
35
39QPointF intersected(const QLineF &line, const QRectF &rect)
40{
41 QPointF point;
42 if (intersects(line, QLineF(rect.topLeft(), rect.topRight()), &point) == QLineF::BoundedIntersection)
43 return point;
44 if (intersects(line, QLineF(rect.topRight(), rect.bottomRight()), &point) == QLineF::BoundedIntersection)
45 return point;
46 if (intersects(line, QLineF(rect.bottomRight(), rect.bottomLeft()), &point) == QLineF::BoundedIntersection)
47 return point;
48 if (intersects(line, QLineF(rect.bottomLeft(), rect.topLeft()), &point) == QLineF::BoundedIntersection)
49 return point;
50 return point;
51}
52
53}
54
56 : QQuickItem(parent)
57 , m_scene(nullptr)
58 , m_element(nullptr)
59 , m_activeness(0.)
60{
61 setAcceptHoverEvents(true);
62 setAcceptedMouseButtons(Qt::AllButtons);
63}
64
68
69QQuickItem *QuickSceneItem::itemForElement(Element *element) const
70{
71 // TODO: Ugly, decouple from ObjectTreeModel
72 auto model = qobject_cast<ObjectTreeModel *>(scene()->model());
73 auto index = model->indexForObject(element);
74 Q_ASSERT(index.isValid());
75 auto object = scene()->itemForIndex(index);
76 Q_ASSERT(object);
77 auto sceneItem = qobject_cast<QQuickItem *>(object);
78 Q_ASSERT(sceneItem);
79 return sceneItem;
80}
81
82void QuickSceneItem::sendClickEvent() // NOLINT(readability-make-member-function-const)
83{
84 // TODO: Send events to scene instead?
86}
87
89{
90 return m_scene;
91}
92
94{
95 Q_ASSERT(scene);
96 Q_ASSERT(!m_scene || scene == m_scene);
97
98 m_scene = scene;
99}
100
102{
103 return m_element;
104}
105
106QPainterPath QuickSceneItem::shape() const
107{
108 return m_shape;
109}
110
111void QuickSceneItem::setShape(const QPainterPath &shape)
112{
113 if (m_shape == shape) {
114 return;
115 }
116
117 m_shape = shape;
118 Q_EMIT shapeChanged(m_shape);
119}
120
121void QuickSceneItem::mousePressEvent(QMouseEvent *event)
122{
123 Q_UNUSED(event);
124}
125
126void QuickSceneItem::mouseReleaseEvent(QMouseEvent *event)
127{
128 if (event->button() == Qt::RightButton) {
129 // note: ideally we'd send the context menu event when we're in the mousePressEvent handler
130 // but this leads to unexpected behavior in the QQuickWindow implementation.
131 // when showing a context menu (i.e. QMenu::exec) from within mousePressEvent,
132 // the QQuickWindow will never get the mouse release event.
133 // Internally QQuickWindow then assumes the currently clicked item is
134 // still actively handling further mouse input, thus are unable to
135 // select other items in the scene until we reset the QQuickWindow's state
136 AbstractSceneContextMenuEvent contextMenuEvent(
137 QContextMenuEvent::Mouse,
138#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
139 event->pos(), event->globalPosition().toPoint(), event->modifiers(),
140#else
141 event->pos(), event->globalPos(), event->modifiers(),
142#endif
143 element());
144 QCoreApplication::sendEvent(scene(), &contextMenuEvent);
145 }
146}
147
148bool QuickSceneItem::contains(const QPointF &point) const
149{
150 if (m_shape.isEmpty()) {
151 return QQuickItem::contains(point);
152 }
153
154 return m_shape.contains(point);
155}
156
158{
159 if (m_element == element)
160 return;
161
162 if (m_element) {
163 m_element->disconnect(this);
164 }
165
166 m_element = element;
167
168 if (m_element) {
169 setWidth(m_element->width());
170 setHeight(m_element->height());
171 setOpacity(m_element->isVisible());
172 connect(m_element, &Element::widthChanged, this, &QQuickItem::setWidth);
173 connect(m_element, &Element::heightChanged, this, &QQuickItem::setHeight);
174 connect(m_element, &Element::visibleChanged, this, &QQuickItem::setOpacity);
175 connect(this, &QQuickItem::opacityChanged, m_element, [=]() {
176 m_element->setVisible(opacity() > 0);
177 });
178 }
179
180 Q_EMIT elementChanged(m_element);
181}
182
184{
185 return m_activeness;
186}
187
188void QuickSceneItem::setActiveness(qreal activeness)
189{
190 if (qFuzzyCompare(m_activeness, activeness))
191 return;
192
193 m_activeness = activeness;
194 Q_EMIT activenessChanged(m_activeness);
195}
196
198 : QuickSceneItem(parent)
199{
200}
201
203 : QuickSceneItem(parent)
204{
205}
206
208{
209 if (element == this->element()) {
210 return;
211 }
212
213 auto transition = qobject_cast<Transition *>(element);
214 if (element && !transition) {
215 qCWarning(KDSME_VIEW) << "Set invalid element on QuickEdgeItem:" << element;
216 return;
217 }
218
219 if (auto previousTransition = toTransition()) {
220 previousTransition->sourceState()->disconnect(this);
221 previousTransition->targetState()->disconnect(this);
222 previousTransition->disconnect(this);
223 }
224
226
227 if (transition) {
228 connect(transition, &Transition::sourceStateChanged,
229 this, &QuickTransitionItem::updateSource);
230 connect(transition, &Transition::targetStateChanged,
231 this, &QuickTransitionItem::updateTarget);
232 }
233
234 updateSource();
235 updateTarget();
236}
237
238void QuickTransitionItem::updatePosition()
239{
240 if (scene()->viewState() == StateMachineScene::RefreshState) {
241 return;
242 }
243
244 auto transition = toTransition();
245 Q_ASSERT(transition);
246 const auto sourceState = transition->sourceState();
247 const auto targetState = transition->targetState();
248 if (sourceState == targetState)
249 return;
250
251 const auto sourceStateItem = itemForElement(sourceState);
252 const auto targetStateItem = itemForElement(targetState);
253
254 const QRectF startRect(mapFromItem(sourceStateItem, QPointF(0, 0)),
255 QSizeF(sourceStateItem->width(), sourceStateItem->height()));
256 const QRectF endRect(mapFromItem(targetStateItem, QPointF(0, 0)),
257 QSizeF(targetStateItem->width(), targetStateItem->height()));
258
259 // const auto shape = transition->shape();
260 const auto labelBoundingRect = transition->labelBoundingRect();
261
262 const auto preliminaryEdge = QLineF(startRect.center(), endRect.center());
263 const auto startPoint = intersected(preliminaryEdge, startRect);
264 Q_ASSERT(!startPoint.isNull());
265 const auto endPoint = intersected(preliminaryEdge, endRect);
266 Q_ASSERT(!endPoint.isNull());
267
268 QPainterPath newShape(startPoint);
269 newShape.lineTo(endPoint);
270 transition->setShape(newShape);
271
272 const auto midPoint = newShape.pointAtPercent(0.5);
273 const qreal angle = newShape.angleAtPercent(0.5);
274 QRectF newLabelBoundingRect(labelBoundingRect);
275 if (angle < 90) {
276 newLabelBoundingRect.moveTopLeft(midPoint);
277 } else if (angle < 180) {
278 newLabelBoundingRect.moveBottomLeft(midPoint);
279 } else if (angle < 270) {
280 newLabelBoundingRect.moveBottomRight(midPoint);
281 } else {
282 newLabelBoundingRect.moveTopRight(midPoint);
283 }
284 transition->setLabelBoundingRect(newLabelBoundingRect);
285}
286
287void QuickTransitionItem::updateSource()
288{
289 const auto *transition = toTransition();
290 if (!transition)
291 return;
292
293 disconnect(m_sourceStateConnection);
294 if (const auto *source = toTransition()->sourceState()) {
295 m_sourceStateConnection = connect(source, &Element::posChanged,
296 this, &QuickTransitionItem::updatePosition);
297 }
298}
299
300void QuickTransitionItem::updateTarget()
301{
302 const auto *transition = toTransition();
303 if (!transition)
304 return;
305
306 disconnect(m_targetStateConnection);
307 if (const auto *targetState = toTransition()->targetState()) {
308 m_targetStateConnection = connect(targetState, &Element::posChanged,
309 this, &QuickTransitionItem::updatePosition);
310 }
311}
312
313Transition *QuickTransitionItem::toTransition() const
314{
315 return static_cast<Transition *>(element());
316}
QObject * itemForIndex(const QModelIndex &index) const
void visibleChanged(bool visible)
void heightChanged(qreal height)
void posChanged(const QPointF &pos)
qreal width
Definition element.h:43
qreal height
Definition element.h:44
void setVisible(bool visible)
Definition element.cpp:150
bool isVisible() const
Definition element.cpp:145
void widthChanged(qreal width)
void setCurrentItem(KDSME::Element *item)
void sourceStateChanged(KDSME::State *sourceState)
void targetStateChanged(KDSME::State *targetState)
bool contains(const QPointF &point) const override
void setShape(const QPainterPath &shape)
virtual void setScene(KDSME::StateMachineScene *scene)
QPainterPath shape
KDSME::StateMachineScene * scene
void activenessChanged(qreal activeness)
QuickSceneItem(QQuickItem *parent=nullptr)
QQuickItem * itemForElement(KDSME::Element *element) const
void setActiveness(qreal activeness)
void shapeChanged(const QPainterPath &shape)
void elementChanged(KDSME::Element *element)
void mouseReleaseEvent(QMouseEvent *event) override
void mousePressEvent(QMouseEvent *event) override
Q_INVOKABLE void sendClickEvent()
virtual void setElement(KDSME::Element *element)
KDSME::Element * element
QuickStateItem(QQuickItem *parent=nullptr)
QuickTransitionItem(QQuickItem *parent=nullptr)
void setElement(KDSME::Element *element) override

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDStateMachineEditor
Create Qt State Machine metacode using a graphical user interface
https://github.com/KDAB/KDStateMachineEditor
Generated on Tue Jul 15 2025 15:21:47 for KDStateMachineEditor API Documentation by doxygen 1.9.8