[Simulator] Further improvements (reload radio, momentary switches, etc.) (#4303)
* [Simulator] Add ability to reload all radio data w/out exiting simulator (will also adjust UI for any hardware changes); Refactor how keymap help text is generated.
* [Simulator] Only update channel/logical switch/GVar output displays if the corresponding tab is actually visible (further CPU usage reductions).
* [Simulator] Implement momentary toggle switches with "latch" function.
* [Simulator] Mouse wheel event over multi-position knobs now properly rotates them to detent positions.
* [Simulator] Fix mouse wheel event propagation after multi-position knob rotation; Cosmetics.
* [Simulator] Internals: Greatly improve object cleanup on exit; Refactor SimulatedUIWidget timer event triggering.
* [Simulator] Fix throttle stick position/lock not being set properly upon repeated launches from Companion.
* [Simulator] Fix build w/out SDL. KnobWidget cleanup.
* [Companion] Do NOT delete SimulatorInterface on simulator dialog exit (fix possible crash on dialog close).
* [Simulator] Ensure joystick is NULL at start.
* [Simulator] Fix/improve color blending on 4-bit screens with different BG colors; Cosmetics.
* [Simulator] Fix LCD screenshot folder path; Fix timer slot for Qt backwards compatibility.
* [TravisCI] Set Qt version to 5.3 to match release build servers.
* Fixed segfault when saving setup with no models defined [Horus]
* Fixed radio data import [Horus] (broken in 0f90ff0b65)
* [Simulator] Always keep toggle switch active while held with mouse button.
This commit is contained in:
committed by
Bertrand Songis
parent
f40e64c2aa
commit
de68db0905
@@ -72,9 +72,7 @@ class LcdWidget : public QWidget
|
||||
|
||||
void setBackgroundColor(QColor color)
|
||||
{
|
||||
_r = color.red();
|
||||
_g = color.green();
|
||||
_b = color.blue();
|
||||
bgColor = color;
|
||||
}
|
||||
|
||||
void makeScreenshot(const QString & fileName)
|
||||
@@ -121,79 +119,87 @@ class LcdWidget : public QWidget
|
||||
unsigned char *previousBuf;
|
||||
|
||||
bool lightEnable;
|
||||
int _r, _g, _b;
|
||||
QColor bgColor;
|
||||
QColor bgDefaultColor;
|
||||
|
||||
inline void doPaint(QPainter & p)
|
||||
{
|
||||
QRgb rgb;
|
||||
uint16_t z;
|
||||
|
||||
if (!lcdBuf)
|
||||
return;
|
||||
|
||||
if (lcdDepth == 16) {
|
||||
for (int x=0; x<lcdWidth; x++) {
|
||||
for (int y=0; y<lcdHeight; y++) {
|
||||
uint16_t z = ((uint16_t *)lcdBuf)[y * lcdWidth + x];
|
||||
rgb = qRgb(255*((z&0xF800)>>11)/0x1F, 255*((z&0x07E0)>>5)/0x3F, 255*(z&0x001F)/0x1F);
|
||||
for (int x = 0; x < lcdWidth; x++) {
|
||||
for (int y = 0; y < lcdHeight; y++) {
|
||||
z = ((uint16_t *)lcdBuf)[y * lcdWidth + x];
|
||||
rgb = qRgb(255 * ((z & 0xF800) >> 11) / 0x1F,
|
||||
255 * ((z & 0x07E0) >> 5) / 0x3F,
|
||||
255 * (z & 0x001F) / 0x1F);
|
||||
p.setPen(rgb);
|
||||
p.drawPoint(x, y);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (lcdDepth == 12) {
|
||||
for (int x=0; x<lcdWidth; x++) {
|
||||
for (int y=0; y<lcdHeight; y++) {
|
||||
uint16_t z = ((uint16_t *)lcdBuf)[y * lcdWidth + x];
|
||||
rgb = qRgb(255*((z&0xF00)>>8)/0x0f, 255*((z&0x0F0)>>4)/0x0f, 255*(z&0x00F)/0x0f);
|
||||
if (lcdDepth == 12) {
|
||||
for (int x = 0; x < lcdWidth; x++) {
|
||||
for (int y = 0; y < lcdHeight; y++) {
|
||||
z = ((uint16_t *)lcdBuf)[y * lcdWidth + x];
|
||||
rgb = qRgb(255 * ((z & 0xF00) >> 8) / 0x0F,
|
||||
255 * ((z & 0x0F0) >> 4) / 0x0F,
|
||||
255 * (z & 0x00F) / 0x0F);
|
||||
p.setPen(rgb);
|
||||
p.drawPoint(x, y);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (lightEnable)
|
||||
rgb = qRgb(_r, _g, _b);
|
||||
else
|
||||
rgb = bgDefaultColor.rgba();
|
||||
|
||||
p.setBackground(QBrush(rgb));
|
||||
p.eraseRect(0, 0, 2*lcdWidth, 2*lcdHeight);
|
||||
QColor bg;
|
||||
if (lightEnable)
|
||||
bg = bgColor;
|
||||
else
|
||||
bg = bgDefaultColor;
|
||||
|
||||
if (lcdBuf) {
|
||||
p.setBackground(QBrush(bg));
|
||||
p.eraseRect(0, 0, 2*lcdWidth, 2*lcdHeight);
|
||||
|
||||
if (lcdDepth == 1) {
|
||||
rgb = qRgb(0, 0, 0);
|
||||
p.setPen(rgb);
|
||||
p.setBrush(QBrush(rgb));
|
||||
}
|
||||
|
||||
uint16_t idx, mask;
|
||||
uint16_t previousDepth = 0xFF;
|
||||
|
||||
for (int y = 0; y < lcdHeight; y++) {
|
||||
idx = (y * lcdDepth / 8) * lcdWidth;
|
||||
mask = (1 << (y % 8));
|
||||
for (int x = 0; x < lcdWidth; x++, idx++) {
|
||||
if (lcdDepth == 1) {
|
||||
rgb = qRgb(0, 0, 0);
|
||||
if (lcdBuf[idx] & mask)
|
||||
p.drawRect(2 * x, 2 * y, 1, 1);
|
||||
continue;
|
||||
}
|
||||
// lcdDepth == 4
|
||||
z = (y & 1) ? (lcdBuf[idx] >> 4) : (lcdBuf[idx] & 0x0F);
|
||||
if (!z)
|
||||
continue;
|
||||
if (z != previousDepth) {
|
||||
previousDepth = z;
|
||||
rgb = qRgb(bg.red() - (z * bg.red()) / 15,
|
||||
bg.green() - (z * bg.green()) / 15,
|
||||
bg.blue() - (z * bg.blue()) / 15);
|
||||
p.setPen(rgb);
|
||||
p.setBrush(QBrush(rgb));
|
||||
}
|
||||
|
||||
unsigned int previousDepth = 0xFF;
|
||||
|
||||
for (int y=0; y<lcdHeight; y++) {
|
||||
unsigned int idx = (y*lcdDepth/8)*lcdWidth;
|
||||
unsigned int mask = (1 << (y%8));
|
||||
for (int x=0; x<lcdWidth; x++, idx++) {
|
||||
if (lcdDepth == 1) {
|
||||
if (lcdBuf[idx] & mask) {
|
||||
p.drawRect(2*x, 2*y, 1, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int z = (y & 1) ? (lcdBuf[idx] >> 4) : (lcdBuf[idx] & 0x0F);
|
||||
if (z) {
|
||||
if (z != previousDepth) {
|
||||
previousDepth = z;
|
||||
if (lightEnable)
|
||||
rgb = qRgb(_r-(z*_r)/15, _g-(z*_g)/15, _b-(z*_b)/15);
|
||||
else
|
||||
rgb = qRgb(161-(z*161)/15, 161-(z*161)/15, 161-(z*161)/15);
|
||||
p.setPen(rgb);
|
||||
p.setBrush(QBrush(rgb));
|
||||
}
|
||||
p.drawRect(2*x, 2*y, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.drawRect(2*x, 2*y, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void paintEvent(QPaintEvent*)
|
||||
|
||||
@@ -68,29 +68,27 @@ class RadioKnobWidget : public RadioWidget
|
||||
|
||||
class KnobWidget : public QDial
|
||||
{
|
||||
friend class RadioKnobWidget;
|
||||
|
||||
public:
|
||||
|
||||
explicit KnobWidget(Board::PotType type, QWidget * parent = 0):
|
||||
QDial(parent),
|
||||
m_type(type),
|
||||
m_positions(type == Board::POT_MULTIPOS_SWITCH ? 5 : 0)
|
||||
QDial(parent)
|
||||
{
|
||||
setToolTip(tr("Right-double-click to reset to center."));
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
setFixedSize(QSize(42, 42));
|
||||
setMinimum(-1024);
|
||||
setMaximum(1024);
|
||||
setNotchesVisible(true);
|
||||
if (m_positions) {
|
||||
if (type == Board::POT_MULTIPOS_SWITCH) {
|
||||
m_stepSize = 2048 / 5;
|
||||
// this is a bit of a hack to get the notch markers to display correctly
|
||||
// the actual notches/value are constrained in setValue()
|
||||
setSingleStep(2048 / m_positions / 10);
|
||||
setPageStep(2048 / m_positions);
|
||||
setSingleStep(m_stepSize / 10);
|
||||
setPageStep(m_stepSize);
|
||||
setNotchTarget(5.7);
|
||||
}
|
||||
else {
|
||||
m_stepSize = 1;
|
||||
setToolTip(tr("Right-double-click to reset to center."));
|
||||
setPageStep(128);
|
||||
setNotchTarget(64);
|
||||
}
|
||||
@@ -98,15 +96,15 @@ class RadioKnobWidget : public RadioWidget
|
||||
|
||||
void setValue(int value)
|
||||
{
|
||||
if (m_positions) {
|
||||
value = ((int)roundf((value + 1024) / (2048.0f / m_positions)) * (2048.0f / m_positions) - 1024);
|
||||
if (m_stepSize > 1) {
|
||||
value = (value + 1024) / m_stepSize * m_stepSize - 1024;
|
||||
}
|
||||
QDial::setValue(value);
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent * event)
|
||||
{
|
||||
if (event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
|
||||
if (m_stepSize == 1 && event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
|
||||
setValue(0);
|
||||
event->accept();
|
||||
return;
|
||||
@@ -114,8 +112,21 @@ class RadioKnobWidget : public RadioWidget
|
||||
QDial::mousePressEvent(event);
|
||||
}
|
||||
|
||||
Board::PotType m_type;
|
||||
quint8 m_positions;
|
||||
void wheelEvent(QWheelEvent * event)
|
||||
{
|
||||
if (event->angleDelta().isNull())
|
||||
return;
|
||||
|
||||
if (m_stepSize > 1) {
|
||||
int numSteps = event->angleDelta().y() / 8 / 15 * m_stepSize; // one step per 15deg
|
||||
setValue(value() + numSteps);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
QDial::wheelEvent(event);
|
||||
}
|
||||
|
||||
quint16 m_stepSize;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
#include "boards.h"
|
||||
|
||||
#include <QSlider>
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
|
||||
class RadioSwitchWidget : public RadioWidget
|
||||
{
|
||||
@@ -50,27 +51,88 @@ class RadioSwitchWidget : public RadioWidget
|
||||
{
|
||||
m_type = RADIO_WIDGET_SWITCH;
|
||||
|
||||
QSlider * sl = new QSlider();
|
||||
sl->setOrientation(Qt::Vertical);
|
||||
sl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
sl->setFixedHeight(50);
|
||||
sl->setInvertedAppearance(true);
|
||||
sl->setInvertedControls(true);
|
||||
sl->setTickPosition(QSlider::TicksBothSides);
|
||||
sl->setPageStep(1);
|
||||
sl->setMinimum((swType == Board::SWITCH_3POS ? -1 : 0));
|
||||
sl->setMaximum(1);
|
||||
sl->setTickInterval(1);
|
||||
sl->setValue(m_value);
|
||||
m_slider = new QSlider();
|
||||
m_slider->setOrientation(Qt::Vertical);
|
||||
m_slider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
m_slider->setInvertedAppearance(true);
|
||||
m_slider->setInvertedControls(true);
|
||||
m_slider->setTickPosition(QSlider::TicksBothSides);
|
||||
m_slider->setPageStep(1);
|
||||
m_slider->setMinimum((swType == Board::SWITCH_3POS ? -1 : 0));
|
||||
m_slider->setMaximum(1);
|
||||
m_slider->setTickInterval(1);
|
||||
m_slider->setValue(m_value);
|
||||
|
||||
connect(sl, &QSlider::valueChanged, this, &RadioWidget::setValue);
|
||||
connect(this, &RadioWidget::valueChanged, sl, &QSlider::setValue);
|
||||
if (swType == Board::SWITCH_TOGGLE) {
|
||||
QIcon icon;
|
||||
icon.addFile(QStringLiteral(":/images/simulator/icons/8/lock-locked.png"), QSize(8, 8), QIcon::Normal, QIcon::On);
|
||||
icon.addFile(QStringLiteral(":/images/simulator/icons/8/lock-unlocked.png"), QSize(8, 8), QIcon::Normal, QIcon::Off);
|
||||
QToolButton * lockBtn = new QToolButton(this);
|
||||
lockBtn->setIcon(icon);
|
||||
lockBtn->setIconSize(QSize(8, 8));
|
||||
lockBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
lockBtn->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||||
lockBtn->setAutoRaise(true);
|
||||
lockBtn->setCheckable(true);
|
||||
lockBtn->setToolTip(tr("Latch/unlatch the momentary switch."));
|
||||
|
||||
setWidget(sl);
|
||||
QWidget * container = new QWidget(this);
|
||||
container->setFixedHeight(50);
|
||||
container->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
QVBoxLayout * cl = new QVBoxLayout(container);
|
||||
cl->setContentsMargins(0, 0, 0, 0);
|
||||
cl->setSpacing(1);
|
||||
cl->addWidget(m_slider, 1, Qt::AlignHCenter);
|
||||
cl->addWidget(lockBtn, 0, Qt::AlignHCenter);
|
||||
|
||||
setWidget(container);
|
||||
|
||||
connect(lockBtn, &QToolButton::toggled, this, &RadioSwitchWidget::setToggleLocked);
|
||||
connect(m_slider, &QSlider::sliderReleased, this, &RadioSwitchWidget::onMomentaryReleased);
|
||||
connect(m_slider, &QSlider::valueChanged, this, &RadioSwitchWidget::onMomentaryReleased);
|
||||
connect(this, &RadioWidget::flagsChanged, lockBtn, &QToolButton::setChecked);
|
||||
}
|
||||
else {
|
||||
m_slider->setFixedHeight(50);
|
||||
setWidget(m_slider);
|
||||
}
|
||||
|
||||
connect(m_slider, &QSlider::valueChanged, this, &RadioWidget::setValue);
|
||||
connect(this, &RadioWidget::valueChanged, m_slider, &QSlider::setValue);
|
||||
|
||||
}
|
||||
|
||||
void setToggleLocked(bool lock)
|
||||
{
|
||||
if (m_flags != lock) {
|
||||
m_flags = lock;
|
||||
setValue((int)lock);
|
||||
emit flagsChanged(m_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void resetToggle()
|
||||
{
|
||||
setValue(0);
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
||||
void onMomentaryReleased()
|
||||
{
|
||||
if (!m_flags && !m_slider->isSliderDown())
|
||||
QTimer::singleShot(500, this, SLOT(onMomentaryTimeout()));
|
||||
}
|
||||
|
||||
void onMomentaryTimeout()
|
||||
{
|
||||
if (!m_flags)
|
||||
resetToggle();
|
||||
}
|
||||
|
||||
private:
|
||||
Board::SwitchType swType;
|
||||
QSlider * m_slider;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ QByteArray RadioWidget::getStateData() const
|
||||
|
||||
RadioWidget::RadioWidgetState RadioWidget::getState() const
|
||||
{
|
||||
return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value));;
|
||||
return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value), quint16(m_flags));;
|
||||
}
|
||||
|
||||
void RadioWidget::init()
|
||||
|
||||
Reference in New Issue
Block a user