diff --git a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp index 0ff955a2c4..4cfe396f69 100644 --- a/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/IOWindow.cpp @@ -11,21 +11,26 @@ #include #include #include +#include +#include #include #include -#include -#include +#include #include #include #include #include +#include #include #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 "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControlReference/ExpressionParser.h" @@ -184,12 +189,26 @@ void ControlExpressionSyntaxHighlighter::highlightBlock(const QString&) } } -IOWindow::IOWindow(QWidget* parent, ControllerEmu::EmulatedController* controller, +class InputStateDelegate : public QItemDelegate +{ +public: + explicit InputStateDelegate(IOWindow* parent); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const override; + +private: + IOWindow* m_parent; +}; + +IOWindow::IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* controller, ControlReference* ref, IOWindow::Type type) : QDialog(parent), m_reference(ref), m_controller(controller), m_type(type) { CreateMainLayout(); + connect(parent, &MappingWidget::Update, this, &IOWindow::Update); + setWindowTitle(type == IOWindow::Type::Input ? tr("Configure Input") : tr("Configure Output")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -198,15 +217,20 @@ IOWindow::IOWindow(QWidget* parent, ControllerEmu::EmulatedController* controlle ConnectWidgets(); } +std::shared_ptr IOWindow::GetSelectedDevice() +{ + return m_selected_device; +} + void IOWindow::CreateMainLayout() { m_main_layout = new QVBoxLayout(); m_devices_combo = new QComboBox(); - m_option_list = new QListWidget(); + m_option_list = new QTableWidget(); m_select_button = new QPushButton(tr("Select")); - m_detect_button = new QPushButton(tr("Detect")); - m_test_button = new QPushButton(tr("Test")); + m_detect_button = new QPushButton(tr("Detect"), this); + m_test_button = new QPushButton(tr("Test"), this); m_button_box = new QDialogButtonBox(); m_clear_button = new QPushButton(tr("Clear")); m_apply_button = new QPushButton(tr("Apply")); @@ -242,7 +266,7 @@ void IOWindow::CreateMainLayout() m_operators_combo->addItem(tr(", Comma")); } - m_functions_combo = new QComboBox(); + m_functions_combo = new QComboBox(this); m_functions_combo->addItem(tr("Functions")); m_functions_combo->insertSeparator(1); m_functions_combo->addItem(QStringLiteral("if")); @@ -269,6 +293,30 @@ void IOWindow::CreateMainLayout() m_range_spinbox->setMaximum(500); m_main_layout->addLayout(range_hbox); + // Options (Buttons, Outputs) and action buttons + + if (m_type == Type::Input) + { + m_option_list->setColumnCount(2); + m_option_list->setColumnWidth(1, 64); + m_option_list->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed); + + m_option_list->setItemDelegate(new InputStateDelegate(this)); + } + else + { + m_option_list->setColumnCount(1); + } + + m_option_list->horizontalHeader()->hide(); + m_option_list->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + m_option_list->verticalHeader()->hide(); + m_option_list->verticalHeader()->setDefaultSectionSize( + m_option_list->verticalHeader()->minimumSectionSize()); + m_option_list->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_option_list->setSelectionBehavior(QAbstractItemView::SelectRows); + m_option_list->setSelectionMode(QAbstractItemView::SingleSelection); + auto* hbox = new QHBoxLayout(); auto* button_vbox = new QVBoxLayout(); hbox->addWidget(m_option_list, 8); @@ -310,6 +358,11 @@ void IOWindow::ConfigChanged() UpdateOptionList(); } +void IOWindow::Update() +{ + m_option_list->viewport()->update(); +} + void IOWindow::ConnectWidgets() { connect(m_select_button, &QPushButton::clicked, [this] { AppendSelectedOption(); }); @@ -378,12 +431,7 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button) if (ciface::ExpressionParser::ParseStatus::SyntaxError == m_reference->GetParseStatus()) { - QMessageBox error(this); - error.setIcon(QMessageBox::Critical); - error.setWindowTitle(tr("Error")); - error.setText(tr("The expression contains a syntax error.")); - error.setWindowModality(Qt::WindowModal); - error.exec(); + ModalMessageBox::warning(this, tr("Error"), tr("The expression contains a syntax error.")); } if (button != m_apply_button) @@ -419,25 +467,32 @@ void IOWindow::OnRangeChanged(int value) void IOWindow::UpdateOptionList() { - m_option_list->clear(); + m_selected_device = g_controller_interface.FindDevice(m_devq); + m_option_list->setRowCount(0); - const auto device = g_controller_interface.FindDevice(m_devq); - - if (device == nullptr) + if (m_selected_device == nullptr) return; if (m_reference->IsInput()) { - for (const auto* input : device->Inputs()) + int row = 0; + for (const auto* input : m_selected_device->Inputs()) { - m_option_list->addItem(QString::fromStdString(input->GetName())); + m_option_list->insertRow(row); + m_option_list->setItem(row, 0, + new QTableWidgetItem(QString::fromStdString(input->GetName()))); + ++row; } } else { - for (const auto* output : device->Outputs()) + int row = 0; + for (const auto* output : m_selected_device->Outputs()) { - m_option_list->addItem(QString::fromStdString(output->GetName())); + m_option_list->insertRow(row); + m_option_list->setItem(row, 0, + new QTableWidgetItem(QString::fromStdString(output->GetName()))); + ++row; } } } @@ -452,3 +507,45 @@ void IOWindow::UpdateDeviceList() m_devices_combo->setCurrentText( QString::fromStdString(m_controller->GetDefaultDevice().ToString())); } + +InputStateDelegate::InputStateDelegate(IOWindow* parent) : QItemDelegate(parent), m_parent(parent) +{ +} + +void InputStateDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QItemDelegate::paint(painter, option, index); + + // Don't do anything special for the first column. + if (index.column() == 0) + return; + + // Clamp off negative values but allow greater than one in the text display. + const auto state = + std::max(m_parent->GetSelectedDevice()->Inputs()[index.row()]->GetState(), 0.0); + const auto state_str = QString::number(state, 'g', 4); + + QRect rect = option.rect; + rect.setWidth(rect.width() * std::clamp(state, 0.0, 1.0)); + + // Create a temporary indicator object to retreive color constants. + MappingIndicator indicator(nullptr); + + painter->save(); + + // Normal text. + painter->setPen(indicator.GetTextColor()); + painter->drawText(option.rect, Qt::AlignCenter, state_str); + + // Input state meter. + painter->fillRect(rect, indicator.GetAdjustedInputColor()); + + // Text on top of meter. + painter->setPen(indicator.GetAltTextColor()); + painter->setClipping(true); + painter->setClipRect(rect); + painter->drawText(option.rect, Qt::AlignCenter, state_str); + + painter->restore(); +} diff --git a/Source/Core/DolphinQt/Config/Mapping/IOWindow.h b/Source/Core/DolphinQt/Config/Mapping/IOWindow.h index 1d8a3f4f2f..9870a4c954 100644 --- a/Source/Core/DolphinQt/Config/Mapping/IOWindow.h +++ b/Source/Core/DolphinQt/Config/Mapping/IOWindow.h @@ -12,11 +12,12 @@ #include "InputCommon/ControllerInterface/Device.h" class ControlReference; +class MappingWidget; class QAbstractButton; class QComboBox; class QDialogButtonBox; class QLineEdit; -class QListWidget; +class QTableWidget; class QVBoxLayout; class QWidget; class QPlainTextEdit; @@ -52,13 +53,16 @@ public: Output }; - explicit IOWindow(QWidget* parent, ControllerEmu::EmulatedController* m_controller, + explicit IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* m_controller, ControlReference* ref, Type type); + std::shared_ptr GetSelectedDevice(); + private: void CreateMainLayout(); void ConnectWidgets(); void ConfigChanged(); + void Update(); void OnDialogButtonPressed(QAbstractButton* button); void OnDeviceChanged(const QString& device); @@ -77,7 +81,7 @@ private: QComboBox* m_devices_combo; // Options - QListWidget* m_option_list; + QTableWidget* m_option_list; // Range QSlider* m_range_slider; @@ -108,4 +112,5 @@ private: ciface::Core::DeviceQualifier m_devq; Type m_type; + std::shared_ptr m_selected_device; }; diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp index 6b1b1fce73..9dcb9d0766 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingButton.cpp @@ -73,7 +73,7 @@ MappingButton::MappingButton(MappingWidget* parent, ControlReference* ref, bool void MappingButton::AdvancedPressed() { - IOWindow io(this, m_parent->GetController(), m_reference, + IOWindow io(m_parent, m_parent->GetController(), m_reference, m_reference->IsInput() ? IOWindow::Type::Input : IOWindow::Type::Output); io.exec(); diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp index a1d0eb144c..6b2b83d2ab 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp @@ -105,7 +105,7 @@ MappingIndicator::MappingIndicator(ControllerEmu::ControlGroup* group) : m_group // TODO: Make these magic numbers less ugly. int required_height = 106; - if (ControllerEmu::GroupType::MixedTriggers == group->type) + if (group && ControllerEmu::GroupType::MixedTriggers == group->type) required_height = 64 + 1; setFixedHeight(required_height); diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h index c380d05b95..669f4f8d9e 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h +++ b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h @@ -33,9 +33,6 @@ public: void SetCalibrationWidget(CalibrationWidget* widget); -protected: - WiimoteEmu::MotionState m_motion_state{}; - QPen GetBBoxPen() const; QBrush GetBBoxBrush() const; QColor GetRawInputColor() const; @@ -49,8 +46,11 @@ protected: QColor GetAltTextColor() const; QColor GetGateColor() const; +protected: double GetScale() const; + WiimoteEmu::MotionState m_motion_state{}; + private: void DrawCursor(ControllerEmu::Cursor& cursor); void DrawReshapableInput(ControllerEmu::ReshapableInput& stick); diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp index b9149027ea..6781bcb3da 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp @@ -9,7 +9,6 @@ #include #include -#include "DolphinQt/Config/Mapping/IOWindow.h" #include "DolphinQt/Config/Mapping/MappingButton.h" #include "DolphinQt/Config/Mapping/MappingIndicator.h" #include "DolphinQt/Config/Mapping/MappingNumeric.h"