diff options
Diffstat (limited to 'src/core/dom')
-rw-r--r-- | src/core/dom/Node.cpp | 58 | ||||
-rw-r--r-- | src/core/dom/Node.hpp | 233 |
2 files changed, 286 insertions, 5 deletions
diff --git a/src/core/dom/Node.cpp b/src/core/dom/Node.cpp index a65fc7f..c9651fb 100644 --- a/src/core/dom/Node.cpp +++ b/src/core/dom/Node.cpp @@ -23,6 +23,16 @@ namespace dom { /* Class Node */ +void Node::setName(std::string name) +{ + // Call the name change event + NameChangeEvent ev{this->name, name}; + triggerEvent(ev); + + // Set the new name + this->name = std::move(name); +} + void Node::path(std::vector<std::string> &p) const { if (!isRoot()) { @@ -46,9 +56,9 @@ void Node::doResolve(std::vector<Rooted<Node>> &res, } int Node::resolve(std::vector<Rooted<Node>> &res, - const std::vector<std::string> &path, Filter filter, - void *filterData, unsigned idx, VisitorSet &visited, - const std::string *alias) + const std::vector<std::string> &path, Filter filter, + void *filterData, unsigned idx, VisitorSet &visited, + const std::string *alias) { // Abort if this node was already visited for this path index std::pair<const Node *, int> recKey = std::make_pair(this, idx); @@ -88,6 +98,48 @@ std::vector<Rooted<Node>> Node::resolve(const std::vector<std::string> &path, return res; } +int Node::registerEventHandler(EventType type, EventHandler handler, + Handle<Managed> owner, + bool includeChildren) +{ + const int id = handlerIdCounter++; + handlers.insert(std::make_pair( + type, + EventHandlerDescriptor{id, handler, owner, this, includeChildren})); + return id; +} + +bool Node::unregisterEventHandler(int id) { + for (auto it = handlers.begin(); it != handlers.end(); it++) { + if (it->second.id == id) { + handlers.erase(it); + return true; + } + } + return false; +} + +bool Node::triggerEvent(Event &event, bool fromChild) { + bool res = false; + // Iterate over all event handlers + const auto range = handlers.equal_range(event.type); + for (auto it = range.first; it != range.second; it++) { + // Fetch a reference to the descriptor, check whether it should be + // called for bubbled events + EventHandlerDescriptor descr = it->second; + if (!fromChild || descr.includeChildren) { + descr.handler(event, descr.owner); + res = true; + } + } + + // If possible, let the event bubble up to the parent node + if (event.canBubble() && !parent.isNull()) { + res = parent->triggerEvent(event, true) | res; + } + return res; +} + } } diff --git a/src/core/dom/Node.hpp b/src/core/dom/Node.hpp index 98a51d9..249d1f2 100644 --- a/src/core/dom/Node.hpp +++ b/src/core/dom/Node.hpp @@ -19,9 +19,10 @@ #ifndef _OUSIA_DOM_NODE_HPP_ #define _OUSIA_DOM_NODE_HPP_ +#include <functional> +#include <map> #include <string> #include <vector> -#include <functional> #include <unordered_set> #include <core/Managed.hpp> @@ -29,6 +30,189 @@ namespace ousia { namespace dom { +/* Forward declarations */ +class Node; +class Event; + +/** + * EventType is an enum containing all possible node events. New event types + * should be added here. + */ +enum class EventType : int { + /** + * Generic update event which may be triggered if some important property + * of the node is changed. + */ + UPDATE, + + /** + * The NAME_CHANGE event is used to inform listeners that the name of the + * node has changed. + */ + NAME_CHANGE, + + /** + * The ADD_CHILD event is used to inform listeners that the node got a new + * child in any of its child node lists. + */ + ADD_CHILD, + + /** + * The DELETE_CHILD event is used to inform listeners that the node got a + * new child in any of its child node lists. + */ + DELETE_CHILD +}; + +/** + * Definition of the EventHandler function. + * + * @param event is a reference to the object holding the event data. + * @param owner is a reference to the managed object that was given in the + * registerEventHandler function. + */ +using EventHandler = void (*)(const Event &event, Handle<Managed> owner); + +/** + * The Event class and its child classes are responsible for containing the + * actual event data which further describes the event to the event handlers. + * Instances of this class and its children must be declared on the stack or as + * a temporary. + */ +class Event { +private: + /** + * True as long as the event can bubble up the node hirarchy. + */ + mutable bool bubble; + +public: + /** + * Contains the actual event type of this class. + */ + const EventType type; + + /** + * Node on which the event was triggered. + */ + Rooted<Node> sender; + + /** + * Constructor of the Event class. + * + * @param type is an element from the EventType enum. + * @param bubble if set to true, the event can bubble up the node hirarchy. + */ + Event(EventType type, bool bubble = true) : bubble(bubble), type(type){}; + + /** + * Delete the copy constructor. + */ + Event(const Event &) = delete; + + /** + * Delete the assignment operator. + */ + Event &operator=(const Event &) = delete; + + /** + * Stops the propagation of this event to the parent element. + */ + void stopPropagation() const { bubble = false; } + + /** + * Returns true if the event can still bubble. + */ + bool canBubble() const { return bubble; } +}; + +/** + * Event used when the name of a node has changed. + */ +class NameChangeEvent : public Event { +public: + /** + * Reference to a string containing the old name of the node. + */ + const std::string &oldName; + + /** + * Reference to a string containing the new name of the node. + */ + const std::string &newName; + + /** + * Constructor of the NameChangeEvent class. + * + * @param oldName is a reference to a string containing the old name of the + * node. + * @param newName is a reference to a string containing the new name of the + * node. + * @param bubble if set to true, the event can bubble up the node hirarchy. + */ + NameChangeEvent(const std::string &oldName, const std::string &newName, + bool bubble = true) + : Event(EventType::NAME_CHANGE, bubble), + oldName(oldName), + newName(newName) + { + } +}; + +/** + * Struct containing the data which describes a single registered event handler. + * Note that the event type (e.g. which type of event this element was + * registered for) is stored outside the EventHandlerDescriptor (in the map + * storing the registered event handlers). + */ +struct EventHandlerDescriptor { + /** + * Unique id of the event handler. + */ + const int id; + + /** + * Reference to the event handler containing the events. + */ + const EventHandler handler; + + /** + * Reference to the managed element which owns the event handler. The object + * which owns the Owned handler is given in the constructor. + */ + const Owned<Managed> owner; + + /** + * Set to true, if this event handler listens to bubbled events comming from + * child nodes. + */ + const bool includeChildren; + + /** + * Constructor of the EventHandlerDescriptor struct. + * + * @param id is the node-unique id of the EventHandlerDescriptor. + * @param handler is the function pointer which is going to be called once + * the associated event handler has fired. + * @param owner is a user-specified object which owns the method that is + * going to be called. This can be used to make sure that the method which + * handles the events has access to its owned object as long as the event + * handler lives. + * @param parent is the parent element this descriptor belongs to. The + * a handle to the "owner" object will be created on behalf of the parent. + * @param includeChildren is set to true if the event handler should handle + * events comming from child elements. + */ + EventHandlerDescriptor(int id, EventHandler handler, Handle<Managed> owner, + Managed *parent, bool includeChildren) + : id(id), + handler(handler), + owner(owner, parent), + includeChildren(includeChildren) + { + } +}; + /** * The Node class builds the base class for any Node within the DOM graph. A * node may either be a descriptive node (such as a domain description etc.) @@ -85,6 +269,17 @@ private: Owned<Node> parent; /** + * Current id counter. The id counter may be used to create ids which are + * unique inside the realm of this manager instance. + */ + int handlerIdCounter = 0; + + /** + * Multimap containing all registered event handlers for this node. + */ + std::multimap<EventType, EventHandlerDescriptor> handlers; + + /** * Private version of the "path" function used to construct the path. Calls * the path function of the parent node and adds the own name to the given * vector. @@ -149,7 +344,7 @@ public: * * @param name is the name that should be assigned to the node. */ - void setName(std::string name) { this->name = std::move(name); } + void setName(std::string name); /** * Returns the name of the node. @@ -281,6 +476,40 @@ public: { return resolve(std::vector<std::string>{name}, nullptr, nullptr); } + + /** + * Registers a new event handler for listening to the given event type. + * + * @param type is the event type the handler should listen to. + * @param handler is the handler that should be called. + * @param owner is an object the handler belongs to. May be nullptr. + * @param includeChildren if set to true, the event handler is also called + * if the same event is triggered on one of the child nodes. + * @return a unique event handler. + */ + int registerEventHandler(EventType type, EventHandler handler, + Handle<Managed> owner = nullptr, + bool includeChildren = false); + + /** + * Unregisters the given event handler from the node. Note that removing an + * event handler has linear time. + * + * @param id is the unique event handler id. + * @return true if the given event handler was successfully unregistered. + */ + bool unregisterEventHandler(int id); + + /** + * Triggers an event on this node. + * + * @param event is a pointer at the event that should be triggered. The + * calling function has ownership over the given event. + * @param fromChild is set to true if the triggerEvent function is called + * from a child node. + * @return true if any event handler was found. + */ + bool triggerEvent(Event &event, bool fromChild = false); }; } } |