Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
deviceskin.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "deviceskin_p.h"
6
7#include <QtWidgets/qapplication.h>
8
9#include <QtGui/qbitmap.h>
10#include <QtGui/qevent.h>
11#include <QtGui/qimage.h>
12#include <QtGui/qpainter.h>
13#include <QtGui/qpixmap.h>
14
15#include <QtCore/qdebug.h>
16#include <QtCore/qdir.h>
17#include <QtCore/qfile.h>
18#include <QtCore/qfileinfo.h>
19#include <QtCore/qnamespace.h>
20#include <QtCore/qregularexpression.h>
21#include <QtCore/qtextstream.h>
22#include <QtCore/qtimer.h>
23
24#ifdef TEST_SKIN
25# include <QtWidgets/qmainwindow.h>
26# include <QtWidgets/qboxlayout.h>
27# include <QtWidgets/qdialog.h>
28# include <QtWidgets/qdialogbuttonbox.h>
29#endif
30
32
33using namespace Qt::StringLiterals;
34
35namespace {
36 enum { joydistance = 10, key_repeat_period = 50, key_repeat_delay = 500 };
37 enum { debugDeviceSkin = 0 };
38}
39
40static void parseRect(const QString &value, QRect *rect) {
41 const auto l = QStringView{value}.split(QLatin1Char(' '));
42 rect->setRect(l[0].toInt(), l[1].toInt(), l[2].toInt(), l[3].toInt());
43}
44
45static QString msgImageNotLoaded(const QString &f) {
46 return DeviceSkin::tr("The image file '%1' could not be loaded.").arg(f);
47}
48
49// ------------ DeviceSkinButtonArea
50QDebug &operator<<(QDebug &str, const DeviceSkinButtonArea &a)
51{
52
53 str << "Area: " << a.name << " keyCode=" << a.keyCode << " area=" << a.area
54 << " text=" << a.text << " activeWhenClosed=" << a.activeWhenClosed;
55 return str;
56}
57
58// ------------ DeviceSkinParameters
59
60QDebug operator<<(QDebug str, const DeviceSkinParameters &p)
61{
62 str << "Images " << p.skinImageUpFileName << ','
63 << p.skinImageDownFileName<< ',' << p.skinImageClosedFileName
64 << ',' << p.skinCursorFileName <<"\nScreen: " << p.screenRect
65 << " back: " << p.backScreenRect << " closed: " << p.closedScreenRect
66 << " cursor: " << p.cursorHot << " Prefix: " << p.prefix
67 << " Joystick: " << p.joystick << " MouseHover" << p.hasMouseHover;
68 const int numAreas = p.buttonAreas.size();
69 for (int i = 0; i < numAreas; i++)
70 str << p.buttonAreas[i];
71 return str;
72}
73
75{
76 return backScreenRect.isNull() ? closedScreenRect .size(): backScreenRect.size();
77}
78
80{
81 return secondaryScreenSize() != QSize(0, 0);
82}
83
84bool DeviceSkinParameters::read(const QString &skinDirectory, ReadMode rm, QString *errorMessage)
85{
86 // Figure out the name. remove ending '/' if present
87 QString skinFile = skinDirectory;
88 if (skinFile.endsWith(QLatin1Char('/')))
89 skinFile.truncate(skinFile.size() - 1);
90
91 QFileInfo fi(skinFile);
92 QString fn;
93 if ( fi.isDir() ) {
94 prefix = skinFile;
95 prefix += QLatin1Char('/');
96 fn = prefix;
97 fn += fi.baseName();
98 fn += ".skin"_L1;
99 } else if (fi.isFile()){
100 fn = skinFile;
101 prefix = fi.path();
102 prefix += QLatin1Char('/');
103 } else {
104 *errorMessage = DeviceSkin::tr("The skin directory '%1' does not contain a configuration file.").arg(skinDirectory);
105 return false;
106 }
107 QFile f(fn);
108 if (!f.open(QIODevice::ReadOnly )) {
109 *errorMessage = DeviceSkin::tr("The skin configuration file '%1' could not be opened.").arg(fn);
110 return false;
111 }
112 QTextStream ts(&f);
113 const bool rc = read(ts, rm, errorMessage);
114 if (!rc)
115 *errorMessage = DeviceSkin::tr("The skin configuration file '%1' could not be read: %2")
116 .arg(fn, *errorMessage);
117 return rc;
118}
119bool DeviceSkinParameters::read(QTextStream &ts, ReadMode rm, QString *errorMessage)
120{
121 QStringList closedAreas;
122 QStringList toggleAreas;
123 QStringList toggleActiveAreas;
124 int nareas = 0;
125 screenDepth = 0;
126 QString mark;
127 ts >> mark;
128 hasMouseHover = true; // historical default
129 if (mark == "[SkinFile]"_L1) {
130 const QString UpKey = "Up"_L1;
131 const QString DownKey = "Down"_L1;
132 const QString ClosedKey = "Closed"_L1;
133 const QString ClosedAreasKey = "ClosedAreas"_L1;
134 const QString ScreenKey = "Screen"_L1;
135 const QString ScreenDepthKey = "ScreenDepth"_L1;
136 const QString BackScreenKey = "BackScreen"_L1;
137 const QString ClosedScreenKey = "ClosedScreen"_L1;
138 const QString CursorKey = "Cursor"_L1;
139 const QString AreasKey = "Areas"_L1;
140 const QString ToggleAreasKey = "ToggleAreas"_L1;
141 const QString ToggleActiveAreasKey = "ToggleActiveAreas"_L1;
142 const QString HasMouseHoverKey = "HasMouseHover"_L1;
143 // New
144 while (!nareas) {
145 QString line = ts.readLine();
146 if ( line.isNull() )
147 break;
148 if (!line.isEmpty() && line.at(0) != u'#') {
149 int eq = line.indexOf(QLatin1Char('='));
150 if ( eq >= 0 ) {
151 const QString key = line.left(eq);
152 eq++;
153 while (eq<line.size()-1 && line[eq].isSpace())
154 eq++;
155 const QString value = line.mid(eq);
156 if ( key == UpKey ) {
157 skinImageUpFileName = value;
158 } else if ( key == DownKey ) {
159 skinImageDownFileName = value;
160 } else if ( key == ClosedKey ) {
161 skinImageClosedFileName = value;
162 } else if ( key == ClosedAreasKey ) {
163 closedAreas = value.split(QLatin1Char(' '));
164 } else if ( key == ScreenKey ) {
165 parseRect( value, &screenRect);
166 } else if ( key == ScreenDepthKey ) {
167 screenDepth = value.toInt();
168 } else if ( key == BackScreenKey ) {
169 parseRect(value, &backScreenRect);
170 } else if ( key == ClosedScreenKey ) {
171 parseRect( value, &closedScreenRect );
172 } else if ( key == CursorKey ) {
173 QStringList l = value.split(QLatin1Char(' '));
174 skinCursorFileName = l[0];
175 cursorHot = QPoint(l[1].toInt(),l[2].toInt());
176 } else if ( key == AreasKey ) {
177 nareas = value.toInt();
178 } else if ( key == ToggleAreasKey ) {
179 toggleAreas = value.split(QLatin1Char(' '));
180 } else if ( key == ToggleActiveAreasKey ) {
181 toggleActiveAreas = value.split(QLatin1Char(' '));
182 } else if ( key == HasMouseHoverKey ) {
183 hasMouseHover = value == "true"_L1 || value == "1"_L1;
184 }
185 } else {
186 *errorMessage = DeviceSkin::tr("Syntax error: %1").arg(line);
187 return false;
188 }
189 }
190 }
191 } else {
192 // Old
193 skinImageUpFileName = mark;
194 QString s;
195 int x,y,w,h,na;
196 ts >> s >> x >> y >> w >> h >> na;
197 skinImageDownFileName = s;
198 screenRect.setRect(x, y, w, h);
199 nareas = na;
200 }
201 // Done for short mode
202 if (rm == ReadSizeOnly)
203 return true;
204 // verify skin files exist
205 skinImageUpFileName.insert(0, prefix);
206 if (!QFile(skinImageUpFileName).exists()) {
207 *errorMessage = DeviceSkin::tr("The skin \"up\" image file '%1' does not exist.").arg(skinImageUpFileName);
208 return false;
209 }
210 if (!skinImageUp.load(skinImageUpFileName)) {
211 *errorMessage = msgImageNotLoaded(skinImageUpFileName);
212 return false;
213 }
214
215 skinImageDownFileName.insert(0, prefix);
216 if (!QFile(skinImageDownFileName).exists()) {
217 *errorMessage = DeviceSkin::tr("The skin \"down\" image file '%1' does not exist.").arg(skinImageDownFileName);
218 return false;
219 }
220 if (!skinImageDown.load(skinImageDownFileName)) {
221 *errorMessage = msgImageNotLoaded(skinImageDownFileName);
222 return false;
223 }
224
225 if (!skinImageClosedFileName.isEmpty()) {
226 skinImageClosedFileName.insert(0, prefix);
227 if (!QFile(skinImageClosedFileName).exists()) {
228 *errorMessage = DeviceSkin::tr("The skin \"closed\" image file '%1' does not exist.").arg(skinImageClosedFileName);
229 return false;
230 }
231 if (!skinImageClosed.load(skinImageClosedFileName)) {
232 *errorMessage = msgImageNotLoaded(skinImageClosedFileName);
233 return false;
234 }
235 }
236
237 if (!skinCursorFileName.isEmpty()) {
238 skinCursorFileName.insert(0, prefix);
239 if (!QFile(skinCursorFileName).exists()) {
240 *errorMessage = DeviceSkin::tr("The skin cursor image file '%1' does not exist.").arg(skinCursorFileName);
241 return false;
242 }
243 if (!skinCursor.load(skinCursorFileName)) {
244 *errorMessage = msgImageNotLoaded(skinCursorFileName);
245 return false;
246 }
247 }
248
249 // read areas
250 if (!nareas)
251 return true;
252 buttonAreas.reserve(nareas);
253
254 int i = 0;
255 ts.readLine(); // eol
256 joystick = -1;
257 const QString Joystick = "Joystick"_L1;
258 const QRegularExpression splitRe("[ \t][ \t]*"_L1);
259 Q_ASSERT(splitRe.isValid());
260 while (i < nareas && !ts.atEnd() ) {
261 buttonAreas.push_back(DeviceSkinButtonArea());
262 DeviceSkinButtonArea &area = buttonAreas.back();
263 const QString line = ts.readLine();
264 if ( !line.isEmpty() && line[0] != QLatin1Char('#') ) {
265 const QStringList tok = line.split(splitRe);
266 if ( tok.size()<6 ) {
267 *errorMessage = DeviceSkin::tr("Syntax error in area definition: %1").arg(line);
268 return false;
269 } else {
270 area.name = tok[0];
271 QString k = tok[1];
272 if ( k.left(2).toLower() == "0x"_L1) {
273 area.keyCode = k.mid(2).toInt(0,16);
274 } else {
275 area.keyCode = k.toInt();
276 }
277
278 int p=0;
279 for (int j=2; j < tok.size() - 1; ) {
280 const int x = tok[j++].toInt();
281 const int y = tok[j++].toInt();
282 area.area.putPoints(p++,1,x,y);
283 }
284
285 const QChar doubleQuote = QLatin1Char('"');
286 if ( area.name[0] == doubleQuote && area.name.endsWith(doubleQuote)) {
287 area.name.truncate(area.name.size() - 1);
288 area.name.remove(0, 1);
289 }
290 if ( area.name.size() == 1 )
291 area.text = area.name;
292 if ( area.name == Joystick)
293 joystick = i;
294 area.activeWhenClosed = closedAreas.contains(area.name)
295 || area.keyCode == Qt::Key_Flip; // must be to work
296 area.toggleArea = toggleAreas.contains(area.name);
297 area.toggleActiveArea = toggleActiveAreas.contains(area.name);
298 if (area.toggleArea)
299 toggleAreaList += i;
300 i++;
301 }
302 }
303 }
304 if (i != nareas) {
305 qWarning() << DeviceSkin::tr("Mismatch in number of areas, expected %1, got %2.")
306 .arg(nareas).arg(i);
307 }
308 if (debugDeviceSkin)
309 qDebug() << *this;
310 return true;
311}
312
313// --------- CursorWindow declaration
314
315namespace qvfb_internal {
316
317class CursorWindow : public QWidget
318{
319public:
320 explicit CursorWindow(const QImage &cursor, QPoint hot, QWidget *sk);
321
322 void setView(QWidget*);
324 bool handleMouseEvent(QEvent *ev);
325
326protected:
327 bool event(QEvent *) override;
328 bool eventFilter(QObject*, QEvent *) override;
329
330private:
331 QWidget *mouseRecipient;
332 QWidget *m_view;
333 QWidget *skin;
334 QPoint hotspot;
335};
336}
337
338// --------- Skin
339
340DeviceSkin::DeviceSkin(const DeviceSkinParameters &parameters, QWidget *p ) :
341 QWidget(p),
342 m_parameters(parameters),
343 buttonRegions(parameters.buttonAreas.size(), QRegion()),
344 parent(p),
345 t_skinkey(new QTimer(this)),
346 t_parentmove(new QTimer(this))
347{
348 Q_ASSERT(p);
349 setMouseTracking(true);
350 setAttribute(Qt::WA_NoSystemBackground);
351
352 setZoom(1.0);
353 connect(t_skinkey, &QTimer::timeout, this, &DeviceSkin::skinKeyRepeat );
354 t_parentmove->setSingleShot( true );
355 connect(t_parentmove, &QTimer::timeout, this, &DeviceSkin::moveParent );
356}
357
358void DeviceSkin::skinKeyRepeat()
359{
360 if ( m_view ) {
361 const DeviceSkinButtonArea &area = m_parameters.buttonAreas[buttonIndex];
362 emit skinKeyReleaseEvent(area.keyCode,area.text, true);
363 emit skinKeyPressEvent(area.keyCode, area.text, true);
364 t_skinkey->start(key_repeat_period);
365 }
366}
367
368void DeviceSkin::calcRegions()
369{
370 const int numAreas = m_parameters.buttonAreas.size();
371 for (int i=0; i<numAreas; i++) {
372 QPolygon xa(m_parameters.buttonAreas[i].area.size());
373 int n = m_parameters.buttonAreas[i].area.size();
374 for (int p = 0; p < n; p++) {
375 xa.setPoint(p,transform.map(m_parameters.buttonAreas[i].area[p]));
376 }
377 if (n == 2) {
378 buttonRegions[i] = QRegion(xa.boundingRect());
379 } else {
380 buttonRegions[i] = QRegion(xa);
381 }
382 }
383}
384
385void DeviceSkin::loadImages()
386{
387 QImage iup = m_parameters.skinImageUp;
388 QImage idown = m_parameters.skinImageDown;
389
390 QImage iclosed;
391 const bool hasClosedImage = !m_parameters.skinImageClosed.isNull();
392
393 if (hasClosedImage)
394 iclosed = m_parameters.skinImageClosed;
395 QImage icurs;
396 const bool hasCursorImage = !m_parameters.skinCursor.isNull();
397 if (hasCursorImage)
398 icurs = m_parameters.skinCursor;
399
400 if (!transform.isIdentity()) {
401 iup = iup.transformed(transform, Qt::SmoothTransformation);
402 idown = idown.transformed(transform, Qt::SmoothTransformation);
403 if (hasClosedImage)
404 iclosed = iclosed.transformed(transform, Qt::SmoothTransformation);
405 if (hasCursorImage)
406 icurs = icurs.transformed(transform, Qt::SmoothTransformation);
407 }
408 const Qt::ImageConversionFlags conv = Qt::ThresholdAlphaDither|Qt::AvoidDither;
409 skinImageUp = QPixmap::fromImage(iup);
410 skinImageDown = QPixmap::fromImage(idown, conv);
411 if (hasClosedImage)
412 skinImageClosed = QPixmap::fromImage(iclosed, conv);
413 if (hasCursorImage)
414 skinCursor = QPixmap::fromImage(icurs, conv);
415
416 setFixedSize( skinImageUp.size() );
417 if (!skinImageUp.mask())
418 skinImageUp.setMask(skinImageUp.createHeuristicMask());
419 if (!skinImageClosed.mask())
420 skinImageClosed.setMask(skinImageClosed.createHeuristicMask());
421
422 QWidget* parent = parentWidget();
423 parent->setMask( skinImageUp.mask() );
424 parent->setFixedSize( skinImageUp.size() );
425
426 delete cursorw;
427 cursorw = 0;
428 if (hasCursorImage) {
429 cursorw = new qvfb_internal::CursorWindow(m_parameters.skinCursor, m_parameters.cursorHot, this);
430 if (m_view)
431 cursorw->setView(m_view);
432 }
433}
434
436{
437 delete cursorw;
438}
439
440void DeviceSkin::setTransform(const QTransform &wm)
441{
442 transform = QImage::trueMatrix(wm,m_parameters.skinImageUp.width(),m_parameters.skinImageUp.height());
443 calcRegions();
444 loadImages();
445 if ( m_view ) {
446 QPoint p = transform.map(QPolygon(m_parameters.screenRect)).boundingRect().topLeft();
447 m_view->move(p);
448 }
449 updateSecondaryScreen();
450}
451
452void DeviceSkin::setZoom( double z )
453{
454 setTransform(QTransform().scale(z,z));
455}
456
457void DeviceSkin::updateSecondaryScreen()
458{
459 if (!m_secondaryView)
460 return;
461 if (flipped_open) {
462 if (m_parameters.backScreenRect.isNull()) {
463 m_secondaryView->hide();
464 } else {
465 m_secondaryView->move(transform.map(QPolygon(m_parameters.backScreenRect)).boundingRect().topLeft());
466 m_secondaryView->show();
467 }
468 } else {
469 if (m_parameters.closedScreenRect.isNull()) {
470 m_secondaryView->hide();
471 } else {
472 m_secondaryView->move(transform.map(QPolygon(m_parameters.closedScreenRect)).boundingRect().topLeft());
473 m_secondaryView->show();
474 }
475 }
476}
477
478void DeviceSkin::setView( QWidget *v )
479{
480 m_view = v;
481 m_view->setFocus();
482 m_view->move(transform.map(QPolygon(m_parameters.screenRect)).boundingRect().topLeft());
483 if ( cursorw )
484 cursorw->setView(v);
485}
486
487void DeviceSkin::setSecondaryView( QWidget *v )
488{
489 m_secondaryView = v;
490 updateSecondaryScreen();
491}
492
493void DeviceSkin::paintEvent( QPaintEvent *)
494{
495 QPainter p( this );
496 if ( flipped_open ) {
497 p.drawPixmap(0, 0, skinImageUp);
498 } else {
499 p.drawPixmap(0, 0, skinImageClosed);
500 }
501 QList<int> toDraw;
502 if ( buttonPressed == true ) {
503 toDraw += buttonIndex;
504 }
505 for (int toggle : std::as_const(m_parameters.toggleAreaList)) {
506 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[toggle];
507 if (flipped_open || ba.activeWhenClosed) {
508 if (ba.toggleArea && ba.toggleActiveArea)
509 toDraw += toggle;
510 }
511 }
512 for (int button : std::as_const(toDraw)) {
513 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[button];
514 const QRect r = buttonRegions[button].boundingRect();
515 if ( ba.area.size() > 2 )
516 p.setClipRegion(buttonRegions[button]);
517 p.drawPixmap( r.topLeft(), skinImageDown, r);
518 }
519}
520
521void DeviceSkin::mousePressEvent( QMouseEvent *e )
522{
523 if (e->button() == Qt::RightButton) {
524 emit popupMenu();
525 } else {
526 buttonPressed = false;
527
528 onjoyrelease = -1;
529 const int numAreas = m_parameters.buttonAreas.size();
530 for (int i = 0; i < numAreas ; i++) {
531 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[i];
532 if ( buttonRegions[i].contains( e->position().toPoint() ) ) {
533 if ( flipped_open || ba.activeWhenClosed ) {
534 if ( m_parameters.joystick == i ) {
535 joydown = true;
536 } else {
537 if ( joydown )
538 onjoyrelease = i;
539 else
540 startPress(i);
541 break;
542 if (debugDeviceSkin)// Debug message to be sure we are clicking the right areas
543 qDebug()<< m_parameters.buttonAreas[i].name << " clicked";
544 }
545 }
546 }
547 }
548 clickPos = e->position().toPoint();
549// This is handy for finding the areas to define rectangles for new skins
550 if (debugDeviceSkin)
551 qDebug()<< "Clicked in " << e->position().toPoint().x() << ',' << e->position().toPoint().y();
552 clickPos = e->position().toPoint();
553 }
554}
555
556void DeviceSkin::flip(bool open)
557{
558 if ( flipped_open == open )
559 return;
560 if ( open ) {
561 parent->setMask(skinImageUp.mask());
562 emit skinKeyReleaseEvent(Qt::Key(Qt::Key_Flip), QString(), false);
563 } else {
564 parent->setMask(skinImageClosed.mask());
565 emit skinKeyPressEvent(Qt::Key(Qt::Key_Flip), QString(), false);
566 }
567 flipped_open = open;
568 updateSecondaryScreen();
569 repaint();
570}
571
572void DeviceSkin::startPress(int i)
573{
574 buttonPressed = true;
575 buttonIndex = i;
576 if (m_view) {
577 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[buttonIndex];
578 if (ba.keyCode == Qt::Key_Flip) {
579 flip(!flipped_open);
580 } else if (ba.toggleArea) {
581 bool active = !ba.toggleActiveArea;
582 const_cast<DeviceSkinButtonArea &>(ba).toggleActiveArea = active;
583 if (active)
584 emit skinKeyPressEvent(ba.keyCode, ba.text, false);
585 else
586 emit skinKeyReleaseEvent(ba.keyCode, ba.text, false);
587 } else {
588 emit skinKeyPressEvent(ba.keyCode, ba.text, false);
589 t_skinkey->start(key_repeat_delay);
590 }
591 repaint(buttonRegions[buttonIndex].boundingRect());
592 }
593}
594
595void DeviceSkin::endPress()
596{
597 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[buttonIndex];
598 if (m_view && ba.keyCode != Qt::Key_Flip && !ba.toggleArea )
599 emit skinKeyReleaseEvent(ba.keyCode, ba.text, false);
600 t_skinkey->stop();
601 buttonPressed = false;
602 repaint( buttonRegions[buttonIndex].boundingRect() );
603}
604
605void DeviceSkin::mouseMoveEvent( QMouseEvent *e )
606{
607 if ( e->buttons() & Qt::LeftButton ) {
608 const int joystick = m_parameters.joystick;
609 QPoint newpos = e->globalPosition().toPoint() - clickPos;
610 if (joydown) {
611 int k1=0, k2=0;
612 if (newpos.x() < -joydistance) {
613 k1 = joystick+1;
614 } else if (newpos.x() > +joydistance) {
615 k1 = joystick+3;
616 }
617 if (newpos.y() < -joydistance) {
618 k2 = joystick+2;
619 } else if (newpos.y() > +joydistance) {
620 k2 = joystick+4;
621 }
622 if (k1 || k2) {
623 if (!buttonPressed) {
624 onjoyrelease = -1;
625 if (k1 && k2) {
626 startPress(k2);
627 endPress();
628 }
629 startPress(k1 ? k1 : k2);
630 }
631 } else if (buttonPressed) {
632 endPress();
633 }
634 } else if (buttonPressed == false) {
635 parentpos = newpos;
636 if (!t_parentmove->isActive())
637 t_parentmove->start(50);
638 }
639 }
640 if ( cursorw )
641 cursorw->setPos(e->globalPosition().toPoint());
642}
643
645{
646 parent->move( parentpos );
647}
648
649void DeviceSkin::mouseReleaseEvent( QMouseEvent * )
650{
651 if ( buttonPressed )
652 endPress();
653 if ( joydown ) {
654 joydown = false;
655 if (onjoyrelease >= 0) {
656 startPress(onjoyrelease);
657 endPress();
658 }
659 }
660}
661
663{
664 return !skinCursor.isNull();
665}
666
667// ------------------ CursorWindow implementation
668
669namespace qvfb_internal {
670
671bool CursorWindow::eventFilter( QObject *, QEvent *ev)
672{
673 handleMouseEvent(ev);
674 return false;
675}
676
677bool CursorWindow::event( QEvent *ev )
678{
679 if (handleMouseEvent(ev))
680 return true;
681 return QWidget::event(ev);
682}
683
685{
686 bool handledEvent = false;
687 static int inhere=0;
688 if ( !inhere ) {
689 inhere++;
690 if (m_view) {
691 if (ev->type() >= QEvent::MouseButtonPress && ev->type() <= QEvent::MouseMove) {
692 QMouseEvent *e = (QMouseEvent*)ev;
693 QPoint gp = e->globalPosition().toPoint();
694 QPoint vp = m_view->mapFromGlobal(gp);
695 QPoint sp = skin->mapFromGlobal(gp);
696 if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) {
697 if (m_view->rect().contains(vp))
698 mouseRecipient = m_view;
699 else if (skin->parentWidget()->geometry().contains(gp))
700 mouseRecipient = skin;
701 else
702 mouseRecipient = 0;
703 }
704 if (mouseRecipient) {
705 setPos(gp);
706 QMouseEvent me(e->type(),mouseRecipient==skin ? sp : vp,gp,e->button(),e->buttons(),e->modifiers());
707 QApplication::sendEvent(mouseRecipient, &me);
708 } else if (!skin->parentWidget()->geometry().contains(gp)) {
709 hide();
710 } else {
711 setPos(gp);
712 }
713 if (e->type() == QEvent::MouseButtonRelease)
714 mouseRecipient = 0;
715 handledEvent = true;
716 }
717 }
718 inhere--;
719 }
720 return handledEvent;
721}
722
723void CursorWindow::setView(QWidget* v)
724{
725 if ( m_view ) {
726 m_view->removeEventFilter(this);
727 m_view->removeEventFilter(this);
728 }
729 m_view = v;
730 m_view->installEventFilter(this);
731 m_view->installEventFilter(this);
732 mouseRecipient = 0;
733}
734
735CursorWindow::CursorWindow(const QImage &img, QPoint hot, QWidget* sk)
736 : QWidget(0),
737 m_view(0),
738 skin(sk),
739 hotspot(hot)
740{
741 setWindowFlags( Qt::FramelessWindowHint );
742 mouseRecipient = 0;
743 setMouseTracking(true);
744#ifndef QT_NO_CURSOR
745 setCursor(Qt::BlankCursor);
746#endif
747 QPixmap p;
748 p = QPixmap::fromImage(img);
749 if (!p.mask()) {
750 QBitmap bm = img.hasAlphaChannel() ? QBitmap::fromImage(img.createAlphaMask())
751 : QBitmap::fromImage(img.createHeuristicMask());
752 p.setMask(bm);
753 }
754 QPalette palette;
755 palette.setBrush(backgroundRole(), QBrush(p));
756 setPalette(palette);
757 setFixedSize( p.size() );
758 if ( !p.mask().isNull() )
759 setMask(p.mask());
760}
761
762void CursorWindow::setPos(QPoint p)
763{
764 move(p-hotspot);
765 show();
766 raise();
767}
768}
769
770#ifdef TEST_SKIN
771
772int main(int argc,char *argv[])
773{
774 if (argc < 1)
775 return 1;
776 const QString skinFile = QString::fromUtf8(argv[1]);
777 QApplication app(argc,argv);
778 QMainWindow mw;
779
780 DeviceSkinParameters params;
781 QString errorMessage;
782 if (!params.read(skinFile, DeviceSkinParameters::ReadAll, &errorMessage)) {
783 qWarning() << errorMessage;
784 return 1;
785 }
786 DeviceSkin ds(params, &mw);
787 // View Dialog
788 QDialog *dialog = new QDialog();
789 QHBoxLayout *dialogLayout = new QHBoxLayout();
790 dialog->setLayout(dialogLayout);
791 QDialogButtonBox *dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
792 QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
793 QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
794 dialogLayout->addWidget(dialogButtonBox);
795 dialog->setFixedSize(params.screenSize());
796 dialog->setParent(&ds, Qt::SubWindow);
797 dialog->setAutoFillBackground(true);
798 ds.setView(dialog);
799
800 QObject::connect(&ds, &DeviceSkin::popupMenu, &mw, &QWidget::close);
801 QObject::connect(&ds, &DeviceSkin::skinKeyPressEvent, &mw, &QWidget::close);
802 mw.show();
803 return app.exec();
804}
805
806#endif
807
808QT_END_NAMESPACE
void mouseReleaseEvent(QMouseEvent *) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse release even...
void mousePressEvent(QMouseEvent *e) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse press events...
~DeviceSkin() override
void paintEvent(QPaintEvent *) override
This event handler can be reimplemented in a subclass to receive paint events passed in event.
void mouseMoveEvent(QMouseEvent *e) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse move events ...
void setZoom(double)
void setTransform(const QTransform &)
bool hasCursor() const
void moveParent()
bool eventFilter(QObject *, QEvent *) override
Filters events if this object has been installed as an event filter for the watched object.
bool handleMouseEvent(QEvent *ev)
CursorWindow(const QImage &cursor, QPoint hot, QWidget *sk)
bool event(QEvent *) override
This virtual function receives events to an object and should return true if the event e was recogniz...
QDebug operator<<(QDebug str, const DeviceSkinParameters &p)
QDebug & operator<<(QDebug &str, const DeviceSkinButtonArea &a)
static void parseRect(const QString &value, QRect *rect)
static QString msgImageNotLoaded(const QString &f)
Combined button and popup list for selecting options.
bool hasSecondaryScreen() const
QSize secondaryScreenSize() const