Mercurial > projects > qtd
comparison demos/shared/hoverpoints.d @ 41:691e68637348
non-working deform example
author | maxter |
---|---|
date | Sun, 17 May 2009 12:41:14 +0000 |
parents | |
children | 4bbd9f3d9add |
comparison
equal
deleted
inserted
replaced
40:a5cc4ada07f5 | 41:691e68637348 |
---|---|
1 /**************************************************************************** | |
2 ** | |
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). | |
4 ** Contact: Qt Software Information (qt-info@nokia.com) | |
5 ** | |
6 ** This file is part of the demonstration applications of the Qt Toolkit. | |
7 ** | |
8 ** $QT_BEGIN_LICENSE:LGPL$ | |
9 ** Commercial Usage | |
10 ** Licensees holding valid Qt Commercial licenses may use this file in | |
11 ** accordance with the Qt Commercial License Agreement provided with the | |
12 ** Software or, alternatively, in accordance with the terms contained in | |
13 ** a written agreement between you and Nokia. | |
14 ** | |
15 ** GNU Lesser General Public License Usage | |
16 ** Alternatively, this file may be used under the terms of the GNU Lesser | |
17 ** General Public License version 2.1 as published by the Free Software | |
18 ** Foundation and appearing in the file LICENSE.LGPL included in the | |
19 ** packaging of this file. Please review the following information to | |
20 ** ensure the GNU Lesser General Public License version 2.1 requirements | |
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
22 ** | |
23 ** In addition, as a special exception, Nokia gives you certain | |
24 ** additional rights. These rights are described in the Nokia Qt LGPL | |
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this | |
26 ** package. | |
27 ** | |
28 ** GNU General Public License Usage | |
29 ** Alternatively, this file may be used under the terms of the GNU | |
30 ** General Public License version 3.0 as published by the Free Software | |
31 ** Foundation and appearing in the file LICENSE.GPL included in the | |
32 ** packaging of this file. Please review the following information to | |
33 ** ensure the GNU General Public License version 3.0 requirements will be | |
34 ** met: http://www.gnu.org/copyleft/gpl.html. | |
35 ** | |
36 ** If you are unsure which license is appropriate for your use, please | |
37 ** contact the sales department at qt-sales@nokia.com. | |
38 ** $QT_END_LICENSE$ | |
39 ** | |
40 ****************************************************************************/ | |
41 | |
42 version (QT_OPENGL_SUPPORT) | |
43 import qt.opengl.QGLWidget; | |
44 | |
45 version (D_Version2) {} | |
46 else | |
47 import tango.core.Array : sort; | |
48 | |
49 import | |
50 qt.gui.QWidget, | |
51 qt.qtd.Array, | |
52 arthurwidgets; | |
53 | |
54 final class HoverPoints : QObject | |
55 { | |
56 public: | |
57 enum PointShape { | |
58 CircleShape, | |
59 RectangleShape | |
60 } | |
61 | |
62 enum LockType { | |
63 LockToLeft = 0x01, | |
64 LockToRight = 0x02, | |
65 LockToTop = 0x04, | |
66 LockToBottom = 0x08 | |
67 } | |
68 | |
69 enum SortType { | |
70 NoSort, | |
71 XSort, | |
72 YSort | |
73 } | |
74 | |
75 enum ConnectionType { | |
76 NoConnection, | |
77 LineConnection, | |
78 CurveConnection | |
79 } | |
80 | |
81 private: | |
82 QWidget m_widget; | |
83 | |
84 QPolygonF m_points; | |
85 QRectF m_bounds; | |
86 PointShape m_shape; | |
87 SortType m_sortType; | |
88 ConnectionType m_connectionType; | |
89 | |
90 uint[] m_locks; | |
91 | |
92 QSizeF m_pointSize; | |
93 int m_currentIndex; | |
94 bool m_editable; | |
95 bool m_enabled; | |
96 | |
97 QPen m_pointPen; | |
98 QBrush m_pointBrush; | |
99 QPen m_connectionPen; | |
100 | |
101 public: | |
102 mixin Signal!("pointsChanged", QPolygonF /*points*/); | |
103 | |
104 this(QWidget widget, PointShape shape) | |
105 { | |
106 super(widget); | |
107 | |
108 m_widget = widget; | |
109 widget.installEventFilter(this); | |
110 | |
111 m_connectionType = ConnectionType.CurveConnection; | |
112 m_sortType = SortType.NoSort; | |
113 m_shape = shape; | |
114 m_pointPen = new QPen(new QBrush(new QColor(255, 255, 255, 191)), 1); | |
115 m_connectionPen = new QPen(new QBrush(new QColor(255, 255, 255, 127)), 2); | |
116 m_pointBrush = new QBrush(new QColor(191, 191, 191, 127)); | |
117 m_pointSize = QSizeF(11, 11); | |
118 m_currentIndex = -1; | |
119 m_editable = true; | |
120 m_enabled = true; | |
121 | |
122 pointsChanged.connect(&m_widget.update); | |
123 } | |
124 | |
125 void setBoundingRect(QRectF boundingRect) { m_bounds = boundingRect; } | |
126 | |
127 | |
128 QRectF pointBoundingRect(int i) | |
129 { | |
130 QPointF p = m_points.at(i); | |
131 qreal w = m_pointSize.width(); | |
132 qreal h = m_pointSize.height(); | |
133 qreal x = p.x() - w / 2; | |
134 qreal y = p.y() - h / 2; | |
135 return new QRectF(x, y, w, h); | |
136 } | |
137 | |
138 QRectF boundingRect() | |
139 { | |
140 if (m_bounds.isEmpty()) | |
141 return new QRectF(m_widget.rect()); | |
142 else | |
143 return m_bounds; | |
144 } | |
145 | |
146 QPolygonF points() { return m_points; } | |
147 | |
148 QSizeF pointSize() { return m_pointSize; } | |
149 void setPointSize(QSizeF size) { m_pointSize = size; } | |
150 | |
151 SortType sortType() { return m_sortType; } | |
152 void setSortType(SortType sortType) { m_sortType = sortType; } | |
153 | |
154 ConnectionType connectionType() { return m_connectionType; } | |
155 void setConnectionType(ConnectionType connectionType) { m_connectionType = connectionType; } | |
156 | |
157 void setConnectionPen(QPen pen) { m_connectionPen = pen; } | |
158 void setShapePen(QPen pen) { m_pointPen = pen; } | |
159 void setShapeBrush(QBrush brush) { m_pointBrush = brush; } | |
160 | |
161 void setPointLock(int pos, LockType lock) { m_locks[pos] = lock; } | |
162 | |
163 void setEditable(bool editable) { m_editable = editable; } | |
164 bool editable() { return m_editable; } | |
165 | |
166 void setEnabled(bool enabled) | |
167 { | |
168 if (m_enabled != enabled) { | |
169 m_enabled = enabled; | |
170 m_widget.update(); | |
171 } | |
172 } | |
173 | |
174 | |
175 override bool eventFilter(QObject object, QEvent event) | |
176 { | |
177 if ((object == m_widget) && m_enabled) { | |
178 switch (event.type()) { | |
179 | |
180 case QEvent.MouseButtonPress: | |
181 { | |
182 QMouseEvent me = cast(QMouseEvent) event; | |
183 | |
184 QPointF clickPos = me.pos(); | |
185 int index = -1; | |
186 for (int i=0; i<m_points.size(); ++i) { | |
187 auto path = new QPainterPath; | |
188 if (m_shape == PointShape.CircleShape) | |
189 path.addEllipse(pointBoundingRect(i)); | |
190 else | |
191 path.addRect(pointBoundingRect(i)); | |
192 | |
193 if (path.contains(clickPos)) { | |
194 index = i; | |
195 break; | |
196 } | |
197 } | |
198 | |
199 if (me.button() == Qt.LeftButton) { | |
200 if (index == -1) { | |
201 if (!m_editable) | |
202 return false; | |
203 int pos = 0; | |
204 // Insert sort for x or y | |
205 if (m_sortType == SortType.XSort) { | |
206 for (int i=0; i<m_points.size(); ++i) | |
207 if (m_points.at(i).x() > clickPos.x()) { | |
208 pos = i; | |
209 break; | |
210 } | |
211 } else if (m_sortType == SortType.YSort) { | |
212 for (int i=0; i<m_points.size(); ++i) | |
213 if (m_points.at(i).y() > clickPos.y()) { | |
214 pos = i; | |
215 break; | |
216 } | |
217 } | |
218 | |
219 // TODO: implement QPoligon(F).insert | |
220 auto tmpPoints = m_points.toList; | |
221 tmpPoints.insert(pos, clickPos); | |
222 m_points = new QPolygonF(tmpPoints); | |
223 | |
224 m_locks.insert(pos, 0u); | |
225 m_currentIndex = pos; | |
226 firePointChange(); | |
227 } else { | |
228 m_currentIndex = index; | |
229 } | |
230 return true; | |
231 | |
232 } else if (me.button() == Qt.RightButton) { | |
233 if ((index >= 0) && m_editable) { | |
234 if (m_locks[index] == 0) { | |
235 m_locks.removeAt(index); | |
236 m_points.remove(index); | |
237 } | |
238 firePointChange(); | |
239 return true; | |
240 } | |
241 } | |
242 | |
243 } | |
244 break; | |
245 | |
246 case QEvent.MouseButtonRelease: | |
247 m_currentIndex = -1; | |
248 break; | |
249 | |
250 case QEvent.MouseMove: | |
251 if (m_currentIndex >= 0) | |
252 movePoint(m_currentIndex, QPointF((cast(QMouseEvent)event).pos)); | |
253 break; | |
254 | |
255 case QEvent.Resize: | |
256 { | |
257 QResizeEvent e = cast(QResizeEvent) event; | |
258 if (e.oldSize().width() == 0 || e.oldSize().height() == 0) | |
259 break; | |
260 qreal stretch_x = e.size().width() / cast(qreal)e.oldSize.width; | |
261 qreal stretch_y = e.size().height() / cast(qreal)e.oldSize.height; | |
262 for (int i=0; i<m_points.size(); ++i) { | |
263 QPointF p = m_points.at(i); | |
264 movePoint(i, QPointF(p.x() * stretch_x, p.y() * stretch_y), false); | |
265 } | |
266 | |
267 firePointChange(); | |
268 break; | |
269 } | |
270 | |
271 case QEvent.Paint: | |
272 { | |
273 QWidget that_widget = m_widget; | |
274 m_widget = null; | |
275 QApplication.sendEvent(object, event); | |
276 m_widget = that_widget; | |
277 paintPoints(); | |
278 version (QT_OPENGL_SUPPORT) | |
279 { | |
280 ArthurFrame af = cast(ArthurFrame)(that_widget); | |
281 if (af && af.usesOpenGL()) | |
282 af.glWidget().swapBuffers(); | |
283 } | |
284 | |
285 return true; | |
286 } | |
287 default: | |
288 break; | |
289 } | |
290 } | |
291 | |
292 return false; | |
293 } | |
294 | |
295 | |
296 void paintPoints() | |
297 { | |
298 scope p = new QPainter; | |
299 version (QT_OPENGL_SUPPORT) | |
300 { | |
301 ArthurFrame af = cast(ArthurFrame)(m_widget); | |
302 if (af && af.usesOpenGL()) | |
303 p.begin(af.glWidget()); | |
304 else | |
305 p.begin(m_widget); | |
306 } | |
307 else | |
308 p.begin(m_widget); | |
309 | |
310 p.setRenderHint(QPainter.Antialiasing); | |
311 | |
312 if (m_connectionPen.style() != Qt.NoPen && m_connectionType != ConnectionType.NoConnection) { | |
313 p.setPen(m_connectionPen); | |
314 | |
315 if (m_connectionType == ConnectionType.CurveConnection) { | |
316 auto path = new QPainterPath; | |
317 path.moveTo(m_points.at(0)); | |
318 for (int i=1; i<m_points.size(); ++i) { | |
319 QPointF p1 = m_points.at(i-1); | |
320 QPointF p2 = m_points.at(i); | |
321 qreal distance = p2.x() - p1.x(); | |
322 | |
323 path.cubicTo(p1.x() + distance / 2, p1.y(), | |
324 p1.x() + distance / 2, p2.y(), | |
325 p2.x(), p2.y()); | |
326 } | |
327 p.drawPath(path); | |
328 } else { | |
329 p.drawPolyline(m_points); | |
330 } | |
331 } | |
332 | |
333 p.setPen(m_pointPen); | |
334 p.setBrush(m_pointBrush); | |
335 | |
336 for (int i=0; i<m_points.size(); ++i) { | |
337 QRectF bounds = pointBoundingRect(i); | |
338 if (m_shape == PointShape.CircleShape) | |
339 p.drawEllipse(bounds); | |
340 else | |
341 p.drawRect(bounds); | |
342 } | |
343 } | |
344 | |
345 | |
346 void setPoints(QPolygonF points) | |
347 { | |
348 delete m_points; | |
349 for (int i=0; i<points.size; ++i) | |
350 m_points.append(bound_point(points.at(i), boundingRect(), 0)); | |
351 | |
352 delete m_locks; | |
353 if (m_points.size > 0) { | |
354 m_locks.length = m_points.size; | |
355 | |
356 m_locks[] = 0; | |
357 } | |
358 } | |
359 | |
360 void movePoint(int index, QPointF point, bool emitUpdate = true) | |
361 { | |
362 m_points.replace(index, bound_point(point, boundingRect(), m_locks[index])); | |
363 if (emitUpdate) | |
364 firePointChange(); | |
365 } | |
366 | |
367 void firePointChange() | |
368 { | |
369 // printf("HoverPoints.firePointChange(), current=%d\n", m_currentIndex); | |
370 | |
371 if (m_sortType != SortType.NoSort) { | |
372 | |
373 QPointF oldCurrent; | |
374 if (m_currentIndex != -1) { | |
375 oldCurrent = m_points.at(m_currentIndex); | |
376 } | |
377 | |
378 if (m_sortType == SortType.XSort) | |
379 { | |
380 auto tmpPoints = m_points.toList; | |
381 sort(tmpPoints, &x_less_than); | |
382 m_points = new QPolygonF(tmpPoints); | |
383 } | |
384 else if (m_sortType == SortType.YSort) | |
385 { | |
386 auto tmpPoints = m_points.toList; | |
387 sort(tmpPoints, &y_less_than); | |
388 m_points = new QPolygonF(tmpPoints); | |
389 } | |
390 | |
391 // Compensate for changed order... | |
392 if (m_currentIndex != -1) { | |
393 for (int i=0; i<m_points.size; ++i) { | |
394 if (m_points.at(i) == oldCurrent) { | |
395 m_currentIndex = i; | |
396 break; | |
397 } | |
398 } | |
399 } | |
400 | |
401 // printf(" - firePointChange(), current=%d\n", m_currentIndex); | |
402 } | |
403 | |
404 // for (int i=0; i<m_points.size(); ++i) { | |
405 // printf(" - point(%2d)=[%.2f, %.2f], lock=%d\n", | |
406 // i, m_points.at(i).x(), m_points.at(i).y(), m_locks.at(i)); | |
407 // } | |
408 | |
409 pointsChanged.emit(m_points); | |
410 } | |
411 } | |
412 | |
413 private QPointF bound_point(QPointF point, QRectF bounds, int lock) | |
414 { | |
415 QPointF p = point; | |
416 | |
417 qreal left = bounds.left(); | |
418 qreal right = bounds.right(); | |
419 qreal top = bounds.top(); | |
420 qreal bottom = bounds.bottom(); | |
421 | |
422 if (p.x() < left || (lock & HoverPoints.LockType.LockToLeft)) p.x = left; | |
423 else if (p.x() > right || (lock & HoverPoints.LockType.LockToRight)) p.x = right; | |
424 | |
425 if (p.y() < top || (lock & HoverPoints.LockType.LockToTop)) p.y = top; | |
426 else if (p.y() > bottom || (lock & HoverPoints.LockType.LockToBottom)) p.y = bottom; | |
427 | |
428 return p; | |
429 } | |
430 | |
431 private bool x_less_than(QPointF p1, QPointF p2) | |
432 { | |
433 return p1.x() < p2.x(); | |
434 } | |
435 | |
436 private bool y_less_than(QPointF p1, QPointF p2) | |
437 { | |
438 return p1.y() < p2.y(); | |
439 } |