1 //
2 // QtQuickFGCanvasItem.cxx
3 //
4 // Copyright (C) 2017 James Turner <zakalawe@mac.com>
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
20 #include "QtQuickFGCanvasItem.hxx"
21
22 #include <QDebug>
23 #include <QSGSimpleTextureNode>
24
25 #include <simgear/canvas/Canvas.hxx>
26 #include <simgear/canvas/events/MouseEvent.hxx>
27 #include <simgear/debug/logstream.hxx>
28
29 #include <Main/globals.hxx>
30 #include <Scripting/NasalSys.hxx>
31
32 namespace sc = simgear::canvas;
33
34 namespace {
35
36 class CanvasTexture : public QSGDynamicTexture
37 {
38 public:
CanvasTexture(sc::CanvasPtr canvas)39 CanvasTexture(sc::CanvasPtr canvas)
40 {
41 _manager = globals->get_subsystem<CanvasMgr>();
42 setHorizontalWrapMode(QSGTexture::ClampToEdge);
43 setVerticalWrapMode(QSGTexture::ClampToEdge);
44 }
45
updateTexture()46 bool updateTexture() override
47 {
48 // no-op, canvas is updated by OSG before QtQuick rendering occurs
49 // this would change if we wanted QtQuick to drive Canvas
50 // updating, which we don't
51 // but do return true so QQ knows to repaint / re-cache
52 return true;
53 }
54
hasMipmaps() const55 bool hasMipmaps() const override
56 {
57 return false;
58 }
59
hasAlphaChannel() const60 bool hasAlphaChannel() const override
61 {
62 return false; // for now, can Canvas have an alpha channel?
63 }
64
bind()65 void bind() override
66 {
67 glBindTexture(GL_TEXTURE_2D, _manager->getCanvasTexId(_canvas));
68 }
69
textureId() const70 int textureId() const override
71 {
72 return _manager->getCanvasTexId(_canvas);
73 }
74
textureSize() const75 QSize textureSize() const override
76 {
77 SGPropertyNode* cprops = _canvas->getProps();
78 return QSize(cprops->getIntValue("value[0]"),
79 cprops->getIntValue("value[1]"));
80 }
81 private:
82 CanvasMgr* _manager;
83 sc::CanvasPtr _canvas;
84
85 };
86
87 } // of anonymous namespace
88
convertQtButtonToCanvas(Qt::MouseButton button)89 static int convertQtButtonToCanvas(Qt::MouseButton button)
90 {
91 switch (button) {
92 case Qt::LeftButton: return 0;
93 case Qt::MiddleButton: return 1;
94 case Qt::RightButton: return 2;
95 default:
96 break;
97 }
98
99 qWarning() << Q_FUNC_INFO << "unmapped Qt mouse button" << button;
100 return 0;
101 }
102
QtQuickFGCanvasItem(QQuickItem * parent)103 QtQuickFGCanvasItem::QtQuickFGCanvasItem(QQuickItem *parent)
104 {
105 }
106
~QtQuickFGCanvasItem()107 QtQuickFGCanvasItem::~QtQuickFGCanvasItem()
108 {
109 if ( _canvas ) {
110 _canvas->destroy();
111 }
112 }
113
updatePaintNode(QSGNode * oldNode,QQuickItem::UpdatePaintNodeData *)114 QSGNode *QtQuickFGCanvasItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
115 {
116 QSGSimpleTextureNode* texNode = static_cast<QSGSimpleTextureNode*>(oldNode);
117 if (!texNode) {
118 texNode = new QSGSimpleTextureNode;
119
120 CanvasTexture* texture = new CanvasTexture(_canvas);
121 texNode->setTexture(texture);
122 }
123
124 // or should this simply be the geometry?
125 texNode->setRect(QRectF(0.0, 0.0, width(), height()));
126
127 return texNode;
128 }
129
convertMouseEvent(QMouseEvent * event)130 sc::MouseEventPtr QtQuickFGCanvasItem::convertMouseEvent(QMouseEvent* event)
131 {
132 sc::MouseEventPtr canvasEvent(new sc::MouseEvent);
133 canvasEvent->time = event->timestamp() / 1000.0;
134 canvasEvent->screen_pos.set(event->screenPos().x(),
135 event->screenPos().y());
136 canvasEvent->client_pos.set(event->pos().x(),
137 event->pos().y());
138
139 // synthesise delta values
140 QPointF delta = event->screenPos() - _lastMouseEventScreenPosition;
141 canvasEvent->delta.set(delta.x(), delta.y());
142 _lastMouseEventScreenPosition = event->screenPos();
143
144 return canvasEvent;
145 }
146
mousePressEvent(QMouseEvent * event)147 void QtQuickFGCanvasItem::mousePressEvent(QMouseEvent *event)
148 {
149 sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
150 canvasEvent->button = convertQtButtonToCanvas(event->button());
151 canvasEvent->type = sc::Event::MOUSE_DOWN;
152 _canvas->handleMouseEvent(canvasEvent);
153 }
154
mouseReleaseEvent(QMouseEvent * event)155 void QtQuickFGCanvasItem::mouseReleaseEvent(QMouseEvent *event)
156 {
157 sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
158 canvasEvent->button = convertQtButtonToCanvas(event->button());
159 canvasEvent->type = sc::Event::MOUSE_UP;
160 _canvas->handleMouseEvent(canvasEvent);
161 }
162
mouseMoveEvent(QMouseEvent * event)163 void QtQuickFGCanvasItem::mouseMoveEvent(QMouseEvent *event)
164 {
165 sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
166 if (event->buttons() == Qt::NoButton) {
167 canvasEvent->type = sc::Event::MOUSE_MOVE;
168 } else {
169 canvasEvent->button = convertQtButtonToCanvas(event->button());
170 canvasEvent->type = sc::Event::DRAG;
171 }
172
173 _canvas->handleMouseEvent(canvasEvent);
174 }
175
mouseDoubleClickEvent(QMouseEvent * event)176 void QtQuickFGCanvasItem::mouseDoubleClickEvent(QMouseEvent *event)
177 {
178 sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
179 canvasEvent->button = convertQtButtonToCanvas(event->button());
180 canvasEvent->type = sc::Event::DBL_CLICK;
181 _canvas->handleMouseEvent(canvasEvent);
182 }
183
184
wheelEvent(QWheelEvent * event)185 void QtQuickFGCanvasItem::wheelEvent(QWheelEvent *event)
186 {
187 sc::MouseEventPtr canvasEvent(new sc::MouseEvent);
188 canvasEvent->time = event->timestamp() / 1000.0;
189 canvasEvent->type = sc::Event::WHEEL;
190
191 // TODO - check if using pixelDelta is beneficial at all.
192 canvasEvent->delta.set(event->angleDelta().x(),
193 event->angleDelta().y());
194 }
195
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)196 void QtQuickFGCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
197 {
198 QQuickItem::geometryChanged(newGeometry, oldGeometry);
199 updateCanvasGeometry();
200 }
201
setCanvas(QString canvas)202 void QtQuickFGCanvasItem::setCanvas(QString canvas)
203 {
204 if (_canvasName == canvas)
205 return;
206
207 if (_canvas) {
208 // release it
209 _canvas->destroy();
210 _canvas.clear();
211 }
212 _canvasName = canvas;
213
214 if (!_canvasName.isEmpty()) {
215 CanvasMgr* canvasManager = globals->get_subsystem<CanvasMgr>();
216 _canvas = canvasManager->createCanvas("");
217
218 SGPropertyNode* cprops = _canvas->getProps();
219 cprops->setBoolValue("render-always", true);
220
221 initCanvasNasalModules();
222 updateCanvasGeometry();
223 }
224
225 emit canvasChanged(_canvasName);
226 }
227
initCanvasNasalModules()228 void QtQuickFGCanvasItem::initCanvasNasalModules()
229 {
230 #if 0
231 SGPropertyNode *nasal = props->getNode("nasal");
232 if( !nasal )
233 return;
234
235 FGNasalSys *nas = globals->get_subsystem<FGNasalSys>();
236 if( !nas )
237 SG_LOG( SG_GENERAL,
238 SG_ALERT,
239 "CanvasWidget: Failed to get nasal subsystem!" );
240
241 const std::string file = std::string("__canvas:")
242 + cprops->getStringValue("name");
243
244 SGPropertyNode *load = nasal->getNode("load");
245 if( load )
246 {
247 const char *s = load->getStringValue();
248 nas->handleCommand(module.c_str(), file.c_str(), s, cprops);
249 }
250 #endif
251 }
252
updateCanvasGeometry()253 void QtQuickFGCanvasItem::updateCanvasGeometry()
254 {
255 SGPropertyNode* cprops = _canvas->getProps();
256 cprops->setIntValue("size[0]", width());
257 cprops->setIntValue("size[1]", height());
258 }
259