-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This class represents the state of the mouse. It is designed to wrap around specific API code so that it can be easily mocked in tests. This commit also extracts the `KeyBitState` struct from the KeyboardState class so that it can be shared between various input state classes.
- Loading branch information
1 parent
507ac14
commit a44ce11
Showing
7 changed files
with
275 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#ifndef GL_ADAGIO_BUTTONBITSTATE_H | ||
#define GL_ADAGIO_BUTTONBITSTATE_H | ||
|
||
namespace Adagio::Input { | ||
// Encodes the state of a digital button, shared by various input state objects. | ||
struct ButtonBitState { | ||
bool isDown: 1; | ||
bool isPressed: 1; | ||
bool isReleased: 1; | ||
}; | ||
} | ||
|
||
#endif //GL_ADAGIO_BUTTONBITSTATE_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#ifndef GL_ADAGIO_MOUSEHANDLER_H | ||
#define GL_ADAGIO_MOUSEHANDLER_H | ||
|
||
#include <cstdint> | ||
#include "../../math/Vector2.h" | ||
|
||
namespace Adagio { | ||
typedef std::uint8_t MouseButton; | ||
|
||
typedef std::int32_t MouseCursor; | ||
|
||
struct MouseHandler { | ||
virtual bool isMouseButtonDown(MouseButton) = 0; | ||
|
||
virtual bool isMouseButtonUp(MouseButton) = 0; | ||
|
||
virtual Vector2i getMouseCoords() = 0; | ||
|
||
virtual void setMouseCoords(Vector2i) = 0; | ||
|
||
virtual void setMouseCursor(MouseCursor) = 0; | ||
}; | ||
} | ||
|
||
#endif //GL_ADAGIO_MOUSEHANDLER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#include "MouseState.h" | ||
|
||
Adagio::MouseState::MouseState() { | ||
for (auto &button: buttonStates) { | ||
button.isDown = false; | ||
button.isPressed = false; | ||
button.isReleased = false; | ||
} | ||
} | ||
|
||
void Adagio::MouseState::setHandler(Adagio::MouseHandler *h) { | ||
handler = h; | ||
} | ||
|
||
void Adagio::MouseState::update() { | ||
updateCoords(); | ||
updateButtonStates(); | ||
} | ||
|
||
void Adagio::MouseState::updateCoords() { | ||
Vector2i prevCoords = cursorCoords; | ||
cursorCoords = handler->getMouseCoords(); | ||
cursorDelta = cursorCoords - prevCoords; | ||
} | ||
|
||
void Adagio::MouseState::updateButtonStates() { | ||
for (Input::ButtonBitState &button: buttonStates) { | ||
const long buttonIndex = &button - buttonStates; | ||
const bool isDown = handler->isMouseButtonDown(buttonIndex); | ||
button.isPressed = !button.isPressed && isDown; | ||
button.isReleased = !button.isReleased && handler->isMouseButtonUp(buttonIndex); | ||
button.isDown = isDown; | ||
} | ||
} | ||
|
||
Adagio::Vector2i Adagio::MouseState::getCursorCoords() const { | ||
return cursorCoords; | ||
} | ||
|
||
Adagio::Vector2i Adagio::MouseState::getCursorDelta() const { | ||
return cursorDelta; | ||
} | ||
|
||
bool Adagio::MouseState::isButtonDown(MouseButton btn) const { | ||
return buttonStates[btn].isDown; | ||
} | ||
|
||
bool Adagio::MouseState::isButtonUp(MouseButton btn) const { | ||
return !buttonStates[btn].isDown; | ||
} | ||
|
||
bool Adagio::MouseState::hasClickStarted(MouseButton btn) const { | ||
return buttonStates[btn].isPressed; | ||
} | ||
|
||
bool Adagio::MouseState::hasClickEnded(Adagio::MouseButton btn) const { | ||
return buttonStates[btn].isReleased; | ||
} | ||
|
||
void Adagio::MouseState::setCursorCoords(const Vector2i &coords) const { | ||
handler->setMouseCoords(coords); | ||
} | ||
|
||
void Adagio::MouseState::setCursor(Adagio::MouseCursor c) const { | ||
handler->setMouseCursor(c); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#ifndef GL_ADAGIO_MOUSESTATE_H | ||
#define GL_ADAGIO_MOUSESTATE_H | ||
|
||
#define MAX_MOUSE_BUTTONS 8 | ||
|
||
#include "MouseHandler.h" | ||
#include "../common/ButtonBitState.h" | ||
|
||
namespace Adagio { | ||
class MouseState { | ||
public: | ||
MouseState(); | ||
|
||
void setHandler(MouseHandler *); | ||
|
||
[[nodiscard]] Vector2i getCursorCoords() const; | ||
|
||
[[nodiscard]] Vector2i getCursorDelta() const; | ||
|
||
[[nodiscard]] bool isButtonDown(MouseButton) const; | ||
|
||
[[nodiscard]] bool isButtonUp(MouseButton) const; | ||
|
||
[[nodiscard]] bool hasClickStarted(MouseButton) const; | ||
|
||
[[nodiscard]] bool hasClickEnded(MouseButton) const; | ||
|
||
void setCursorCoords(const Vector2i &) const; | ||
|
||
void setCursor(MouseCursor) const; | ||
|
||
void update(); | ||
|
||
private: | ||
MouseHandler *handler{nullptr}; | ||
Vector2i cursorCoords; | ||
Vector2i cursorDelta; | ||
Input::ButtonBitState buttonStates[MAX_MOUSE_BUTTONS]{}; | ||
|
||
void updateButtonStates(); | ||
|
||
void updateCoords(); | ||
}; | ||
} | ||
|
||
#endif //GL_ADAGIO_MOUSESTATE_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#include <catch2/catch.hpp> | ||
#include "../../src/input/mouse/MouseState.h" | ||
#include <unordered_map> | ||
|
||
struct MockMouse : public Adagio::MouseHandler { | ||
Adagio::Vector2i coords{0, 0}; | ||
std::unordered_map<Adagio::MouseButton, bool> buttons; | ||
int cursor{0}; | ||
|
||
bool isMouseButtonDown(Adagio::MouseButton btn) override { | ||
return buttons[btn]; | ||
} | ||
|
||
bool isMouseButtonUp(Adagio::MouseButton btn) override { | ||
return !buttons[btn]; | ||
} | ||
|
||
Adagio::Vector2i getMouseCoords() override { | ||
return coords; | ||
} | ||
|
||
void setMouseCoords(Adagio::Vector2i c) override { | ||
coords = c; | ||
} | ||
|
||
void setMouseCursor(Adagio::MouseCursor i) override { | ||
cursor = i; | ||
} | ||
|
||
void setMockCoords(int x, int y) { | ||
coords.x = x; | ||
coords.y = y; | ||
} | ||
|
||
void pressButton(Adagio::MouseButton i) { | ||
buttons[i] = true; | ||
} | ||
|
||
void releaseButton(Adagio::MouseButton i) { | ||
buttons[i] = false; | ||
} | ||
}; | ||
|
||
TEST_CASE("MouseState", "[input][mouse]") { | ||
Adagio::MouseState mouseState; | ||
MockMouse mouse; | ||
mouseState.setHandler(&mouse); | ||
|
||
SECTION("It can get mouse coords") { | ||
mouse.setMockCoords(1336, 1235); | ||
auto nonUpdatedCoords = mouseState.getCursorCoords(); | ||
mouseState.update(); | ||
mouse.setMockCoords(1337, 1234); | ||
mouseState.update(); | ||
auto result = mouseState.getCursorCoords(); | ||
auto delta = mouseState.getCursorDelta(); | ||
REQUIRE(nonUpdatedCoords.x == 0); | ||
REQUIRE(nonUpdatedCoords.y == 0); | ||
REQUIRE(result.x == 1337); | ||
REQUIRE(result.y == 1234); | ||
REQUIRE(delta.x == 1); | ||
REQUIRE(delta.y == -1); | ||
} | ||
|
||
SECTION("It can query an empty mouse button state") { | ||
for (char i = 0; i < 3; i++) { | ||
REQUIRE_FALSE(mouseState.isButtonDown(i)); | ||
REQUIRE(mouseState.isButtonUp(i)); | ||
REQUIRE_FALSE(mouseState.hasClickStarted(i)); | ||
REQUIRE_FALSE(mouseState.hasClickEnded(i)); | ||
} | ||
} | ||
|
||
SECTION("It can detect a mouse button press") { | ||
for (char i = 0; i < 3; i++) { | ||
mouse.pressButton(i); | ||
mouseState.update(); | ||
REQUIRE(mouseState.isButtonDown(i)); | ||
REQUIRE(mouseState.hasClickStarted(i)); | ||
REQUIRE_FALSE(mouseState.isButtonUp(i)); | ||
REQUIRE_FALSE(mouseState.hasClickEnded(i)); | ||
mouseState.update(); | ||
REQUIRE_FALSE(mouseState.hasClickStarted(i)); | ||
mouse.releaseButton(i); | ||
} | ||
} | ||
|
||
SECTION("It can detect a mouse button release") { | ||
for (char i = 0; i < 3; i++) { | ||
mouse.pressButton(i); | ||
mouseState.update(); | ||
mouse.releaseButton(i); | ||
mouseState.update(); | ||
REQUIRE_FALSE(mouseState.isButtonDown(i)); | ||
REQUIRE_FALSE(mouseState.hasClickStarted(i)); | ||
REQUIRE(mouseState.isButtonUp(i)); | ||
REQUIRE(mouseState.hasClickEnded(i)); | ||
mouseState.update(); | ||
REQUIRE_FALSE(mouseState.hasClickEnded(i)); | ||
} | ||
} | ||
|
||
SECTION("It can set mouse cursor") { | ||
mouseState.setCursor(1337); | ||
REQUIRE(mouse.cursor == 1337); | ||
} | ||
|
||
SECTION("It can set mouse coords") { | ||
mouseState.setCursorCoords({123, 456}); | ||
REQUIRE(mouse.coords.x == 123); | ||
REQUIRE(mouse.coords.y == 456); | ||
} | ||
} |