Browse Source

Add RegisterEventType to Core API. Update readme. Element::DispatchEvent should now be backwards compatible. Prevent default action after calling StopPropagation(),

Michael Ragazzon 6 years ago
parent
commit
0b5d8c7189

+ 8 - 0
Include/Rocket/Core/Core.h

@@ -146,6 +146,14 @@ ROCKETCORE_API int GetNumContexts();
 /// Registers a generic Rocket plugin.
 /// Registers a generic Rocket plugin.
 ROCKETCORE_API void RegisterPlugin(Plugin* plugin);
 ROCKETCORE_API void RegisterPlugin(Plugin* plugin);
 
 
+/// Registers a new event type. If the type already exists, it will replace custom event types, but not internal types.
+/// @param[in] type The new event type.
+/// @param[in] interruptible Whether the event can be interrupted during dispatch.
+/// @param[in] bubbles Whether the event executes the bubble phase. If false, only capture and target phase is executed.
+/// @param[in] default_action_phase Defines during which phase(s) the 'Element::ProcessDefaultAction' method is called.
+/// @return The EventId of the newly created type, or existing type if 'type' is an internal type.
+ROCKETCORE_API EventId RegisterEventType(const String& type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase = DefaultActionPhase::None);
+
 /// Forces all compiled geometry handles generated by libRocket to be released.
 /// Forces all compiled geometry handles generated by libRocket to be released.
 ROCKETCORE_API void ReleaseCompiledGeometries();
 ROCKETCORE_API void ReleaseCompiledGeometries();
 /// Forces all texture handles loaded and generated by libRocket to be released.
 /// Forces all texture handles loaded and generated by libRocket to be released.

+ 1 - 1
Include/Rocket/Core/Element.h

@@ -490,7 +490,7 @@ public:
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	bool DispatchEvent(const String& type, const Dictionary& parameters);
 	bool DispatchEvent(const String& type, const Dictionary& parameters);
 	/// Sends an event to this element, overriding the default behavior for the given event type.
 	/// Sends an event to this element, overriding the default behavior for the given event type.
-	bool DispatchEvent(const String& type, const Dictionary& parameters, bool interruptible, bool bubbles);
+	bool DispatchEvent(const String& type, const Dictionary& parameters, bool interruptible, bool bubbles = true);
 	/// Sends an event to this element by event id.
 	/// Sends an event to this element by event id.
 	bool DispatchEvent(EventId id, const Dictionary& parameters);
 	bool DispatchEvent(EventId id, const Dictionary& parameters);
 
 

+ 6 - 0
Source/Core/Core.cpp

@@ -34,6 +34,7 @@
 #include "StyleSheetFactory.h"
 #include "StyleSheetFactory.h"
 #include "TemplateCache.h"
 #include "TemplateCache.h"
 #include "TextureDatabase.h"
 #include "TextureDatabase.h"
