KDStateMachineEditor API Documentation 2.3
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 event->pos(), event->globalPosition().toPoint(), event->modifiers(),
139 element());
140 QCoreApplication::sendEvent(scene(), &contextMenuEvent);
141 }
142}
143
144bool QuickSceneItem::contains(const QPointF &point) const
145{
146 if (m_shape.isEmpty()) {
147 return QQuickItem::contains(point);
148 }
149
150 return m_shape.contains(point);
151}
152
154{
155 if (m_element == element)
156 return;
157
158 if (m_element) {
159 m_element->disconnect(this);
160 }
161
162 m_element = element;
163
164 if (m_element) {
165 setWidth(m_element->width());
166 setHeight(m_element->height());
167 setOpacity(m_element->isVisible());
168 connect(m_element, &Element::widthChanged, this, &QQuickItem::setWidth);
169 connect(m_element, &Element::heightChanged, this, &QQuickItem::setHeight);
170 connect(m_element, &Element::visibleChanged, this, &QQuickItem::setOpacity);
171 connect(this, &QQuickItem::opacityChanged, m_element, [=]() {
172 m_element->setVisible(opacity() > 0);
173 });
174 }
175
176 Q_EMIT elementChanged(m_element);
177}
178
180{
181 return m_activeness;
182}
183
184void QuickSceneItem::setActiveness(qreal activeness)
185{
186 if (qFuzzyCompare(m_activeness, activeness))
187 return;
188
189 m_activeness = activeness;
190 Q_EMIT activenessChanged(m_activeness);
191}
192
194 : QuickSceneItem(parent)
195{
196}
197
199 : QuickSceneItem(parent)
200{
201}
202
204{
205 if (element == this->element()) {
206 return;
207 }
208
209 auto transition = qobject_cast<Transition *>(element);
210 if (element && !transition) {
211 qCWarning(KDSME_VIEW) << "Set invalid element on QuickEdgeItem:" << element;
212 return;
213 }
214
215 if (auto previousTransition = toTransition()) {
216 previousTransition->sourceState()->disconnect(this);
217 previousTransition->targetState()->disconnect(this);
218 previousTransition->disconnect(this);
219 }
220
222
223 if (transition) {
224 connect(transition, &Transition::sourceStateChanged,
225 this, &QuickTransitionItem::updateSource);
226 connect(transition, &Transition::targetStateChanged,
227 this, &QuickTransitionItem::updateTarget);
228 }
229
230 updateSource();
231 updateTarget();
232}
233
234void QuickTransitionItem::updatePosition()
235{
236 if (scene()->viewState() == StateMachineScene::RefreshState) {
237 return;
238 }
239
240 auto transition = toTransition();
241 Q_ASSERT(transition);
242 const auto sourceState = transition->sourceState();
243 const auto targetState = transition->targetState();
244 if (sourceState == targetState)
245 return;
246
247 const auto sourceStateItem = itemForElement(sourceState);
248 const auto targetStateItem = itemForElement(targetState);
249
250 const QRectF startRect(mapFromItem(sourceStateItem, QPointF(0, 0)),
251 QSizeF(sourceStateItem->width(), sourceStateItem->height()));
252 const QRectF endRect(mapFromItem(targetStateItem, QPointF(0, 0)),
253 QSizeF(targetStateItem->width(), targetStateItem->height()));
254
255 // const auto shape = transition->shape();
256 const auto labelBoundingRect = transition->labelBoundingRect();
257
258 const auto preliminaryEdge = QLineF(startRect.center(), endRect.center());
259 const auto startPoint = intersected(preliminaryEdge, startRect);
260 Q_ASSERT(!startPoint.isNull());
261 const auto endPoint = intersected(preliminaryEdge, endRect);
262 Q_ASSERT(!endPoint.isNull());
263
264 QPainterPath newShape(startPoint);
265 newShape.lineTo(endPoint);
266 transition->setShape(newShape);
267
268 const auto midPoint = newShape.pointAtPercent(0.5);
269 const qreal angle = newShape.angleAtPercent(0.5);
270 QRectF newLabelBoundingRect(labelBoundingRect);
271 if (angle < 90) {
272 newLabelBoundingRect.moveTopLeft(midPoint);
273 } else if (angle < 180) {
274 newLabelBoundingRect.moveBottomLeft(midPoint);
275 } else if (angle < 270) {
276 newLabelBoundingRect.moveBottomRight(midPoint);
277 } else {
278 newLabelBoundingRect.moveTopRight(midPoint);
279 }
280 transition->setLabelBoundingRect(newLabelBoundingRect);
281}
282
283void QuickTransitionItem::updateSource()
284{
285 const auto *transition = toTransition();
286 if (!transition)
287 return;
288
289 disconnect(m_sourceStateConnection);
290 if (const auto *source = toTransition()->sourceState()) {
291 m_sourceStateConnection = connect(source, &Element::posChanged,
292 this, &QuickTransitionItem::updatePosition);
293 }
294}
295
296void QuickTransitionItem::updateTarget()
297{
298 const auto *transition = toTransition();
299 if (!transition)
300 return;
301
302 disconnect(m_targetStateConnection);
303 if (const auto *targetState = toTransition()->targetState()) {
304 m_targetStateConnection = connect(targetState, &Element::posChanged,
305 this, &QuickTransitionItem::updatePosition);
306 }
307}
308
309Transition *QuickTransitionItem::toTransition() const
310{
311 return static_cast<Transition *>(element());
312}
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 Mon Mar 9 2026 12:16:36 for KDStateMachineEditor API Documentation by doxygen 1.9.8