Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ CTestTestfile.cmake
_deps/

# IDEs and Editors
.clangd
.vscode/
.idea/
*.swp
Expand Down
38 changes: 38 additions & 0 deletions include/events/Delegate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef DELEGATE_HPP
#define DELEGATE_HPP

#include <algorithm>
#include <vector>

#include "events/EventListener.hpp"

struct Context;

class Delegate {
public:
Delegate() = default;
~Delegate() = default;

Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
Delegate(Delegate&&) = delete;
Delegate& operator=(Delegate&&) = delete;

void subscribe(EventListener* listener) { listeners.push_back(listener); }

void unsubscribe(EventListener* listener) {
listeners.erase(std::remove(listeners.begin(), listeners.end(), listener), listeners.end());
}

void invoke(const Context& ctx) {
for (auto* listener : listeners) {
listener->onEventTriggered(ctx);
}
}

private:
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
std::vector<EventListener*> listeners;
};

#endif // DELEGATE_HPP
19 changes: 19 additions & 0 deletions include/events/EventListener.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef EVENTLISTENER_HPP
#define EVENTLISTENER_HPP

struct Context;

class EventListener {
public:
EventListener() = default;
virtual ~EventListener() = default;

EventListener(const EventListener&) = delete;
EventListener& operator=(const EventListener&) = delete;
EventListener(EventListener&&) = delete;
EventListener& operator=(EventListener&&) = delete;

virtual void onEventTriggered(const Context& ctx) = 0;
};

#endif // EVENTLISTENER_HPP
24 changes: 24 additions & 0 deletions include/events/MarkerEventLink.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef MARKEREVENTLINK_HPP
#define MARKEREVENTLINK_HPP

#include <string>
#include <utility>

#include "data_structures/Context.hpp"
#include "events/EventListener.hpp"

class MarkerEventLink : public EventListener {
public:
explicit MarkerEventLink(std::string name) : eventName(std::move(name)) {}

void onEventTriggered(const Context& ctx) override {
if (ctx.markers != nullptr) {
ctx.markers->push_back(eventName);
}
}

private:
std::string eventName;
};

#endif // MARKEREVENTLINK_HPP
5 changes: 5 additions & 0 deletions include/scene/Scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#include <memory>
#include <string>
#include <string_view>
#include <vector>

class Delegate;
class SceneObject;
class SDL_Renderer;
struct Context;
Expand All @@ -24,6 +26,9 @@ class Scene {

void update(const Context& ctx);
void render(SDL_Renderer* renderer);

void onSceneReady();
Delegate& resolveEvents(std::string_view objectName, std::string_view eventName);
};

#endif // SCENE_HPP
9 changes: 9 additions & 0 deletions include/scene/components/Component.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
#define COMPONENT_HPP

#include <memory>
#include <string_view>

