5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
22 #define GBALL_SIZE_MN 20
23 #define GBALL_SIZE_MX 35
24 #define CENTER_INTERVAL 50 // ms
26 #include "virtualjoystickwidget.h"
29 #include "modeledit/node.h"
31 #include "radiotrimwidget.h"
32 #include "simulator.h"
34 VirtualJoystickWidget::VirtualJoystickWidget(QWidget
*parent
, QChar side
, bool showTrims
, bool showBtns
, bool showValues
, QSize size
) :
49 ar
= (float)size
.width() / size
.height();
50 extraSize
= QSize(0, 0);
53 layout
= new QGridLayout(this);
54 layout
->setContentsMargins(0, 0, 0, 0);
55 layout
->setSpacing(0);
57 QSizePolicy
sizePolicy(QSizePolicy::Expanding
, QSizePolicy::Expanding
);
58 setSizePolicy(sizePolicy
);
60 gv
= new QGraphicsView(this);
61 gv
->setSizePolicy(sizePolicy
);
62 gv
->setMinimumSize(size
);
63 gv
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
64 gv
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
65 gv
->setRenderHints(QPainter::Antialiasing
);
67 scene
= new CustomGraphicsScene(gv
);
68 scene
->setItemIndexMethod(QGraphicsScene::NoIndex
);
72 node
->setPos(-GBALL_SIZE
/ 2, -GBALL_SIZE
/ 2);
73 node
->setBallSize(GBALL_SIZE
);
76 int colvt
, colbb
, colvx
, colvy
;
77 if (stickSide
== 'L') {
91 hTrimWidget
= createTrimWidget('H');
92 vTrimWidget
= createTrimWidget('V');
94 layout
->addWidget(vTrimWidget
, 1, colvt
, 1, 1);
95 layout
->addWidget(hTrimWidget
, 2, 2, 1, 1);
97 extraSize
+= QSize(vTrimWidget
->sizeHint().width(), hTrimWidget
->sizeHint().height());
100 colvx
= colvy
= colvt
;
104 QVBoxLayout
* btnbox
= new QVBoxLayout();
105 btnbox
->setContentsMargins(9, 9, 9, 9);
106 btnbox
->setSpacing(2);
107 btnbox
->addWidget(btnHoldY
= createButtonWidget(HOLD_Y
));
108 btnbox
->addWidget(btnFixY
= createButtonWidget(FIX_Y
));
109 btnbox
->addWidget(btnFixX
= createButtonWidget(FIX_X
));
110 btnbox
->addWidget(btnHoldX
= createButtonWidget(HOLD_X
));
112 layout
->addLayout(btnbox
, 1, colbb
, 1, 1);
114 extraSize
.setWidth(extraSize
.width() + btnbox
->sizeHint().width());
118 QLayout
* valX
= createNodeValueLayout('X', nodeLabelX
);
119 QLayout
* valY
= createNodeValueLayout('Y', nodeLabelY
);
121 QVBoxLayout
* vertXY
= new QVBoxLayout();
122 vertXY
->addLayout(valX
);
123 vertXY
->addLayout(valY
);
124 layout
->addLayout(vertXY
, 1, colvx
, 1, 1);
125 extraSize
+= QSize(nodeLabelX
->sizeHint().width(), 0);
128 layout
->addLayout(valX
, 2, colvx
, 1, 1);
129 layout
->addLayout(valY
, 2, colvy
, 1, 1);
133 layout
->addItem(new QSpacerItem(0, 0), 0, 0, 1, 5); // r0 c0-4: top v spacer
134 layout
->addItem(new QSpacerItem(0, 0), 1, 0, 2, 1); // r1-2 c0: left h spacer
135 layout
->addWidget(gv
, 1, 2, 1, 1); // r1 c2: stick widget
136 layout
->addItem(new QSpacerItem(0, 0), 1, 4, 2, 1); // r1-2 c4: right h spacer
137 layout
->addItem(new QSpacerItem(0, 0), 3, 0, 1, 5); // r4 c0-4: bot v spacer
139 setStickIndices(getStickIndex('H'), getStickIndex('V'));
141 connect(node
, &Node::xChanged
, this, &VirtualJoystickWidget::onNodeXChanged
);
142 connect(node
, &Node::yChanged
, this, &VirtualJoystickWidget::onNodeYChanged
);
144 connect(scene
, &CustomGraphicsScene::mouseEvent
, this, &VirtualJoystickWidget::onGsMouseEvent
);
146 setSize(prefSize
, frameSize());
148 centerStickTimer
.setInterval(CENTER_INTERVAL
);
149 connect(¢erStickTimer
, &QTimer::timeout
, this, &VirtualJoystickWidget::centerStick
);
150 centerStickTimer
.start();
153 void VirtualJoystickWidget::setStickX(qreal x
)
158 void VirtualJoystickWidget::setStickY(qreal y
)
163 void VirtualJoystickWidget::setStickPos(QPointF xy
)
168 void VirtualJoystickWidget::setStickAxisValue(int index
, int value
)
170 using namespace Board
;
171 qreal rvalue
= value
/ 1024.0f
;
175 if (stickSide
== 'L')
179 if (stickSide
== 'R')
183 if (stickSide
== 'L')
187 if (stickSide
== 'R')
193 void VirtualJoystickWidget::centerStick()
195 node
->stepToCenter();
198 qreal
VirtualJoystickWidget::getStickX()
200 return getStickPos().x();
203 qreal
VirtualJoystickWidget::getStickY()
205 return getStickPos().y();
208 QPointF
VirtualJoystickWidget::getStickPos()
210 return QPointF(node
->getX(), node
->getY());
213 void VirtualJoystickWidget::setTrimsRange(int min
, int max
)
216 hTrimWidget
->setTrimRange(min
, max
);
218 vTrimWidget
->setTrimRange(min
, max
);
221 void VirtualJoystickWidget::setTrimsRange(int rng
)
223 setTrimsRange(-rng
, rng
);
226 void VirtualJoystickWidget::setTrimRange(int index
, int min
, int max
)
228 if (index
== Board::TRIM_AXIS_COUNT
) {
229 setTrimsRange(min
, max
);
232 RadioTrimWidget
* trim
= getTrimWidget(index
);
234 trim
->setTrimRange(min
, max
);
238 void VirtualJoystickWidget::setTrimValue(int index
, int value
)
240 RadioTrimWidget
* trim
= getTrimWidget(index
);
242 trim
->setValue(value
);
246 int VirtualJoystickWidget::getStickScale() const
251 void VirtualJoystickWidget::setStickScale(int stickScale
)
253 m_stickScale
= stickScale
;
256 void VirtualJoystickWidget::setWidgetValue(const RadioWidget::RadioWidgetType type
, const int index
, const int value
)
258 if (type
== RadioWidget::RADIO_WIDGET_TRIM
)
259 setTrimValue(index
, value
);
260 else if (type
== RadioWidget::RADIO_WIDGET_STICK
)
261 setStickAxisValue(index
, value
);
264 int VirtualJoystickWidget::getTrimValue(int which
)
266 RadioTrimWidget
* trim
= getTrimWidget(which
);
268 return trim
->getValue();
273 void VirtualJoystickWidget::setStickIndices(int xIdx
, int yIdx
)
279 void VirtualJoystickWidget::setStickConstraint(quint8 index
, bool active
)
281 if (btnHoldX
== NULL
|| !index
)
282 return; // no buttons
285 btnHoldX
->setChecked(active
);
287 btnHoldY
->setChecked(active
);
289 btnFixX
->setChecked(active
);
291 btnFixY
->setChecked(active
);
294 void VirtualJoystickWidget::setStickColor(const QColor
& color
)
296 node
->setColor(color
);
299 void VirtualJoystickWidget::loadDefaultsForMode(const unsigned mode
)
301 if (((mode
& 1) && stickSide
== 'L') || (!(mode
& 1) && stickSide
== 'R')) {
302 setStickConstraint(HOLD_Y
, true);
308 QSize
VirtualJoystickWidget::sizeHint() const {
312 void VirtualJoystickWidget::resizeEvent(QResizeEvent
* event
)
314 QWidget::resizeEvent(event
);
315 setSize(event
->size(), event
->oldSize());
318 void VirtualJoystickWidget::onNodeXChanged()
320 emit
valueChange(RadioWidget::RADIO_WIDGET_STICK
, m_xIndex
, int(m_stickScale
* getStickX()));
321 updateNodeValueLabels();
324 void VirtualJoystickWidget::onNodeYChanged()
326 emit
valueChange(RadioWidget::RADIO_WIDGET_STICK
, m_yIndex
, int(-m_stickScale
* getStickY()));
327 updateNodeValueLabels();
330 void VirtualJoystickWidget::setSize(const QSize
& size
, const QSize
&)
332 float thisAspectRatio
= (float)size
.width() / size
.height();
333 float newGvSz
, spacerSz
;
336 if (thisAspectRatio
> ar
) {
338 newGvSz
= (height() - extraSize
.height()) * ar
; // new width
339 spacerSz
= (width() - newGvSz
- extraSize
.width()) / 2; // + 0.5;
340 if (spacerSz
>= 0.0f
) {
341 layout
->setRowStretch(0, 0);
342 layout
->setColumnStretch(0, spacerSz
);
343 layout
->setColumnStretch(4, spacerSz
);
344 layout
->setRowStretch(3, 0);
350 newGvSz
= (width() - extraSize
.width()) * ar
; // new height
351 spacerSz
= (height() - newGvSz
- extraSize
.height()) / 2; // + 0.5;
352 spacerSz
= qMax(spacerSz
, 0.0f
);
353 layout
->setRowStretch(0, spacerSz
);
354 layout
->setColumnStretch(0, 0);
355 layout
->setColumnStretch(4, 0);
356 layout
->setRowStretch(3, spacerSz
);
359 layout
->setColumnStretch(2, newGvSz
);
360 layout
->setRowStretch(1, newGvSz
);
362 gv
->resize(newGvSz
, newGvSz
);
363 gv
->updateGeometry();
365 int ballSize
= (newGvSz
* GBALL_SIZE
* 0.005f
);
366 ballSize
= qMin(GBALL_SIZE_MX
, qMax(ballSize
, GBALL_SIZE_MN
));
368 QRectF qr
= (QRectF
)gv
->contentsRect();
369 qreal w
= qr
.width() - ballSize
;
370 qreal h
= qr
.height() - ballSize
;
371 qreal cx
= qr
.width() / 2;
372 qreal cy
= qr
.height() / 2;
373 qreal nodeX
= node
->getX();
374 qreal nodeY
= node
->getY();
376 scene
->setSceneRect(-cx
,-cy
,w
,h
);
378 node
->setBallSize(ballSize
);
382 //qDebug() << thisAspectRatio << size << newGvSz << spacerSz << extraSize << gv->geometry() << gv->contentsRect() << gv->frameRect() << getStickPos();
385 RadioTrimWidget
* VirtualJoystickWidget::createTrimWidget(QChar type
)
387 RadioTrimWidget
* trimWidget
= new RadioTrimWidget(type
== 'H' ? Qt::Horizontal
: Qt::Vertical
);
388 trimWidget
->setIndices(getTrimSliderType(type
), getTrimButtonType(type
, 0), getTrimButtonType(type
, 1));
390 connect(trimWidget
, &RadioTrimWidget::valueChange
, this, &VirtualJoystickWidget::valueChange
);
395 QToolButton
* VirtualJoystickWidget::createButtonWidget(int type
)
397 QString btnLabel
, tooltip
;
401 btnLabel
= tr("Hld Y");
402 tooltip
= tr("Hold Vertical stick position.");
403 icon
= Simulator::SimulatorIcon("hold_y");
406 btnLabel
= tr("Fix Y");
407 tooltip
= tr("Prevent Vertical movement of stick.");
408 icon
= Simulator::SimulatorIcon("fixed_y");
411 btnLabel
= tr("Fix X");
412 tooltip
= tr("Prevent Horizontal movement of stick.");
413 icon
= Simulator::SimulatorIcon("fixed_x");
416 btnLabel
= tr("Hld X");
417 tooltip
= tr("Hold Horizontal stick position.");
418 icon
= Simulator::SimulatorIcon("hold_x");
423 QToolButton
* btn
= new QToolButton(this);
424 btn
->setProperty("btnType", type
);
426 btn
->setIconSize(QSize(20, 20));
427 btn
->setToolButtonStyle(Qt::ToolButtonIconOnly
);
428 btn
->setText(btnLabel
);
429 btn
->setToolTip(tooltip
);
430 btn
->setCheckable(true);
431 btn
->setAutoRaise(true);
433 connect(btn
, &QToolButton::toggled
, this, &VirtualJoystickWidget::onButtonChange
);
438 QLayout
*VirtualJoystickWidget::createNodeValueLayout(QChar type
, QLabel
*& valLabel
)
440 QLabel
* lbl
= new QLabel(QString("%1 %").arg(type
), this);
441 lbl
->setSizePolicy(QSizePolicy::Preferred
, QSizePolicy::Fixed
);
442 lbl
->setAlignment(Qt::AlignCenter
);
443 QLabel
* val
= new QLabel("0");
444 val
->setObjectName(QString("val_%1").arg(type
));
445 val
->setSizePolicy(QSizePolicy::Preferred
, QSizePolicy::Fixed
);
446 val
->setAlignment(Qt::AlignCenter
);
447 val
->setMinimumWidth(val
->fontMetrics().width("-100 "));
448 QVBoxLayout
* layout
= new QVBoxLayout();
449 layout
->setContentsMargins(2, 2, 2, 2);
450 layout
->setSpacing(2);
451 layout
->addWidget(lbl
);
452 layout
->addWidget(val
);
458 int VirtualJoystickWidget::getStickIndex(QChar type
)
460 using namespace Board
;
462 if (stickSide
== 'L') {
464 return STICK_AXIS_LH
;
466 return STICK_AXIS_LV
;
470 return STICK_AXIS_RH
;
472 return STICK_AXIS_RV
;
476 int VirtualJoystickWidget::getTrimSliderType(QChar type
)
478 using namespace Board
;
480 if (stickSide
== 'L') {
494 int VirtualJoystickWidget::getTrimButtonType(QChar type
, int pos
)
496 using namespace Board
;
498 if (stickSide
== 'L') {
501 return TRIM_SW_LH_DEC
;
503 return TRIM_SW_LH_INC
;
507 return TRIM_SW_LV_DEC
;
509 return TRIM_SW_LV_INC
;
516 return TRIM_SW_RH_DEC
;
518 return TRIM_SW_RH_INC
;
522 return TRIM_SW_RV_DEC
;
524 return TRIM_SW_RV_INC
;
529 RadioTrimWidget
* VirtualJoystickWidget::getTrimWidget(int which
)
531 if ((stickSide
== 'L' && which
== Board::TRIM_AXIS_LH
) || (stickSide
== 'R' && which
== Board::TRIM_AXIS_RH
))
533 else if ((stickSide
== 'L' && which
== Board::TRIM_AXIS_LV
) || (stickSide
== 'R' && which
== Board::TRIM_AXIS_RV
))
539 void VirtualJoystickWidget::onButtonChange(bool checked
)
541 if (!sender() || !sender()->property("btnType").isValid())
544 switch (sender()->property("btnType").toInt()) {
546 node
->setCenteringY(!checked
);
549 node
->setFixedY(checked
);
552 node
->setFixedX(checked
);
555 node
->setCenteringX(!checked
);
560 void VirtualJoystickWidget::updateNodeValueLabels()
563 nodeLabelX
->setText(QString("%1").arg(node
->getX() * 100.0f
, 2, 'f', 0));
565 nodeLabelY
->setText(QString("%1").arg(node
->getY() * -100.0f
, 2, 'f', 0));
568 void VirtualJoystickWidget::onGsMouseEvent(QGraphicsSceneMouseEvent
* event
)
573 //qDebug() << event->type() << event->scenePos() << event->buttons() << event->isAccepted() << m_stickPressed;
574 if (event
->type() == QEvent::GraphicsSceneMouseRelease
&& m_stickPressed
) {
575 node
->setPressed(false);
576 m_stickPressed
= false;
580 if (!(event
->buttons() & Qt::LeftButton
))
583 if (event
->type() == QEvent::GraphicsSceneMousePress
) {
584 node
->setPressed(true);
585 m_stickPressed
= true;
587 else if (!m_stickPressed
|| event
->type() != QEvent::GraphicsSceneMouseMove
) {
590 node
->setPos(event
->scenePos());
595 * CustomGraphicsScene
598 void CustomGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent
* event
)
600 QGraphicsScene::mousePressEvent(event
);
601 if (!event
->isAccepted()) {
603 emit
mouseEvent(event
);
607 void CustomGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent
* event
)
609 QGraphicsScene::mouseReleaseEvent(event
);
610 emit
mouseEvent(event
);
613 void CustomGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent
* event
)
615 QGraphicsScene::mouseMoveEvent(event
);
616 if (!event
->isAccepted() && (event
->buttons() & Qt::LeftButton
)) {
618 emit
mouseEvent(event
);