Merge pull request #13320 from jordan-woyak/detect-alternations
DolphinQt/Mapping: Add setting to enable waiting for alternate mappings.
This commit is contained in:
commit
a8fae9b826
|
@ -19,8 +19,10 @@
|
||||||
namespace MappingCommon
|
namespace MappingCommon
|
||||||
{
|
{
|
||||||
constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
|
constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
|
||||||
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0);
|
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(750);
|
||||||
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
|
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
|
||||||
|
// Ignore the mouse-click when queuing more buttons with "alternate mappings" enabled.
|
||||||
|
constexpr auto INPUT_DETECT_ENDING_IGNORE_TIME = std::chrono::milliseconds(50);
|
||||||
|
|
||||||
class MappingProcessor : public QWidget
|
class MappingProcessor : public QWidget
|
||||||
{
|
{
|
||||||
|
@ -50,7 +52,7 @@ public:
|
||||||
button->StartMapping();
|
button->StartMapping();
|
||||||
|
|
||||||
std::vector device_strings{default_device.ToString()};
|
std::vector device_strings{default_device.ToString()};
|
||||||
if (m_parent->IsMappingAllDevices())
|
if (m_parent->IsCreateOtherDeviceMappingsEnabled())
|
||||||
device_strings = g_controller_interface.GetAllDeviceStrings();
|
device_strings = g_controller_interface.GetAllDeviceStrings();
|
||||||
|
|
||||||
m_input_detector = std::make_unique<ciface::Core::InputDetector>();
|
m_input_detector = std::make_unique<ciface::Core::InputDetector>();
|
||||||
|
@ -63,20 +65,26 @@ public:
|
||||||
if (!m_input_detector)
|
if (!m_input_detector)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_input_detector->Update(INPUT_DETECT_INITIAL_TIME, INPUT_DETECT_CONFIRMATION_TIME,
|
const auto confirmation_time =
|
||||||
|
INPUT_DETECT_CONFIRMATION_TIME * (m_parent->IsWaitForAlternateMappingsEnabled() ? 1 : 0);
|
||||||
|
|
||||||
|
m_input_detector->Update(INPUT_DETECT_INITIAL_TIME, confirmation_time,
|
||||||
INPUT_DETECT_MAXIMUM_TIME);
|
INPUT_DETECT_MAXIMUM_TIME);
|
||||||
|
|
||||||
if (m_input_detector->IsComplete())
|
if (m_input_detector->IsComplete())
|
||||||
{
|
{
|
||||||
auto detections = m_input_detector->TakeResults();
|
|
||||||
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
|
|
||||||
|
|
||||||
// No inputs detected. Cancel this and any other queued mappings.
|
// No inputs detected. Cancel this and any other queued mappings.
|
||||||
if (detections.empty())
|
if (!FinalizeMapping(m_input_detector->TakeResults()))
|
||||||
{
|
|
||||||
CancelMapping();
|
CancelMapping();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FinalizeMapping(ciface::Core::InputDetector::Results detections)
|
||||||
|
{
|
||||||
|
if (!ciface::MappingCommon::ContainsCompleteDetection(detections))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
|
||||||
|
|
||||||
const auto& default_device = m_parent->GetController()->GetDefaultDevice();
|
const auto& default_device = m_parent->GetController()->GetDefaultDevice();
|
||||||
auto& button = m_clicked_mapping_buttons.front();
|
auto& button = m_clicked_mapping_buttons.front();
|
||||||
|
@ -89,7 +97,7 @@ public:
|
||||||
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface,
|
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface,
|
||||||
control_reference);
|
control_reference);
|
||||||
UnQueueInputDetection(button);
|
UnQueueInputDetection(button);
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateInputDetectionStartTimer()
|
void UpdateInputDetectionStartTimer()
|
||||||
|
@ -121,6 +129,15 @@ public:
|
||||||
|
|
||||||
button->setText(QStringLiteral("[ ... ]"));
|
button->setText(QStringLiteral("[ ... ]"));
|
||||||
m_clicked_mapping_buttons.push_back(button);
|
m_clicked_mapping_buttons.push_back(button);
|
||||||
|
|
||||||
|
if (m_input_detector)
|
||||||
|
{
|
||||||
|
// Ignore the mouse-click that queued this new detection and finalize the current mapping.
|
||||||
|
auto results = m_input_detector->TakeResults();
|
||||||
|
ciface::MappingCommon::RemoveDetectionsAfterTimePoint(
|
||||||
|
&results, ciface::Core::DeviceContainer::Clock::now() - INPUT_DETECT_ENDING_IGNORE_TIME);
|
||||||
|
FinalizeMapping(std::move(results));
|
||||||
|
}
|
||||||
UpdateInputDetectionStartTimer();
|
UpdateInputDetectionStartTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,11 +111,15 @@ void MappingWindow::CreateDevicesLayout()
|
||||||
const auto refresh_action = new QAction(tr("Refresh"), options);
|
const auto refresh_action = new QAction(tr("Refresh"), options);
|
||||||
connect(refresh_action, &QAction::triggered, this, &MappingWindow::RefreshDevices);
|
connect(refresh_action, &QAction::triggered, this, &MappingWindow::RefreshDevices);
|
||||||
|
|
||||||
m_all_devices_action = new QAction(tr("Create mappings for other devices"), options);
|
m_other_device_mappings = new QAction(tr("Create Mappings for Other Devices"), options);
|
||||||
m_all_devices_action->setCheckable(true);
|
m_other_device_mappings->setCheckable(true);
|
||||||
|
|
||||||
|
m_wait_for_alternate_mappings = new QAction(tr("Wait for Alternate Input Mappings"), options);
|
||||||
|
m_wait_for_alternate_mappings->setCheckable(true);
|
||||||
|
|
||||||
options->addAction(refresh_action);
|
options->addAction(refresh_action);
|
||||||
options->addAction(m_all_devices_action);
|
options->addAction(m_other_device_mappings);
|
||||||
|
options->addAction(m_wait_for_alternate_mappings);
|
||||||
options->setDefaultAction(refresh_action);
|
options->setDefaultAction(refresh_action);
|
||||||
|
|
||||||
m_devices_combo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
m_devices_combo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||||
|
@ -354,9 +358,14 @@ void MappingWindow::OnSelectDevice(int)
|
||||||
m_controller->UpdateReferences(g_controller_interface);
|
m_controller->UpdateReferences(g_controller_interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MappingWindow::IsMappingAllDevices() const
|
bool MappingWindow::IsCreateOtherDeviceMappingsEnabled() const
|
||||||
{
|
{
|
||||||
return m_all_devices_action->isChecked();
|
return m_other_device_mappings->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MappingWindow::IsWaitForAlternateMappingsEnabled() const
|
||||||
|
{
|
||||||
|
return m_wait_for_alternate_mappings->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappingWindow::RefreshDevices()
|
void MappingWindow::RefreshDevices()
|
||||||
|
|
|
@ -51,7 +51,8 @@ public:
|
||||||
|
|
||||||
int GetPort() const;
|
int GetPort() const;
|
||||||
ControllerEmu::EmulatedController* GetController() const;
|
ControllerEmu::EmulatedController* GetController() const;
|
||||||
bool IsMappingAllDevices() const;
|
bool IsCreateOtherDeviceMappingsEnabled() const;
|
||||||
|
bool IsWaitForAlternateMappingsEnabled() const;
|
||||||
void ShowExtensionMotionTabs(bool show);
|
void ShowExtensionMotionTabs(bool show);
|
||||||
void ActivateExtensionTab();
|
void ActivateExtensionTab();
|
||||||
|
|
||||||
|
@ -103,7 +104,8 @@ private:
|
||||||
QGroupBox* m_devices_box;
|
QGroupBox* m_devices_box;
|
||||||
QHBoxLayout* m_devices_layout;
|
QHBoxLayout* m_devices_layout;
|
||||||
QComboBox* m_devices_combo;
|
QComboBox* m_devices_combo;
|
||||||
QAction* m_all_devices_action;
|
QAction* m_other_device_mappings;
|
||||||
|
QAction* m_wait_for_alternate_mappings;
|
||||||
|
|
||||||
// Profiles
|
// Profiles
|
||||||
QGroupBox* m_profiles_box;
|
QGroupBox* m_profiles_box;
|
||||||
|
|
|
@ -491,7 +491,7 @@ void InputDetector::Update(std::chrono::milliseconds initial_wait,
|
||||||
Detection new_detection;
|
Detection new_detection;
|
||||||
new_detection.device = device_state.device;
|
new_detection.device = device_state.device;
|
||||||
new_detection.input = input_state.input;
|
new_detection.input = input_state.input;
|
||||||
new_detection.press_time = Clock::now();
|
new_detection.press_time = now;
|
||||||
new_detection.smoothness = smoothness;
|
new_detection.smoothness = smoothness;
|
||||||
|
|
||||||
// We found an input. Add it to our detections.
|
// We found an input. Add it to our detections.
|
||||||
|
@ -516,12 +516,12 @@ bool InputDetector::IsComplete() const
|
||||||
return !m_state;
|
return !m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputDetector::GetResults() const -> const std::vector<Detection>&
|
auto InputDetector::GetResults() const -> const Results&
|
||||||
{
|
{
|
||||||
return m_detections;
|
return m_detections;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputDetector::TakeResults() -> std::vector<Detection>
|
auto InputDetector::TakeResults() -> Results
|
||||||
{
|
{
|
||||||
return std::move(m_detections);
|
return std::move(m_detections);
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,6 +250,7 @@ class InputDetector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Detection = DeviceContainer::InputDetection;
|
using Detection = DeviceContainer::InputDetection;
|
||||||
|
using Results = std::vector<Detection>;
|
||||||
|
|
||||||
InputDetector();
|
InputDetector();
|
||||||
~InputDetector();
|
~InputDetector();
|
||||||
|
@ -259,16 +260,16 @@ public:
|
||||||
std::chrono::milliseconds maximum_wait);
|
std::chrono::milliseconds maximum_wait);
|
||||||
bool IsComplete() const;
|
bool IsComplete() const;
|
||||||
|
|
||||||
const std::vector<Detection>& GetResults() const;
|
const Results& GetResults() const;
|
||||||
|
|
||||||
// move-return'd to prevent copying.
|
// move-return'd to prevent copying.
|
||||||
std::vector<Detection> TakeResults();
|
Results TakeResults();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
|
|
||||||
Clock::time_point m_start_time;
|
Clock::time_point m_start_time;
|
||||||
std::vector<Detection> m_detections;
|
Results m_detections;
|
||||||
std::unique_ptr<Impl> m_state;
|
std::unique_ptr<Impl> m_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,11 +51,10 @@ std::string GetExpressionForControl(const std::string& control_name,
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string BuildExpression(const Core::InputDetector::Results& detections,
|
||||||
BuildExpression(const std::vector<ciface::Core::DeviceContainer::InputDetection>& detections,
|
|
||||||
const ciface::Core::DeviceQualifier& default_device, Quote quote)
|
const ciface::Core::DeviceQualifier& default_device, Quote quote)
|
||||||
{
|
{
|
||||||
std::vector<const ciface::Core::DeviceContainer::InputDetection*> pressed_inputs;
|
std::vector<const Core::InputDetector::Detection*> pressed_inputs;
|
||||||
|
|
||||||
std::vector<std::string> alternations;
|
std::vector<std::string> alternations;
|
||||||
|
|
||||||
|
@ -135,8 +134,7 @@ BuildExpression(const std::vector<ciface::Core::DeviceContainer::InputDetection>
|
||||||
return fmt::to_string(fmt::join(alternations, "|"));
|
return fmt::to_string(fmt::join(alternations, "|"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveSpuriousTriggerCombinations(
|
void RemoveSpuriousTriggerCombinations(Core::InputDetector::Results* detections)
|
||||||
std::vector<ciface::Core::DeviceContainer::InputDetection>* detections)
|
|
||||||
{
|
{
|
||||||
const auto is_spurious = [&](const auto& detection) {
|
const auto is_spurious = [&](const auto& detection) {
|
||||||
return std::ranges::any_of(*detections, [&](const auto& d) {
|
return std::ranges::any_of(*detections, [&](const auto& d) {
|
||||||
|
@ -149,4 +147,21 @@ void RemoveSpuriousTriggerCombinations(
|
||||||
std::erase_if(*detections, is_spurious);
|
std::erase_if(*detections, is_spurious);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RemoveDetectionsAfterTimePoint(Core::InputDetector::Results* results,
|
||||||
|
Core::DeviceContainer::Clock::time_point after)
|
||||||
|
{
|
||||||
|
const auto is_after_time = [&](const Core::InputDetector::Detection& detection) {
|
||||||
|
return detection.release_time.value_or(after) >= after;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::erase_if(*results, is_after_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContainsCompleteDetection(const Core::InputDetector::Results& results)
|
||||||
|
{
|
||||||
|
return std::ranges::any_of(results, [](const Core::InputDetector::Detection& detection) {
|
||||||
|
return detection.release_time.has_value();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ciface::MappingCommon
|
} // namespace ciface::MappingCommon
|
||||||
|
|
|
@ -17,13 +17,16 @@ enum class Quote
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetExpressionForControl(const std::string& control_name,
|
std::string GetExpressionForControl(const std::string& control_name,
|
||||||
const ciface::Core::DeviceQualifier& control_device,
|
const Core::DeviceQualifier& control_device,
|
||||||
const ciface::Core::DeviceQualifier& default_device,
|
const Core::DeviceQualifier& default_device,
|
||||||
Quote quote = Quote::On);
|
Quote quote = Quote::On);
|
||||||
|
|
||||||
std::string BuildExpression(const std::vector<ciface::Core::DeviceContainer::InputDetection>&,
|
std::string BuildExpression(const Core::InputDetector::Results&,
|
||||||
const ciface::Core::DeviceQualifier& default_device, Quote quote);
|
const Core::DeviceQualifier& default_device, Quote quote);
|
||||||
|
|
||||||
void RemoveSpuriousTriggerCombinations(std::vector<ciface::Core::DeviceContainer::InputDetection>*);
|
void RemoveSpuriousTriggerCombinations(Core::InputDetector::Results*);
|
||||||
|
void RemoveDetectionsAfterTimePoint(Core::InputDetector::Results*,
|
||||||
|
Core::DeviceContainer::Clock::time_point after);
|
||||||
|
bool ContainsCompleteDetection(const Core::InputDetector::Results&);
|
||||||
|
|
||||||
} // namespace ciface::MappingCommon
|
} // namespace ciface::MappingCommon
|
||||||
|
|
Loading…
Reference in New Issue