class Delegate;
class Scene;
class SceneObject;
class SDL_Renderer;
struct Context;
Expand All @@ -21,6 +24,12 @@ class Component {
virtual void update(const Context& context) = 0;
virtual void render(SDL_Renderer* renderer) = 0;

virtual void onSceneReady(Scene& scene) { (void)scene; }
virtual Delegate* getEvent(std::string_view eventName) {
(void)eventName;
return nullptr;
}

protected:
// NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
std::weak_ptr<SceneObject> owner;
Expand Down
52 changes: 52 additions & 0 deletions include/scene/components/MarkerEmitterComponent.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef MARKEREMITTERCOMPONENT_HPP
#define MARKEREMITTERCOMPONENT_HPP

#include <memory>
#include <string>
#include <vector>

#include "events/MarkerEventLink.hpp"
#include "scene/components/Component.hpp"

class Delegate;
class Scene;

namespace NeuronIDE {
class Component;
}

class MarkerEmitterComponent : public Component {
public:
struct Binding {
std::string targetObject;
std::string targetEvent;
std::string markerName;
};

MarkerEmitterComponent(const std::shared_ptr<SceneObject>& owner,
std::vector<Binding> bindings);
~MarkerEmitterComponent() override;

MarkerEmitterComponent(const MarkerEmitterComponent&) = delete;
MarkerEmitterComponent& operator=(const MarkerEmitterComponent&) = delete;
MarkerEmitterComponent(MarkerEmitterComponent&&) = delete;
MarkerEmitterComponent& operator=(MarkerEmitterComponent&&) = delete;

void update(const Context& context) override;
void render(SDL_Renderer* renderer) override;
void onSceneReady(Scene& scene) override;

static std::unique_ptr<Component> createMarkerEmitter(
const NeuronIDE::Component& protoComp, const std::shared_ptr<SceneObject>& owner);

private:
struct ActiveSubscription {
std::unique_ptr<MarkerEventLink> link;
Delegate* delegate = nullptr;
};

std::vector<Binding> bindings;
std::vector<ActiveSubscription> activeSubscriptions;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One decision worth talking about is storing "Binding"s and "ActiveSubscription"s separately. ActiveSubscription is a resolved binding, and I think we should think if the types shouldn't be combined, having uninitialized fields that get initialized onSceneReady. The downside of that approach is more complicated logic if any rebinding is to happen. I have chosen this one at first, as the clear separation of a POD type and more complicated resolved type easily mimics the config/runtime separation and allows for rebinding, refreshes etc. later

};

#endif // MARKEREMITTERCOMPONENT_HPP
10 changes: 10 additions & 0 deletions protoFiles/neuronide.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ message ScriptComponent {
string script_path = 1;
}

message MarkerEmitterComponent {
message Binding {
string target_object = 1;
string target_event = 2;
string marker_name = 3;
}
repeated Binding bindings = 1;
}

message Transform {
double x = 1;
double y = 2;
Expand All @@ -35,6 +44,7 @@ message Component {
TextRenderer text = 2;
BlinkComponent blinker = 3;
ScriptComponent script = 4;
MarkerEmitterComponent marker_emitter = 5;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ std::shared_ptr<Scene> Parser::parseStream(std::istream& stream) {
scene->addObject(std::move(obj));
}

scene->onSceneReady();

return scene;
}

Expand Down
1 change: 1 addition & 0 deletions src/scene/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_library(scene OBJECT
Scene.cpp
components/ComponentRegistry.cpp
components/BlinkComponent.cpp
components/MarkerEmitterComponent.cpp
)
target_include_directories(scene PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
target_link_libraries(scene PUBLIC neuronide_proto)
30 changes: 30 additions & 0 deletions src/scene/Scene.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include "scene/Scene.hpp"

#include <stdexcept>

#include "events/Delegate.hpp"
#include "scene/SceneObject.hpp"
#include "scene/components/Component.hpp"

void Scene::update(const Context& ctx) {
for (const auto& obj : objects) {
Expand All @@ -14,4 +18,30 @@ void Scene::render(SDL_Renderer* renderer) {
obj->render(renderer);
}
}
}

void Scene::onSceneReady() {
for (const auto& obj : objects) {
for (const auto& comp : obj->components) {
comp->onSceneReady(*this);
}
}
}

Delegate& Scene::resolveEvents(std::string_view objectName, std::string_view eventName) {
for (const auto& obj : objects) {
if (obj->name != objectName) {
continue;
}
for (const auto& comp : obj->components) {
auto* delegate = comp->getEvent(eventName);
if (delegate != nullptr) {
return *delegate;
}
}
throw std::runtime_error("Scene::resolveEvents: event '" + std::string(eventName) +
"' not found on object '" + std::string(objectName) + "'");
}
throw std::runtime_error("Scene::resolveEvents: object '" + std::string(objectName) +
"' not found");
}
44 changes: 44 additions & 0 deletions src/scene/components/MarkerEmitterComponent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "scene/components/MarkerEmitterComponent.hpp"

#include "events/Delegate.hpp"
#include "neuronide.pb.h"
#include "scene/Scene.hpp"
#include "scene/components/ComponentRegistry.hpp"

MarkerEmitterComponent::MarkerEmitterComponent(const std::shared_ptr<SceneObject>& owner,
std::vector<Binding> bindings)
: Component(owner), bindings(std::move(bindings)) {}

MarkerEmitterComponent::~MarkerEmitterComponent() {
for (auto& sub : activeSubscriptions) {
sub.delegate->unsubscribe(sub.link.get());
}
}

void MarkerEmitterComponent::update(const Context& context) { (void)context; }

void MarkerEmitterComponent::render(SDL_Renderer* renderer) { (void)renderer; }

void MarkerEmitterComponent::onSceneReady(Scene& scene) {
for (const auto& binding : bindings) {
auto& delegate = scene.resolveEvents(binding.targetObject, binding.targetEvent);

auto link = std::make_unique<MarkerEventLink>(binding.markerName);
delegate.subscribe(link.get());

activeSubscriptions.push_back({std::move(link), &delegate});
}
}

std::unique_ptr<Component> MarkerEmitterComponent::createMarkerEmitter(
const NeuronIDE::Component& protoComp, const std::shared_ptr<SceneObject>& owner) {
std::vector<Binding> bindings;
for (const auto& protoBinding : protoComp.marker_emitter().bindings()) {
bindings.push_back({protoBinding.target_object(), protoBinding.target_event(),
protoBinding.marker_name()});
}
return std::make_unique<MarkerEmitterComponent>(owner, std::move(bindings));
}

REGISTER_COMPONENT(NeuronIDE::Component::kMarkerEmitter,
MarkerEmitterComponent::createMarkerEmitter)
Loading
Loading