mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-22 13:49:53 -06:00
DolphinQt: Make input mapping and output testing non-blocking.
This commit is contained in:
@ -4,7 +4,6 @@
|
||||
#include "DolphinQt/Config/Mapping/IOWindow.h"
|
||||
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include <QBrush>
|
||||
#include <QColor>
|
||||
@ -20,16 +19,15 @@
|
||||
#include <QSlider>
|
||||
#include <QSpinBox>
|
||||
#include <QTableWidget>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Core/Core.h"
|
||||
|
||||
#include "DolphinQt/Config/Mapping/MappingCommon.h"
|
||||
#include "DolphinQt/Config/Mapping/MappingIndicator.h"
|
||||
#include "DolphinQt/Config/Mapping/MappingWidget.h"
|
||||
#include "DolphinQt/Config/Mapping/MappingWindow.h"
|
||||
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
#include "InputCommon/ControlReference/ControlReference.h"
|
||||
@ -40,6 +38,9 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto INPUT_DETECT_TIME = std::chrono::seconds(2);
|
||||
constexpr auto OUTPUT_TEST_TIME = std::chrono::seconds(2);
|
||||
|
||||
QTextCharFormat GetSpecialCharFormat()
|
||||
{
|
||||
QTextCharFormat format;
|
||||
@ -228,15 +229,17 @@ private:
|
||||
bool m_should_paint_state_indicator = false;
|
||||
};
|
||||
|
||||
IOWindow::IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* controller,
|
||||
IOWindow::IOWindow(MappingWindow* window, ControllerEmu::EmulatedController* controller,
|
||||
ControlReference* ref, IOWindow::Type type)
|
||||
: QDialog(parent), m_reference(ref), m_original_expression(ref->GetExpression()),
|
||||
: QDialog(window), m_reference(ref), m_original_expression(ref->GetExpression()),
|
||||
m_controller(controller), m_type(type)
|
||||
{
|
||||
SetQWidgetWindowDecorations(this);
|
||||
|
||||
CreateMainLayout();
|
||||
|
||||
connect(parent, &MappingWidget::Update, this, &IOWindow::Update);
|
||||
connect(parent->GetParent(), &MappingWindow::ConfigChanged, this, &IOWindow::ConfigChanged);
|
||||
connect(window, &MappingWindow::Update, this, &IOWindow::Update);
|
||||
connect(window, &MappingWindow::ConfigChanged, this, &IOWindow::ConfigChanged);
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &IOWindow::ConfigChanged);
|
||||
|
||||
setWindowTitle(type == IOWindow::Type::Input ? tr("Configure Input") : tr("Configure Output"));
|
||||
@ -258,18 +261,29 @@ void IOWindow::CreateMainLayout()
|
||||
|
||||
m_devices_combo = new QComboBox();
|
||||
m_option_list = new QTableWidget();
|
||||
m_select_button = new QPushButton(tr("Select"));
|
||||
m_detect_button = new QPushButton(tr("Detect"), this);
|
||||
m_test_button = new QPushButton(tr("Test"), this);
|
||||
|
||||
m_select_button =
|
||||
new QPushButton(m_type == IOWindow::Type::Input ? tr("Insert Input") : tr("Insert Output"));
|
||||
m_detect_button = new QPushButton(tr("Detect Input"), this);
|
||||
m_test_button = new QPushButton(tr("Test Output"), this);
|
||||
m_button_box = new QDialogButtonBox();
|
||||
m_clear_button = new QPushButton(tr("Clear"));
|
||||
m_scalar_spinbox = new QSpinBox();
|
||||
|
||||
m_parse_text = new InputStateLineEdit([this] {
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
return m_reference->GetState<ControlState>();
|
||||
});
|
||||
m_parse_text->setReadOnly(true);
|
||||
if (m_type == Type::Input)
|
||||
{
|
||||
m_parse_text = new InputStateLineEdit([this] {
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
return m_reference->GetState<ControlState>();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_parse_text = new InputStateLineEdit([this] {
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
return m_output_test_timer->isActive() * m_reference->range;
|
||||
});
|
||||
}
|
||||
|
||||
m_expression_text = new QPlainTextEdit();
|
||||
m_expression_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
@ -419,11 +433,17 @@ void IOWindow::CreateMainLayout()
|
||||
m_button_box->addButton(m_clear_button, QDialogButtonBox::ActionRole);
|
||||
m_button_box->addButton(QDialogButtonBox::Ok);
|
||||
|
||||
m_output_test_timer = new QTimer(this);
|
||||
m_output_test_timer->setSingleShot(true);
|
||||
|
||||
setLayout(m_main_layout);
|
||||
}
|
||||
|
||||
void IOWindow::ConfigChanged()
|
||||
{
|
||||
emit DetectInputComplete();
|
||||
emit TestOutputComplete();
|
||||
|
||||
const QSignalBlocker blocker(this);
|
||||
const auto lock = ControllerEmu::EmulatedController::GetStateLock();
|
||||
|
||||
@ -444,6 +464,31 @@ void IOWindow::Update()
|
||||
{
|
||||
m_option_list->viewport()->update();
|
||||
m_parse_text->update();
|
||||
|
||||
if (!m_input_detector)
|
||||
return;
|
||||
|
||||
if (m_input_detector->IsComplete())
|
||||
{
|
||||
const auto results = m_input_detector->TakeResults();
|
||||
|
||||
emit DetectInputComplete();
|
||||
|
||||
if (results.empty())
|
||||
return;
|
||||
|
||||
// Select the first detected input.
|
||||
auto list = m_option_list->findItems(QString::fromStdString(results.front().input->GetName()),
|
||||
Qt::MatchFixedString);
|
||||
if (list.empty())
|
||||
return;
|
||||
|
||||
m_option_list->setCurrentItem(list.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input_detector->Update(INPUT_DETECT_TIME, {}, INPUT_DETECT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
void IOWindow::ConnectWidgets()
|
||||
@ -453,8 +498,50 @@ void IOWindow::ConnectWidgets()
|
||||
connect(&Settings::Instance(), &Settings::ReleaseDevices, this, &IOWindow::ReleaseDevices);
|
||||
connect(&Settings::Instance(), &Settings::DevicesChanged, this, &IOWindow::UpdateDeviceList);
|
||||
|
||||
connect(m_detect_button, &QPushButton::clicked, this, &IOWindow::OnDetectButtonPressed);
|
||||
connect(m_test_button, &QPushButton::clicked, this, &IOWindow::OnTestButtonPressed);
|
||||
// Input detection:
|
||||
// Clicking "Detect" button starts a timer before the actual detection.
|
||||
auto* const input_detect_start_timer = new QTimer(this);
|
||||
input_detect_start_timer->setSingleShot(true);
|
||||
connect(m_detect_button, &QPushButton::clicked, [this, input_detect_start_timer] {
|
||||
m_detect_button->setText(tr("[ ... ]"));
|
||||
input_detect_start_timer->start(MappingCommon::INPUT_DETECT_INITIAL_DELAY);
|
||||
});
|
||||
connect(input_detect_start_timer, &QTimer::timeout, [this] {
|
||||
m_detect_button->setText(tr("[ Press Now ]"));
|
||||
m_input_detector = std::make_unique<ciface::Core::InputDetector>();
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
m_input_detector->Start(g_controller_interface, {m_devq.ToString()});
|
||||
QtUtils::InstallKeyboardBlocker(m_detect_button, this, &IOWindow::DetectInputComplete);
|
||||
});
|
||||
connect(this, &IOWindow::DetectInputComplete,
|
||||
[this, initial_text = m_detect_button->text(), input_detect_start_timer] {
|
||||
input_detect_start_timer->stop();
|
||||
m_input_detector.reset();
|
||||
m_detect_button->setText(initial_text);
|
||||
});
|
||||
|
||||
// Rumble testing:
|
||||
connect(m_test_button, &QPushButton::clicked, [this] {
|
||||
// Stop if already started.
|
||||
if (m_output_test_timer->isActive())
|
||||
{
|
||||
emit IOWindow::TestOutputComplete();
|
||||
return;
|
||||
}
|
||||
m_test_button->setText(QStringLiteral("[ ... ]"));
|
||||
m_output_test_timer->start(OUTPUT_TEST_TIME);
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
m_reference->State(1.0);
|
||||
});
|
||||
connect(m_output_test_timer, &QTimer::timeout,
|
||||
[this, initial_text = m_test_button->text()] { emit TestOutputComplete(); });
|
||||
connect(this, &IOWindow::TestOutputComplete, [this, initial_text = m_test_button->text()] {
|
||||
m_output_test_timer->stop();
|
||||
m_test_button->setText(initial_text);
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
m_reference->State(0.0);
|
||||
});
|
||||
connect(this, &QWidget::destroyed, this, &IOWindow::TestOutputComplete);
|
||||
|
||||
connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed);
|
||||
connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged);
|
||||
@ -546,30 +633,10 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button)
|
||||
}
|
||||
}
|
||||
|
||||
void IOWindow::OnDetectButtonPressed()
|
||||
{
|
||||
const auto expression =
|
||||
MappingCommon::DetectExpression(m_detect_button, g_controller_interface, {m_devq.ToString()},
|
||||
m_devq, ciface::MappingCommon::Quote::Off);
|
||||
|
||||
if (expression.isEmpty())
|
||||
return;
|
||||
|
||||
const auto list = m_option_list->findItems(expression, Qt::MatchFixedString);
|
||||
|
||||
// Try to select the first. If this fails, the last selected item would still appear as such
|
||||
if (!list.empty())
|
||||
m_option_list->setCurrentItem(list[0]);
|
||||
}
|
||||
|
||||
void IOWindow::OnTestButtonPressed()
|
||||
{
|
||||
MappingCommon::TestOutput(m_test_button, static_cast<OutputReference*>(m_reference));
|
||||
}
|
||||
|
||||
void IOWindow::OnRangeChanged(int value)
|
||||
{
|
||||
m_reference->range = value / 100.0;
|
||||
emit TestOutputComplete();
|
||||
}
|
||||
|
||||
void IOWindow::ReleaseDevices()
|
||||
@ -670,6 +737,8 @@ void IOWindow::UpdateDeviceList()
|
||||
|
||||
void IOWindow::UpdateExpression(std::string new_expression, UpdateMode mode)
|
||||
{
|
||||
emit TestOutputComplete();
|
||||
|
||||
const auto lock = m_controller->GetStateLock();
|
||||
if (mode != UpdateMode::Force && new_expression == m_reference->GetExpression())
|
||||
return;
|
||||
@ -719,6 +788,7 @@ InputStateDelegate::InputStateDelegate(IOWindow* parent, int column,
|
||||
InputStateLineEdit::InputStateLineEdit(std::function<ControlState()> state_evaluator)
|
||||
: m_state_evaluator(std::move(state_evaluator))
|
||||
{
|
||||
setReadOnly(true);
|
||||
}
|
||||
|
||||
static void PaintStateIndicator(QPainter& painter, const QRect& region, ControlState state)
|
||||
|
Reference in New Issue
Block a user