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;
+ }
+}