Gui: Add standardized HintsTable for InputHints

Input hints in various DrawSketchHandler were implemented independently
using declarative mechanism with searching over lookup table. This
results in a lot of duplicated code, this commit will create generic
mechanisms that can be used to replace them.
This commit is contained in:
Kacper Donat
2025-06-28 16:47:29 +02:00
parent 207e6b576d
commit d9b4e25664
3 changed files with 129 additions and 1 deletions

View File

@@ -253,6 +253,16 @@ struct InputHint
InputSequence(const std::initializer_list<UserInput> keys)
: keys(keys)
{}
friend bool operator==(const InputSequence& lhs, const InputSequence& rhs)
{
return lhs.keys == rhs.keys;
}
friend bool operator!=(const InputSequence& lhs, const InputSequence& rhs)
{
return !(lhs == rhs);
}
};
/**
@@ -270,8 +280,41 @@ struct InputHint
* @brief List of sequences to be substituted.
*/
std::list<InputSequence> sequences;
friend bool operator==(const InputHint& lhs, const InputHint& rhs)
{
return lhs.message == rhs.message && lhs.sequences == rhs.sequences;
}
friend bool operator!=(const InputHint& lhs, const InputHint& rhs)
{
return !(lhs == rhs);
}
};
template <typename T>
struct StateHints
{
T state;
std::list<InputHint> hints;
};
template <typename T>
using HintTable = std::vector<StateHints<T>>;
template <typename T>
static std::list<InputHint> lookupHints(T state, HintTable<T> table, const std::list<InputHint>& fallback = {}) {
const auto stateMatches = [&state](const StateHints<T>& entry) {
return entry.state == state;
};
if (auto it = std::ranges::find_if(table, stateMatches); it != table.end()) {
return it->hints;
}
return fallback;
}
} // namespace Gui
#endif // GUI_INPUTHINT_H

View File

@@ -5,12 +5,12 @@ add_executable(Gui_tests_run
StyleParameters/StyleParametersApplicationTest.cpp
StyleParameters/ParserTest.cpp
StyleParameters/ParameterManagerTest.cpp
InputHintTest.cpp
)
# Qt tests
setup_qt_test(QuantitySpinBox)
target_link_libraries(Gui_tests_run PRIVATE
GTest::gtest_main
GTest::gmock_main

View File

@@ -0,0 +1,85 @@
#include <gtest/gtest.h>
#include "Gui/InputHint.h"
using namespace Gui;
using enum InputHint::UserInput;
class InputHintTest: public ::testing::Test
{
protected:
enum class State : std::uint8_t
{
SeekFirst,
SeekSecond
};
enum class Method : std::uint8_t
{
FirstMethod,
SecondMethod
};
using StateMethod = std::pair<State, Method>;
// Constants for hints
static const InputHint firstHint;
static const InputHint secondHint;
static const InputHint firstMethodHint;
static const InputHint secondMethodHint;
static const InputHint thirdMethodHint;
static const InputHint fourthMethodHint;
};
// Define the constants
const InputHint InputHintTest::firstHint = {QString("First hint"), {{KeySpace}}};
const InputHint InputHintTest::secondHint = {QString("Second hint"), {{KeyEnter}}};
const InputHint InputHintTest::firstMethodHint = {QString("First method hint"), {{KeyA}}};
const InputHint InputHintTest::secondMethodHint = {QString("Second method hint"), {{KeyB}}};
const InputHint InputHintTest::thirdMethodHint = {QString("Third method hint"), {{KeyC}}};
const InputHint InputHintTest::fourthMethodHint = {QString("Fourth method hint"), {{KeyD}}};
TEST_F(InputHintTest, LookupHintsSimpleState)
{
// Arrange
std::list<InputHint> hintsSeekFirst = {firstHint};
std::list<InputHint> hintsSeekSecond = {secondHint};
HintTable<State> table = {{.state = State::SeekFirst, .hints = hintsSeekFirst},
{.state = State::SeekSecond, .hints = hintsSeekSecond}};
// Act
auto resultFirst = lookupHints(State::SeekFirst, table);
auto resultSecond = lookupHints(State::SeekSecond, table);
// Assert
EXPECT_EQ(resultFirst, hintsSeekFirst);
EXPECT_EQ(resultSecond, hintsSeekSecond);
}
TEST_F(InputHintTest, LookupHintsPairState)
{
// Arrange
std::list<InputHint> firstFirstHints = {firstMethodHint};
std::list<InputHint> firstSecondHints = {secondMethodHint};
std::list<InputHint> secondFirstHints = {thirdMethodHint};
std::list<InputHint> secondSecondHints = {fourthMethodHint};
HintTable<StateMethod> table = {
{.state = {State::SeekFirst, Method::FirstMethod}, .hints = firstFirstHints},
{.state = {State::SeekFirst, Method::SecondMethod}, .hints = firstSecondHints},
{.state = {State::SeekSecond, Method::FirstMethod}, .hints = secondFirstHints},
{.state = {State::SeekSecond, Method::SecondMethod}, .hints = secondSecondHints}};
// Act
auto resultFirstFirst = lookupHints({State::SeekFirst, Method::FirstMethod}, table);
auto resultFirstSecond = lookupHints({State::SeekFirst, Method::SecondMethod}, table);
auto resultSecondFirst = lookupHints({State::SeekSecond, Method::FirstMethod}, table);
auto resultSecondSecond = lookupHints({State::SeekSecond, Method::SecondMethod}, table);
// Assert
EXPECT_EQ(resultFirstFirst, firstFirstHints);
EXPECT_EQ(resultFirstSecond, firstSecondHints);
EXPECT_EQ(resultSecondFirst, secondFirstHints);
EXPECT_EQ(resultSecondSecond, secondSecondHints);
}