xref: /OK3568_Linux_fs/app/forlinx/forlinx_qt/qopenglwidget/glwidget.cpp (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /****************************************************************************
2  **
3  ** Copyright (C) 2016 The Qt Company Ltd.
4  ** Contact: https://www.qt.io/licensing/
5  **
6  ** This file is part of the examples of the Qt Toolkit.
7  **
8  ** $QT_BEGIN_LICENSE:BSD$
9  ** Commercial License Usage
10  ** Licensees holding valid commercial Qt licenses may use this file in
11  ** accordance with the commercial license agreement provided with the
12  ** Software or, alternatively, in accordance with the terms contained in
13  ** a written agreement between you and The Qt Company. For licensing terms
14  ** and conditions see https://www.qt.io/terms-conditions. For further
15  ** information use the contact form at https://www.qt.io/contact-us.
16  **
17  ** BSD License Usage
18  ** Alternatively, you may use this file under the terms of the BSD license
19  ** as follows:
20  **
21  ** "Redistribution and use in source and binary forms, with or without
22  ** modification, are permitted provided that the following conditions are
23  ** met:
24  **   * Redistributions of source code must retain the above copyright
25  **     notice, this list of conditions and the following disclaimer.
26  **   * Redistributions in binary form must reproduce the above copyright
27  **     notice, this list of conditions and the following disclaimer in
28  **     the documentation and/or other materials provided with the
29  **     distribution.
30  **   * Neither the name of The Qt Company Ltd nor the names of its
31  **     contributors may be used to endorse or promote products derived
32  **     from this software without specific prior written permission.
33  **
34  **
35  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46  **
47  ** $QT_END_LICENSE$
48  **
49  ****************************************************************************/
50 
51 #include "glwidget.h"
52 #include <QPainter>
53 #include <QPaintEngine>
54 #include <QOpenGLShaderProgram>
55 #include <QOpenGLTexture>
56 #include <QRandomGenerator>
57 #include <QCoreApplication>
58 #include <qmath.h>
59 
60 #include "mainwindow.h"
61 #include "bubble.h"
62 
63 const int bubbleNum = 8;
64 
65 #ifndef GL_SRGB8_ALPHA8
66 #define GL_SRGB8_ALPHA8 0x8C43
67 #endif
68 
GLWidget(MainWindow * mw,bool button,const QColor & background)69 GLWidget::GLWidget(MainWindow *mw, bool button, const QColor &background)
70     : m_mainWindow(mw),
71       m_showBubbles(true),
72       m_qtLogo(true),
73       m_frames(0),
74       m_program1(0),
75       m_program2(0),
76       m_texture(0),
77       m_transparent(false),
78       m_btn(0),
79       m_hasButton(button),
80       m_background(background)
81 {
82     setMinimumSize(300, 250);
83     if (QCoreApplication::arguments().contains(QStringLiteral("--srgb")))
84         setTextureFormat(GL_SRGB8_ALPHA8);
85 }
86 
~GLWidget()87 GLWidget::~GLWidget()
88 {
89     qDeleteAll(m_bubbles);
90 
91     // And now release all OpenGL resources.
92     makeCurrent();
93     delete m_texture;
94     delete m_program1;
95     delete m_program2;
96     delete m_vshader1;
97     delete m_fshader1;
98     delete m_vshader2;
99     delete m_fshader2;
100     m_vbo1.destroy();
101     m_vbo2.destroy();
102     doneCurrent();
103 }
104 
setScaling(int scale)105 void GLWidget::setScaling(int scale)
106 {
107     if (scale > 30)
108         m_fScale = 1 + qreal(scale - 30) / 30 * 0.25;
109     else if (scale < 30)
110         m_fScale =  1 - (qreal(30 - scale) / 30 * 0.25);
111     else
112         m_fScale = 1;
113 }
114 
setLogo()115 void GLWidget::setLogo()
116 {
117     m_qtLogo = true;
118 }
119 
setTexture()120 void GLWidget::setTexture()
121 {
122     m_qtLogo = false;
123 }
124 
setShowBubbles(bool bubbles)125 void GLWidget::setShowBubbles(bool bubbles)
126 {
127     m_showBubbles = bubbles;
128 }
129 
paintQtLogo()130 void GLWidget::paintQtLogo()
131 {
132     m_program1->enableAttributeArray(m_vertexAttr1);
133     m_program1->enableAttributeArray(m_normalAttr1);
134 
135     m_vbo1.bind();
136     // The data in the buffer is placed like this:
137     // vertex1.x, vertex1.y, vertex1.z, normal1.x, normal1.y, normal1.z, vertex2.x, ...
138     m_program1->setAttributeBuffer(m_vertexAttr1, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));
139     m_program1->setAttributeBuffer(m_normalAttr1, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
140     m_vbo1.release();
141 
142     glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
143 
144     m_program1->disableAttributeArray(m_normalAttr1);
145     m_program1->disableAttributeArray(m_vertexAttr1);
146 }
147 
paintTexturedCube()148 void GLWidget::paintTexturedCube()
149 {
150     m_texture->bind();
151 
152     if (!m_vbo2.isCreated()) {
153         static GLfloat afVertices[] = {
154             -0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5,
155             0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5,
156             -0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5,
157             0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5,
158 
159             0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5,
160             0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5,
161             -0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5,
162             -0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5,
163 
164             0.5, 0.5,  -0.5, -0.5, 0.5,  0.5,  -0.5,  0.5,  -0.5,
165             -0.5,  0.5,  0.5,  0.5,  0.5,  -0.5, 0.5, 0.5,  0.5,
166             -0.5,  -0.5, -0.5, -0.5, -0.5, 0.5,  0.5, -0.5, -0.5,
167             0.5, -0.5, 0.5,  0.5,  -0.5, -0.5, -0.5,  -0.5, 0.5
168         };
169 
170         static GLfloat afTexCoord[] = {
171             0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
172             1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
173             1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
174             0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
175 
176             1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
177             0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
178             0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
179             1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
180 
181             0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f,
182             1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f,
183             1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f,
184             0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f
185         };
186 
187         GLfloat afNormals[] = {
188 
189             0,0,-1, 0,0,-1, 0,0,-1,
190             0,0,-1, 0,0,-1, 0,0,-1,
191             0,0,1, 0,0,1, 0,0,1,
192             0,0,1, 0,0,1, 0,0,1,
193 
194             -1,0,0, -1,0,0, -1,0,0,
195             -1,0,0, -1,0,0, -1,0,0,
196             1,0,0, 1,0,0, 1,0,0,
197             1,0,0, 1,0,0, 1,0,0,
198 
199             0,-1,0, 0,-1,0, 0,-1,0,
200             0,-1,0, 0,-1,0, 0,-1,0,
201             0,1,0, 0,1,0, 0,1,0,
202             0,1,0, 0,1,0, 0,1,0
203         };
204 
205         m_vbo2.create();
206         m_vbo2.bind();
207         m_vbo2.allocate(36 * 8 * sizeof(GLfloat));
208         m_vbo2.write(0, afVertices, sizeof(afVertices));
209         m_vbo2.write(sizeof(afVertices), afTexCoord, sizeof(afTexCoord));
210         m_vbo2.write(sizeof(afVertices) + sizeof(afTexCoord), afNormals, sizeof(afNormals));
211         m_vbo2.release();
212     }
213 
214     m_program2->setUniformValue(m_textureUniform2, 0); // use texture unit 0
215 
216     m_program2->enableAttributeArray(m_vertexAttr2);
217     m_program2->enableAttributeArray(m_normalAttr2);
218     m_program2->enableAttributeArray(m_texCoordAttr2);
219 
220     m_vbo2.bind();
221     // In the buffer we first have 36 vertices (3 floats for each), then 36 texture
222     // coordinates (2 floats for each), then 36 normals (3 floats for each).
223     m_program2->setAttributeBuffer(m_vertexAttr2, GL_FLOAT, 0, 3);
224     m_program2->setAttributeBuffer(m_texCoordAttr2, GL_FLOAT, 36 * 3 * sizeof(GLfloat), 2);
225     m_program2->setAttributeBuffer(m_normalAttr2, GL_FLOAT, 36 * 5 * sizeof(GLfloat), 3);
226     m_vbo2.release();
227 
228     glDrawArrays(GL_TRIANGLES, 0, 36);
229 
230     m_program2->disableAttributeArray(m_vertexAttr2);
231     m_program2->disableAttributeArray(m_normalAttr2);
232     m_program2->disableAttributeArray(m_texCoordAttr2);
233 }
234 
initializeGL()235 void GLWidget::initializeGL()
236 {
237     initializeOpenGLFunctions();
238 
239     m_texture = new QOpenGLTexture(QImage(":/qt.png"));
240 
241     m_vshader1 = new QOpenGLShader(QOpenGLShader::Vertex);
242     const char *vsrc1 =
243         "attribute highp vec4 vertex;\n"
244         "attribute mediump vec3 normal;\n"
245         "uniform mediump mat4 matrix;\n"
246         "varying mediump vec4 color;\n"
247         "void main(void)\n"
248         "{\n"
249         "    vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
250         "    float angle = max(dot(normal, toLight), 0.0);\n"
251         "    vec3 col = vec3(0.40, 1.0, 0.0);\n"
252         "    color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
253         "    color = clamp(color, 0.0, 1.0);\n"
254         "    gl_Position = matrix * vertex;\n"
255         "}\n";
256     m_vshader1->compileSourceCode(vsrc1);
257 
258     m_fshader1 = new QOpenGLShader(QOpenGLShader::Fragment);
259     const char *fsrc1 =
260         "varying mediump vec4 color;\n"
261         "void main(void)\n"
262         "{\n"
263         "    gl_FragColor = color;\n"
264         "}\n";
265     m_fshader1->compileSourceCode(fsrc1);
266 
267     m_program1 = new QOpenGLShaderProgram;
268     m_program1->addShader(m_vshader1);
269     m_program1->addShader(m_fshader1);
270     m_program1->link();
271 
272     m_vertexAttr1 = m_program1->attributeLocation("vertex");
273     m_normalAttr1 = m_program1->attributeLocation("normal");
274     m_matrixUniform1 = m_program1->uniformLocation("matrix");
275 
276     m_vshader2 = new QOpenGLShader(QOpenGLShader::Vertex);
277     const char *vsrc2 =
278         "attribute highp vec4 vertex;\n"
279         "attribute highp vec4 texCoord;\n"
280         "attribute mediump vec3 normal;\n"
281         "uniform mediump mat4 matrix;\n"
282         "varying highp vec4 texc;\n"
283         "varying mediump float angle;\n"
284         "void main(void)\n"
285         "{\n"
286         "    vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
287         "    angle = max(dot(normal, toLight), 0.0);\n"
288         "    gl_Position = matrix * vertex;\n"
289         "    texc = texCoord;\n"
290         "}\n";
291     m_vshader2->compileSourceCode(vsrc2);
292 
293     m_fshader2 = new QOpenGLShader(QOpenGLShader::Fragment);
294     const char *fsrc2 =
295         "varying highp vec4 texc;\n"
296         "uniform sampler2D tex;\n"
297         "varying mediump float angle;\n"
298         "void main(void)\n"
299         "{\n"
300         "    highp vec3 color = texture2D(tex, texc.st).rgb;\n"
301         "    color = color * 0.2 + color * 0.8 * angle;\n"
302         "    gl_FragColor = vec4(clamp(color, 0.0, 1.0), 1.0);\n"
303         "}\n";
304     m_fshader2->compileSourceCode(fsrc2);
305 
306     m_program2 = new QOpenGLShaderProgram;
307     m_program2->addShader(m_vshader2);
308     m_program2->addShader(m_fshader2);
309     m_program2->link();
310 
311     m_vertexAttr2 = m_program2->attributeLocation("vertex");
312     m_normalAttr2 = m_program2->attributeLocation("normal");
313     m_texCoordAttr2 = m_program2->attributeLocation("texCoord");
314     m_matrixUniform2 = m_program2->uniformLocation("matrix");
315     m_textureUniform2 = m_program2->uniformLocation("tex");
316 
317     m_fAngle = 0;
318     m_fScale = 1;
319 
320     createGeometry();
321 
322     // Use a vertex buffer object. Client-side pointers are old-school and should be avoided.
323     m_vbo1.create();
324     m_vbo1.bind();
325     // For the cube all the data belonging to the texture coordinates and
326     // normals is placed separately, after the vertices. Here, for the Qt logo,
327     // let's do something different and potentially more efficient: create a
328     // properly interleaved data set.
329     const int vertexCount = m_vertices.count();
330     QVector<GLfloat> buf;
331     buf.resize(vertexCount * 3 * 2);
332     GLfloat *p = buf.data();
333     for (int i = 0; i < vertexCount; ++i) {
334         *p++ = m_vertices[i].x();
335         *p++ = m_vertices[i].y();
336         *p++ = m_vertices[i].z();
337         *p++ = m_normals[i].x();
338         *p++ = m_normals[i].y();
339         *p++ = m_normals[i].z();
340     }
341     m_vbo1.allocate(buf.constData(), buf.count() * sizeof(GLfloat));
342     m_vbo1.release();
343 
344     createBubbles(bubbleNum - m_bubbles.count());
345 }
346 
paintGL()347 void GLWidget::paintGL()
348 {
349     createBubbles(bubbleNum - m_bubbles.count());
350 
351     QPainter painter;
352     painter.begin(this);
353 
354     painter.beginNativePainting();
355 
356     glClearColor(m_background.redF(), m_background.greenF(), m_background.blueF(), m_transparent ? 0.0f : 1.0f);
357     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
358 
359     glFrontFace(GL_CW);
360     glCullFace(GL_FRONT);
361     glEnable(GL_CULL_FACE);
362     glEnable(GL_DEPTH_TEST);
363 
364     QMatrix4x4 modelview;
365     modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f);
366     modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f);
367     modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f);
368     modelview.scale(m_fScale);
369     modelview.translate(0.0f, -0.2f, 0.0f);
370 
371     if (m_qtLogo) {
372         m_program1->bind();
373         m_program1->setUniformValue(m_matrixUniform1, modelview);
374         paintQtLogo();
375         m_program1->release();
376     } else {
377         m_program2->bind();
378         m_program2->setUniformValue(m_matrixUniform2, modelview);
379         paintTexturedCube();
380         m_program2->release();
381     }
382 
383     glDisable(GL_DEPTH_TEST);
384     glDisable(GL_CULL_FACE);
385 
386     painter.endNativePainting();
387 
388     if (m_showBubbles)
389         foreach (Bubble *bubble, m_bubbles) {
390             bubble->drawBubble(&painter);
391         }
392 
393     if (const int elapsed = m_time.elapsed()) {
394         QString framesPerSecond;
395         framesPerSecond.setNum(m_frames /(elapsed / 1000.0), 'f', 2);
396         painter.setPen(m_transparent ? Qt::black : Qt::white);
397         painter.drawText(20, 40, framesPerSecond + " paintGL calls / s");
398     }
399 
400     painter.end();
401 
402     QMutableListIterator<Bubble*> iter(m_bubbles);
403 
404     while (iter.hasNext()) {
405         Bubble *bubble = iter.next();
406         bubble->move(rect());
407     }
408     if (!(m_frames % 100)) {
409         m_time.start();
410         m_frames = 0;
411     }
412     m_fAngle += 1.0f;
413     ++m_frames;
414 
415     // When requested, follow the ideal way to animate: Rely on
416     // blocking swap and just schedule updates continuously.
417     if (!m_mainWindow->timerEnabled())
418         update();
419 }
420 
createBubbles(int number)421 void GLWidget::createBubbles(int number)
422 {
423     for (int i = 0; i < number; ++i) {
424         QPointF position(width()*(0.1 + QRandomGenerator::global()->bounded(0.8)),
425                          height()*(0.1 + QRandomGenerator::global()->bounded(0.8)));
426         qreal radius = qMin(width(), height())*(0.0175 + QRandomGenerator::global()->bounded(0.0875));
427         QPointF velocity(width()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)),
428                          height()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)));
429 
430         m_bubbles.append(new Bubble(position, radius, velocity));
431     }
432 }
433 
createGeometry()434 void GLWidget::createGeometry()
435 {
436     m_vertices.clear();
437     m_normals.clear();
438 
439     qreal x1 = +0.06f;
440     qreal y1 = -0.14f;
441     qreal x2 = +0.14f;
442     qreal y2 = -0.06f;
443     qreal x3 = +0.08f;
444     qreal y3 = +0.00f;
445     qreal x4 = +0.30f;
446     qreal y4 = +0.22f;
447 
448     quad(x1, y1, x2, y2, y2, x2, y1, x1);
449     quad(x3, y3, x4, y4, y4, x4, y3, x3);
450 
451     extrude(x1, y1, x2, y2);
452     extrude(x2, y2, y2, x2);
453     extrude(y2, x2, y1, x1);
454     extrude(y1, x1, x1, y1);
455     extrude(x3, y3, x4, y4);
456     extrude(x4, y4, y4, x4);
457     extrude(y4, x4, y3, x3);
458 
459     const int NumSectors = 100;
460     const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors;
461 
462     for (int i = 0; i < NumSectors; ++i) {
463         qreal angle = i * sectorAngle;
464         qreal x5 = 0.30 * sin(angle);
465         qreal y5 = 0.30 * cos(angle);
466         qreal x6 = 0.20 * sin(angle);
467         qreal y6 = 0.20 * cos(angle);
468 
469         angle += sectorAngle;
470         qreal x7 = 0.20 * sin(angle);
471         qreal y7 = 0.20 * cos(angle);
472         qreal x8 = 0.30 * sin(angle);
473         qreal y8 = 0.30 * cos(angle);
474 
475         quad(x5, y5, x6, y6, x7, y7, x8, y8);
476 
477         extrude(x6, y6, x7, y7);
478         extrude(x8, y8, x5, y5);
479     }
480 
481     for (int i = 0;i < m_vertices.size();i++)
482         m_vertices[i] *= 2.0f;
483 }
484 
quad(qreal x1,qreal y1,qreal x2,qreal y2,qreal x3,qreal y3,qreal x4,qreal y4)485 void GLWidget::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
486 {
487     m_vertices << QVector3D(x1, y1, -0.05f);
488     m_vertices << QVector3D(x2, y2, -0.05f);
489     m_vertices << QVector3D(x4, y4, -0.05f);
490 
491     m_vertices << QVector3D(x3, y3, -0.05f);
492     m_vertices << QVector3D(x4, y4, -0.05f);
493     m_vertices << QVector3D(x2, y2, -0.05f);
494 
495     QVector3D n = QVector3D::normal
496         (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
497 
498     m_normals << n;
499     m_normals << n;
500     m_normals << n;
501 
502     m_normals << n;
503     m_normals << n;
504     m_normals << n;
505 
506     m_vertices << QVector3D(x4, y4, 0.05f);
507     m_vertices << QVector3D(x2, y2, 0.05f);
508     m_vertices << QVector3D(x1, y1, 0.05f);
509 
510     m_vertices << QVector3D(x2, y2, 0.05f);
511     m_vertices << QVector3D(x4, y4, 0.05f);
512     m_vertices << QVector3D(x3, y3, 0.05f);
513 
514     n = QVector3D::normal
515         (QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
516 
517     m_normals << n;
518     m_normals << n;
519     m_normals << n;
520 
521     m_normals << n;
522     m_normals << n;
523     m_normals << n;
524 }
525 
extrude(qreal x1,qreal y1,qreal x2,qreal y2)526 void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
527 {
528     m_vertices << QVector3D(x1, y1, +0.05f);
529     m_vertices << QVector3D(x2, y2, +0.05f);
530     m_vertices << QVector3D(x1, y1, -0.05f);
531 
532     m_vertices << QVector3D(x2, y2, -0.05f);
533     m_vertices << QVector3D(x1, y1, -0.05f);
534     m_vertices << QVector3D(x2, y2, +0.05f);
535 
536     QVector3D n = QVector3D::normal
537         (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
538 
539     m_normals << n;
540     m_normals << n;
541     m_normals << n;
542 
543     m_normals << n;
544     m_normals << n;
545     m_normals << n;
546 }
547 
setTransparent(bool transparent)548 void GLWidget::setTransparent(bool transparent)
549 {
550     setAttribute(Qt::WA_AlwaysStackOnTop, transparent);
551     m_transparent = transparent;
552     // Call update() on the top-level window after toggling AlwayStackOnTop to make sure
553     // the entire backingstore is updated accordingly.
554     window()->update();
555 }
556 
resizeGL(int,int)557 void GLWidget::resizeGL(int, int)
558 {
559     if (m_hasButton) {
560         if (!m_btn) {
561             m_btn = new QPushButton("A widget on top.\nPress for more widgets.", this);
562             connect(m_btn, &QPushButton::clicked, this, &GLWidget::handleButtonPress);
563         }
564         m_btn->move(20, 80);
565     }
566 }
567 
handleButtonPress()568 void GLWidget::handleButtonPress()
569 {
570     m_mainWindow->addNew();
571 }
572