+#include "EventSpecification.h"
 
 
 namespace Rocket {
 namespace Rocket {
 namespace Core {
 namespace Core {
@@ -314,6 +315,11 @@ void RegisterPlugin(Plugin* plugin)
 	PluginRegistry::RegisterPlugin(plugin);
 	PluginRegistry::RegisterPlugin(plugin);
 }
 }
 
 
+EventId RegisterEventType(const String& type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+{
+	return EventSpecificationInterface::InsertOrReplaceCustom(type, interruptible, bubbles, default_action_phase);
+}
+
 // Forces all compiled geometry handles generated by libRocket to be released.
 // Forces all compiled geometry handles generated by libRocket to be released.
 void ReleaseCompiledGeometries()
 void ReleaseCompiledGeometries()
 {
 {

+ 2 - 6
Source/Core/EventDispatcher.cpp

@@ -119,10 +119,6 @@ bool EventDispatcher::DispatchEvent(Element* target_element, EventId id, const S
 	if (!event)
 	if (!event)
 		return false;
 		return false;
 
 
-	//// Click events may be disabled on the element.
-	//if (id == EventId::Click && target_element->IsDisabled())
-	//	return false;
-
 	// Build the element traversal from the tree
 	// Build the element traversal from the tree
 	typedef std::vector<Element*> Elements;
 	typedef std::vector<Element*> Elements;
 	Elements elements;
 	Elements elements;
@@ -152,10 +148,10 @@ bool EventDispatcher::DispatchEvent(Element* target_element, EventId id, const S
 		TriggerEvents(event, default_action_phase);
 		TriggerEvents(event, default_action_phase);
 	}
 	}
 
 
+	// Bubble phase - target to root (normal event bindings)
 	if (bubbles && event->IsPropagating())
 	if (bubbles && event->IsPropagating())
 	{
 	{
 		event->SetPhase(EventPhase::Bubble);
 		event->SetPhase(EventPhase::Bubble);
-		// Bubble phase - target to root (normal event bindings)
 		for (size_t i = 0; i < elements.size() && event->IsPropagating(); i++) 
 		for (size_t i = 0; i < elements.size() && event->IsPropagating(); i++) 
 		{
 		{
 			EventDispatcher* dispatcher = elements[i]->GetEventDispatcher();
 			EventDispatcher* dispatcher = elements[i]->GetEventDispatcher();
@@ -208,7 +204,7 @@ void EventDispatcher::TriggerEvents(Event* event, DefaultActionPhase default_act
 
 
 	const bool do_default_action = ((int)phase & (int)default_action_phase);
 	const bool do_default_action = ((int)phase & (int)default_action_phase);
 
 
-	if (do_default_action)
+	if (do_default_action && event->IsPropagating())
 	{
 	{
 		element->ProcessDefaultAction(*event);
 		element->ProcessDefaultAction(*event);
 	}
 	}

+ 39 - 15
Source/Core/EventSpecification.cpp

@@ -75,8 +75,8 @@ void Initialize()
 		{EventId::Handledrag    , "handledrag"    , false , true  , DefaultActionPhase::None},
 		{EventId::Handledrag    , "handledrag"    , false , true  , DefaultActionPhase::None},
 		{EventId::Resize        , "resize"        , false , false , DefaultActionPhase::None},
 		{EventId::Resize        , "resize"        , false , false , DefaultActionPhase::None},
 		{EventId::Scroll        , "scroll"        , false , true  , DefaultActionPhase::None},
 		{EventId::Scroll        , "scroll"        , false , true  , DefaultActionPhase::None},
-		{EventId::Animationend  , "animationend"  , true  , true  , DefaultActionPhase::None},
-		{EventId::Transitionend , "transitionend" , true  , true  , DefaultActionPhase::None},
+		{EventId::Animationend  , "animationend"  , false , true  , DefaultActionPhase::None},
+		{EventId::Transitionend , "transitionend" , false , true  , DefaultActionPhase::None},
 								 				 
 								 				 
 		{EventId::Change        , "change"        , false , true  , DefaultActionPhase::None},
 		{EventId::Change        , "change"        , false , true  , DefaultActionPhase::None},
 		{EventId::Submit        , "submit"        , true  , true  , DefaultActionPhase::None},
 		{EventId::Submit        , "submit"        , true  , true  , DefaultActionPhase::None},
@@ -105,7 +105,7 @@ void Initialize()
 #endif
 #endif
 }
 }
 
 
-const EventSpecification& Get(EventId id)
+static EventSpecification& GetMutable(EventId id)
 {
 {
 	size_t i = static_cast<size_t>(id);
 	size_t i = static_cast<size_t>(id);
 	if (i < specifications.size())
 	if (i < specifications.size())
@@ -113,22 +113,14 @@ const EventSpecification& Get(EventId id)
 	return specifications[0];
 	return specifications[0];
 }
 }
 
 
-const EventSpecification& GetOrInsert(const String& event_type)
-{
-	// Default values for new event types defined as follows:
-	constexpr bool interruptible = true;
-	constexpr bool bubbles = true;
-	constexpr DefaultActionPhase default_action_phase = DefaultActionPhase::None;
-
-	return GetOrInsert(event_type, interruptible, bubbles, default_action_phase);
-}
-
-const EventSpecification& GetOrInsert(const String& event_type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+// Get event specification for the given type.
+// If not found: Inserts a new entry with given values.
+static EventSpecification& GetOrInsert(const String& event_type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
 {
 {
 	auto it = type_lookup.find(event_type);
 	auto it = type_lookup.find(event_type);
 
 
 	if (it != type_lookup.end())
 	if (it != type_lookup.end())
-		return Get(it->second);
+		return GetMutable(it->second);
 
 
 	// No specification found for this name, insert a new entry with default values
 	// No specification found for this name, insert a new entry with default values
 	EventId new_id = static_cast<EventId>(specifications.size());
 	EventId new_id = static_cast<EventId>(specifications.size());
@@ -137,6 +129,21 @@ const EventSpecification& GetOrInsert(const String& event_type, bool interruptib
 	return specifications.back();
 	return specifications.back();
 }
 }
 
 
+const EventSpecification& Get(EventId id)
+{
+	return GetMutable(id);
+}
+
+const EventSpecification& GetOrInsert(const String& event_type)
+{
+	// Default values for new event types defined as follows:
+	constexpr bool interruptible = true;
+	constexpr bool bubbles = true;
+	constexpr DefaultActionPhase default_action_phase = DefaultActionPhase::None;
+
+	return GetOrInsert(event_type, interruptible, bubbles, default_action_phase);
+}
+
 EventId GetIdOrInsert(const String& event_type)
 EventId GetIdOrInsert(const String& event_type)
 {
 {
 	auto it = type_lookup.find(event_type);
 	auto it = type_lookup.find(event_type);
@@ -146,6 +153,23 @@ EventId GetIdOrInsert(const String& event_type)
 	return GetOrInsert(event_type).id;
 	return GetOrInsert(event_type).id;
 }
 }
 
 
+EventId InsertOrReplaceCustom(const String& event_type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+{
+	const size_t size_before = specifications.size();
+	EventSpecification& specification = GetOrInsert(event_type, interruptible, bubbles, default_action_phase);
+	bool got_existing_entry = (size_before == specifications.size());
+
+	// If we found an existing entry of same type, replace it, but only if it is a custom event id.
+	if (got_existing_entry && (int)specification.id >= (int)EventId::FirstCustomId)
+	{
+		specification.interruptible = interruptible;
+		specification.bubbles = bubbles;
+		specification.default_action_phase = default_action_phase;
+	}
+
+	return specification.id;
+}
+
 
 
 }
 }
 }
 }

+ 3 - 6
Source/Core/EventSpecification.h

@@ -52,10 +52,6 @@ namespace EventSpecificationInterface {
 	// Returns the 'invalid' event type if no specification exists for id.
 	// Returns the 'invalid' event type if no specification exists for id.
 	const EventSpecification& Get(EventId id);
 	const EventSpecification& Get(EventId id);
 
 
-	// Get event specification for the given type.
-	// If not found: Inserts a new entry with given values.
-	const EventSpecification& GetOrInsert(const String& event_type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase);
-
 	// Get event specification for the given type.
 	// Get event specification for the given type.
 	// If not found: Inserts a new entry with default values.
 	// If not found: Inserts a new entry with default values.
 	const EventSpecification& GetOrInsert(const String& event_type);
 	const EventSpecification& GetOrInsert(const String& event_type);
@@ -64,10 +60,11 @@ namespace EventSpecificationInterface {
 	// If not found: Inserts a new entry with default values.
 	// If not found: Inserts a new entry with default values.
 	EventId GetIdOrInsert(const String& event_type);
 	EventId GetIdOrInsert(const String& event_type);
 
 
+	// Insert a new specification for the given event_type.
+	// If the type already exists, it will be replaced if and only if the event type is not an internal type.
+	EventId InsertOrReplaceCustom(const String& event_type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase);
 }
 }
 
 
-
-
 }
 }
 }
 }
 
 

+ 19 - 18
readme.md

@@ -48,45 +48,46 @@ If upgrading from the original libRocket branch, some breaking changes should be
 
 
 ### Events
 ### Events
 
 
-Events have some differences, however the existing API should mostly still work.
+There are some changes to events in RmlUi, however, for most users, existing code should still work as before.
 
 
 There is now a distinction between actions executed in event listeners, and default actions for events:
 There is now a distinction between actions executed in event listeners, and default actions for events:
 
 
-- Event listeners are attached to an element as before. Events following the normal phases: capture (root -> target), target, and bubble (target -> root). Each event listener can be either attached to the bubble phase (default) or capture phase. The target element always fire if reached. Listeners are executed in the order they are added to the element. Each event type specifies whether it executes the bubble phase or not, see below for details.
-- Default actions are primarily for actions performed internally in the library. They are executed in the function `virtual void Element::ProcessDefaultAction(Event& event)`. However, any object that derives from `Element` can override the default behavior and add new behavior. The default actions follow the normal event phases, but are only executed in the phase according to the `default_action_phase` which is defined for each event type. If an event is cancelled with `Event::StopPropagation()`, then the default action is not performed unless already executed.
+- Event listeners are attached to an element as before. Events follow the normal phases: capture (root -> target), target, and bubble (target -> root). Each event listener can be either attached to the bubble phase (default) or capture phase. The target phase always executes if reached. Listeners are executed in the order they are added to the element. Each event type specifies whether it executes the bubble phase or not, see below for details.
+- Default actions are primarily for actions performed internally in the library. They are executed in the function `virtual void Element::ProcessDefaultAction(Event& event)`. However, any object that derives from `Element` can override the default behavior and add new behavior. The default actions follow the normal event phases, but are only executed in the phase according to their `default_action_phase` which is defined for each event type. If an event is cancelled with `Event::StopPropagation()`, then the default action is not performed unless already executed.
 
 
 
 
-Each event type now has an associated EventId. Each event type has the following specifications:
+Each event type now has an associated EventId as well as a specification defined as follows:
 
 
 - `interruptible`: Whether the event can be cancelled by calling `Event::StopPropagation()`.
 - `interruptible`: Whether the event can be cancelled by calling `Event::StopPropagation()`.
-- `bubbles`: Whether the event executes the bubble phase. If true, all three phases, capture, target, and bubble, are executed. If false, only capture and target phases are executed.
-- `default_action_phase`: One of: None, Target, Bubble, BubbleAndTarget. Specifies during which phases the default action is executed, if any. That is, the phase for which `Element::ProcessDefaultAction` is called. See above for details.
+- `bubbles`: Whether the event executes the bubble phase. If true, all three phases: capture, target, and bubble, are executed. If false, only capture and target phases are executed.
+- `default_action_phase`: One of: None, Target, Bubble, TargetAndBubble. Specifies during which phases the default action is executed, if any. That is, the phase for which `Element::ProcessDefaultAction` is called. See above for details.
 
 
 For example, the event type `click` has the following specification:
 For example, the event type `click` has the following specification:
 ```
 ```
 id: EventId::Click
 id: EventId::Click
-name: "click"
-interruptable: true
+type: "click"
+interruptible: true
 bubbles: true
 bubbles: true
-default_action_phase: BubbleAndTarget
+default_action_phase: TargetAndBubble
 ```
 ```
 
 
 Whenever an event listener is added or event is dispatched, and the provided event type does not already have a specification, the default specification
 Whenever an event listener is added or event is dispatched, and the provided event type does not already have a specification, the default specification
-`interruptible: true, bubbles: true, default_action_phase: None` is added for that event type. The default specification can be overriden by first calling `EventId Rocket::Core::RegisterEventType(const String& name, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)`. This is only necessary once for each application run, and should be performed before listeners of the same type are used.
-
-Other changes:
-
-- All event listeners on the current element will always be called after calling StopPropagation(). Only when propagating to the next element, the event is stopped. This is the same behavior as in Javascript.
-
-Breaking change:
+`interruptible: true, bubbles: true, default_action_phase: None` is added for that event type. To provide a custom specification for a new event, first call the method:
+```
+EventId Rocket::Core::RegisterEventType(const String& type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+```
+After this call, any usage of this type will use the provided specification by default. The returned EventId can be used to dispatch events instead of the type string.
 
 
-- `EventInstancer::InstanceEvent` now takes an `EventId` instead of a `String`.
+Various changes:
+- All event listeners on the current element will always be called after calling StopPropagation(). However, the default action on the current element will be prevented. When propagating to the next element, the event is stopped. This behavior is consistent with the standard DOM events model.
+- `Element::DispatchEvent` can now optionally take an `EventId` instead of a `String`.
+- The `resize` event now only applies to the document size, not individual elements.
+- The `scrollchange` event has been replaced by a function call. To capture scroll changes, instead use the `scroll` event.
 
 
 ## Other changes
 ## Other changes
 
 
 - `Context::ProcessMouseWheel` now takes a float value for the `wheel_delta` property, thereby enabling continuous/smooth scrolling for input devices with such support. The default scroll length for unity value of `wheel_delta` is now three times the default line-height multiplied by the current dp-ratio.
 - `Context::ProcessMouseWheel` now takes a float value for the `wheel_delta` property, thereby enabling continuous/smooth scrolling for input devices with such support. The default scroll length for unity value of `wheel_delta` is now three times the default line-height multiplied by the current dp-ratio.
 
 
-
 ## Performance
 ## Performance
 
 
 Users moving to this fork should generally see a substantial performance increase. 
 Users moving to this fork should generally see a substantial performance increase.