Sketch of pointer/message APIs
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index a246f5b..e9a32d8 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -6,9 +6,13 @@ #include "rive/core_context.hpp" #include "rive/generated/artboard_base.hpp" #include "rive/hit_info.hpp" +#include "rive/message.hpp" +#include "rive/pointer_event.hpp" #include "rive/math/aabb.hpp" #include "rive/renderer.hpp" #include "rive/shapes/shape_paint_container.hpp" + +#include <queue> #include <vector> namespace rive { @@ -40,6 +44,8 @@ bool m_IsInstance = false; bool m_FrameOrigin = true; + std::queue<Message> m_MessageQueue; + void sortDependencies(); void sortDrawOrder(); @@ -51,6 +57,8 @@ void addStateMachine(StateMachine* object); void addNestedArtboard(NestedArtboard* object); + void testing_only_enque_message(const Message&); + public: ~Artboard(); StatusCode initialize(); @@ -70,6 +78,24 @@ bool advance(double elapsedSeconds); + // Call this to forward pointer events to the artboard + // They will be processed when advance() is called. + // + void postPointerEvent(const PointerEvent&); + + // Returns true iff calling popMessage() will return true. + bool hasMessages() const; + + // If there are any queued messages... + // copies the first message into msg parameter + // removes that message from the queue + // returns true + // else + // ignores msg parameter + // returns false + // + bool nextMessage(Message* msg); + enum class DrawOption { kNormal, kHideBG,
diff --git a/include/rive/message.hpp b/include/rive/message.hpp new file mode 100644 index 0000000..5fda832 --- /dev/null +++ b/include/rive/message.hpp
@@ -0,0 +1,19 @@ +#ifndef _RIVE_MESSAGE_HPP_ +#define _RIVE_MESSAGE_HPP_ + +#include "rive/math/vec2d.hpp" +#include <string> + +namespace rive { + +struct Message { + // TODO -- how to represent this? + // Perhaps some sort of JSON-like object: key-value pairs? + // For now, store a string so we can test it... + + std::string m_Str; +}; + +} // namespace rive + +#endif
diff --git a/include/rive/pointer_event.hpp b/include/rive/pointer_event.hpp new file mode 100644 index 0000000..7b29707 --- /dev/null +++ b/include/rive/pointer_event.hpp
@@ -0,0 +1,24 @@ +#ifndef _RIVE_POINTER_EVENT_HPP_ +#define _RIVE_POINTER_EVENT_HPP_ + +#include "rive/math/vec2d.hpp" + +namespace rive { + +enum class PointerEventType { + down, // The button has gone from up to down + move, // The pointer's position has changed + up, // The button has gone from down to up +}; + +struct PointerEvent { + PointerEventType m_Type; + Vec2D m_Position; + int m_PointerIndex; + + // add more fields as needed +}; + +} // namespace rive + +#endif
diff --git a/skia/viewer/build/premake5.lua b/skia/viewer/build/premake5.lua index acc6885..1442293 100644 --- a/skia/viewer/build/premake5.lua +++ b/skia/viewer/build/premake5.lua
@@ -31,7 +31,7 @@ "../../dependencies/imgui/imgui.cpp", "../../dependencies/imgui/imgui_tables.cpp", "../../dependencies/imgui/imgui_draw.cpp"} -buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti", "-flto=full"} +buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti", "-flto=full", "-g"} filter "configurations:debug" defines {"DEBUG"} symbols "On"
diff --git a/skia/viewer/src/main.cpp b/skia/viewer/src/main.cpp index c14e6e1..6ce3d05 100644 --- a/skia/viewer/src/main.cpp +++ b/skia/viewer/src/main.cpp
@@ -129,6 +129,61 @@ initAnimation(0); } +// returns the mouse position, transforming it through the inverse of +// the canvas' CTM -- which may have been altered to scale/translate +// the artboard into the window. +// +static void post_mouse_event(rive::Artboard* artboard, const SkMatrix& ctm) { + static ImVec2 gPrevMousePos = {-1000, -1000}; + const auto mouse = ImGui::GetMousePos(); + + static bool gPrevMouseButtonDown = false; + const bool isDown = ImGui::IsMouseDown(ImGuiMouseButton_Left); + + if (mouse.x == gPrevMousePos.x && + mouse.y == gPrevMousePos.y && + isDown == gPrevMouseButtonDown) + { + return; + } + + auto evtType = rive::PointerEventType::move; + if (isDown && !gPrevMouseButtonDown) { + evtType = rive::PointerEventType::down; // we just went down + } else if (!isDown && gPrevMouseButtonDown) { + evtType = rive::PointerEventType::up; // we just went up + } + + gPrevMousePos = mouse; + gPrevMouseButtonDown = isDown; + + SkMatrix inv; + (void)ctm.invert(&inv); + + // scale by 2 for the DPI of a high-res monitor + const auto pt = inv.mapXY(mouse.x * 2, mouse.y * 2); + + const int pointerIndex = 0; // til we track more than one button/mouse + rive::PointerEvent evt = { + evtType, + {pt.fX, pt.fY}, + pointerIndex, + }; + artboard->postPointerEvent(evt); +} + +static void test_messages(rive::Artboard* artboard) { + rive::Message msg; + int i = 0; + bool hasAny = artboard->hasMessages(); + + while (artboard->nextMessage(&msg)) { + printf("-- message[%d]: '%s'\n", i, msg.m_Str.c_str()); + i += 1; + } + assert((hasAny && i > 0) || (!hasAny && i == 0)); +} + int main() { if (!glfwInit()) { fprintf(stderr, "Failed to initialize glfw.\n"); @@ -244,6 +299,9 @@ rive::Alignment::center, rive::AABB(0, 0, width, height), artboard->bounds()); + + post_mouse_event(artboard, canvas->getTotalMatrix()); + artboard->draw(&renderer); renderer.restore(); } @@ -334,6 +392,8 @@ ImGui::Columns(1); } ImGui::End(); + + test_messages(artboard); } else { ImGui::Text("Drop a .riv file to preview."); } @@ -359,4 +419,4 @@ glfwTerminate(); return 0; -} \ No newline at end of file +}
diff --git a/src/artboard.cpp b/src/artboard.cpp index bdd4614..d9cb7cb 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp
@@ -543,3 +543,60 @@ } return result; } + +void Artboard::postPointerEvent(const PointerEvent& evt) { + if (true) { + static bool gButtonIsDown; + + switch (evt.m_Type) { + case PointerEventType::down: + assert(!gButtonIsDown); + gButtonIsDown = true; + break; + case PointerEventType::up: + assert(gButtonIsDown); + gButtonIsDown = false; + break; + default: break; + } + +#if 0 + const char* typeNames[] = { + "down", "move", "up ", + }; + printf("pointer: %s [%g %g] %s\n", + typeNames[(int)evt.m_Type], + evt.m_Position.x(), evt.m_Position.y(), + gButtonIsDown ? "DOWN" : "UP"); +#endif + } + +#if 0 + // TESTING ONLY + // This is the sort of message that the Artboard would post... + // e.g. if the down AND up were inside the same clickable shape. + if (evt.m_Type == PointerEventType::up) { + Message msg; + msg.m_Str = "ClickEvent"; + this->testing_only_enque_message(msg); + } +#endif +} + +void Artboard::testing_only_enque_message(const Message& msg) { + m_MessageQueue.push(msg); +} + +bool Artboard::hasMessages() const { + return !m_MessageQueue.empty(); +} + +bool Artboard::nextMessage(Message* msg) { + if (m_MessageQueue.empty()) { + return false; + } else { + *msg = m_MessageQueue.front(); + m_MessageQueue.pop(); + return true; + } +}