浏览代码

Implement EventId and associated EventSpecification

Michael Ragazzon 6 年之前
父节点
当前提交
bcc1ac4f2a

+ 3 - 0
Build/cmake/FileList.cmake

@@ -30,6 +30,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/EventDispatcher.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancerDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventIterators.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/EventSpecification.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectNone.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectNoneInstancer.h
@@ -153,6 +154,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Geometry.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/GeometryUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Header.h
+    ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/ID.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Input.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Log.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Math.h
@@ -247,6 +249,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventListenerInstancer.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/EventSpecification.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Factory.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.cpp

+ 2 - 2
Include/Rocket/Core/Context.h

@@ -45,7 +45,7 @@ class ContextInstancer;
 class ElementDocument;
 class EventListener;
 class RenderInterface;
-enum class DefaultActionPhase;
+enum class EventId : uint16_t;
 
 /**
 	A context for storing, rendering and processing RML documents. Multiple contexts can exist simultaneously.
@@ -327,7 +327,7 @@ private:
 	void ReleaseUnloadedDocuments();
 
 	// Sends the specified event to all elements in new_items that don't appear in old_items.
-	static void SendEvents(const ElementSet& old_items, const ElementSet& new_items, const String& event, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase);
+	static void SendEvents(const ElementSet& old_items, const ElementSet& new_items, EventId id, const Dictionary& parameters);
 
 	friend class Element;
 	friend ROCKETCORE_API Context* CreateContext(const String&, const Vector2i&, RenderInterface*);

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

@@ -58,6 +58,7 @@
 #include "FontGlyph.h"
 #include "Geometry.h"
 #include "GeometryUtilities.h"
+#include "ID.h"
 #include "Input.h"
 #include "Log.h"
 #include "Plugin.h"

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

@@ -493,7 +493,8 @@ public:
 	/// @param[in] parameters The event parameters.
 	/// @param[in] interruptible True if the propagation of the event be stopped.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
-	bool DispatchEvent(const String& event, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase);
+	bool DispatchEvent(const String& event, const Dictionary& parameters);
+	bool DispatchEvent(EventId event_id, const Dictionary& parameters);
 
 	/// Scrolls the parent element's contents so that this element is visible.
 	/// @param[in] align_with_top If true, the element will align itself to the top of the parent element's window. If false, the element will be aligned to the bottom of the parent element's window.

+ 12 - 6
Include/Rocket/Core/Event.h

@@ -28,15 +28,17 @@
 #ifndef ROCKETCOREEVENT_H
 #define ROCKETCOREEVENT_H
 
+#include "Header.h"
 #include "Dictionary.h"
 #include "ScriptInterface.h"
-#include "Header.h"
+#include "ID.h"
 
 namespace Rocket {
 namespace Core {
 
 class Element;
 class EventInstancer;
+struct EventSpecification;
 
 enum class EventPhase { None, Capture = 1, Target = 2, Bubble = 4 };
 enum class DefaultActionPhase { None, Target = (int)EventPhase::Target, Bubble = (int)EventPhase::Bubble, TargetAndBubble = ((int)Target | (int)Bubble) };
@@ -58,7 +60,7 @@ public:
 	/// @param[in] type The event type
 	/// @param[in] parameters The event parameters
 	/// @param[in] interruptible Can this event have is propagation stopped?
-	Event(Element* target, const String& type, const Dictionary& parameters, bool interruptible = false);
+	Event(Element* target, EventId id, const Dictionary& parameters);
 	/// Destructor
 	virtual ~Event();
 
@@ -108,7 +110,12 @@ public:
 	const Dictionary* GetParameters() const;
 
 	/// Release this event.
-	virtual void OnReferenceDeactivate();
+	virtual void OnReferenceDeactivate() override;
+
+
+	EventId GetId() const;
+	DefaultActionPhase GetDefaultActionPhase() const;
+	bool GetBubbles() const;
 
 private:
 	/// Project the mouse coordinates to the current element to enable
@@ -116,7 +123,6 @@ private:
 	void ProjectMouse(Element* element);
 
 protected:
-	String type;
 	Dictionary parameters;
 
 	Element* target_element;
@@ -125,8 +131,8 @@ protected:
 private:
 	Dictionary parameters_backup;
 
-	bool interruptible;
-	bool interruped;
+	const EventSpecification& specification;
+	bool interrupted;
 
 	EventPhase phase;
 

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

@@ -53,7 +53,7 @@ public:
 	/// @param[in] name Name of this event.
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] interruptible If the event propagation can be stopped.
-	virtual Event* InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible) = 0;
+	virtual Event* InstanceEvent(Element* target, EventId id, const Dictionary& parameters) = 0;
 
 	/// Releases an event instanced by this instancer.
 	/// @param[in] event The event to release.

+ 2 - 2
Include/Rocket/Core/Factory.h

@@ -30,7 +30,6 @@
 
 #include "XMLParser.h"
 #include "Header.h"
-#include <map>
 
 namespace Rocket {
 namespace Core {
@@ -50,6 +49,7 @@ class FontEffect;
 class FontEffectInstancer;
 class StyleSheet;
 class PropertyDictionary;
+enum class EventId : uint16_t;
 
 /**
 	The Factory contains a registry of instancers for different types.
@@ -159,7 +159,7 @@ public:
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] interruptible If the event propagation can be stopped.
 	/// @return The instanced event.
-	static Event* InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible);
+	static Event* InstanceEvent(Element* target, EventId id, const Dictionary& parameters);
 
 	/// Register the instancer to be used for all event listeners.
 	/// @return The registered instancer on success, NULL on failure.

+ 90 - 0
Include/Rocket/Core/ID.h

@@ -0,0 +1,90 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+ 
+#ifndef ROCKETCOREID_H
+#define ROCKETCOREID_H
+
+namespace Rocket {
+namespace Core {
+
+enum class EventId : uint16_t 
+{
+	Invalid,
+
+	// Core events
+	Mousedown,
+	Mousescroll,
+	Mouseover,
+	Mouseout,
+	Focus,
+	Blur,
+	Keydown,
+	Keyup,
+	Textinput,
+	Mouseup,
+	Click,
+	Dblclick,
+	Load,
+	Unload,
+	Show,
+	Hide,
+	Mousemove,
+	Dragmove,
+	Drag,
+	Dragstart,
+	Dragover,
+	Dragdrop,
+	Dragout,
+	Dragend,
+	Handledrag,
+	Resize,
+	Scroll,
+	Scrollchange,
+	Animationend,
+	Transitionend,
+
+	// Controls events
+	Change,
+	Submit,
+	Tabchange,
+	Columnadd,
+	Rowadd,
+	Rowchange,
+	Rowremove,
+	Rowupdate,
+
+	NumDefinedIds,
+
+	// Custom IDs start here
+	FirstCustomId = NumDefinedIds
+};
+
+}
+}
+
+#endif

+ 1 - 1
Samples/luainvaders/src/ElementGame.cpp

@@ -82,7 +82,7 @@ void ElementGame::OnUpdate()
 	game->Update();
 
 	if (game->IsGameOver())
-		DispatchEvent("gameover", Rocket::Core::Dictionary(), false, true, Rocket::Core::DefaultActionPhase::None);
+		DispatchEvent("gameover", Rocket::Core::Dictionary());
 }
 
 // Renders the game.

+ 2 - 2
Source/Controls/ElementDataGrid.cpp

@@ -139,7 +139,7 @@ void ElementDataGrid::AddColumn(const Rocket::Core::String& fields, const Rocket
 
 	Rocket::Core::Dictionary parameters;
 	parameters["index"] = (int)(columns.size() - 1);
-	if (DispatchEvent("columnadd", parameters, false, false, Core::DefaultActionPhase::None))
+	if (DispatchEvent(Core::EventId::Columnadd, parameters))
 	{
 		root->RefreshRows();
 		DirtyLayout();
@@ -236,7 +236,7 @@ void ElementDataGrid::OnUpdate()
 	if (any_new_children)
 	{
 		// @performance: Does anyone really use this?
-		DispatchEvent("rowupdate", Rocket::Core::Dictionary(), false, true, Core::DefaultActionPhase::None);
+		DispatchEvent(Core::EventId::Rowupdate, Rocket::Core::Dictionary());
 	}
 	
 	if (!body_visible && (!any_new_children || root->GetNumLoadedChildren() >= GetAttribute("min-rows", 0)))

+ 3 - 3
Source/Controls/ElementDataGridRow.cpp

@@ -409,7 +409,7 @@ void ElementDataGridRow::AddChildren(int first_row_added, int num_rows_added)
 	parameters["first_row_added"] = GetChildTableRelativeIndex(first_row_added);
 	parameters["num_rows_added"] = num_rows_added;
 	// @performance: Does anyone really use this?
-	parent_grid->DispatchEvent("rowadd", parameters, false, true, Core::DefaultActionPhase::None);
+	parent_grid->DispatchEvent(Core::EventId::Rowadd, parameters);
 }
 
 void ElementDataGridRow::RemoveChildren(int first_row_removed, int num_rows_removed)
@@ -439,7 +439,7 @@ void ElementDataGridRow::RemoveChildren(int first_row_removed, int num_rows_remo
 	parameters["first_row_removed"] = GetChildTableRelativeIndex(first_row_removed);
 	parameters["num_rows_removed"] = num_rows_removed;
 	// @performance: Does anyone really use this?
-	parent_grid->DispatchEvent("rowremove", parameters, false, true, Core::DefaultActionPhase::None);
+	parent_grid->DispatchEvent(Core::EventId::Rowremove, parameters);
 }
 
 void ElementDataGridRow::ChangeChildren(int first_row_changed, int num_rows_changed)
@@ -451,7 +451,7 @@ void ElementDataGridRow::ChangeChildren(int first_row_changed, int num_rows_chan
 	parameters["first_row_changed"] = GetChildTableRelativeIndex(first_row_changed);
 	parameters["num_rows_changed"] = num_rows_changed;
 	// @performance: Does anyone really use this?
-	parent_grid->DispatchEvent("rowchange", parameters, false, true, Core::DefaultActionPhase::None);
+	parent_grid->DispatchEvent(Core::EventId::Rowchange, parameters);
 }
 
 // Returns the number of rows under this row (children, grandchildren, etc)

+ 1 - 1
Source/Controls/ElementForm.cpp

@@ -86,7 +86,7 @@ void ElementForm::Submit(const Rocket::Core::String& name, const Rocket::Core::S
 			values[control_name] = control_value;
 	}
 
-	DispatchEvent("submit", values, true, true, Core::DefaultActionPhase::None);
+	DispatchEvent(Core::EventId::Submit, values);
 }
 
 }

+ 1 - 1
Source/Controls/ElementTabSet.cpp

@@ -132,7 +132,7 @@ void ElementTabSet::SetActiveTab(int tab_index)
 
 		Rocket::Core::Dictionary parameters;
 		parameters["tab_index"] = active_tab;
-		DispatchEvent("tabchange", parameters, false, true, Core::DefaultActionPhase::None);
+		DispatchEvent(Core::EventId::Tabchange, parameters);
 	}
 }
 

+ 1 - 1
Source/Controls/InputTypeCheckbox.cpp

@@ -56,7 +56,7 @@ bool InputTypeCheckbox::OnAttributeChange(const Core::AttributeNameList& changed
 
 		Rocket::Core::Dictionary parameters;
 		parameters["value"] = Rocket::Core::String(checked ? GetValue() : "");
-		element->DispatchEvent("change", parameters, false, true, Core::DefaultActionPhase::None);
+		element->DispatchEvent(Core::EventId::Change, parameters);
 	}
 
 	return true;

+ 1 - 1
Source/Controls/InputTypeRadio.cpp

@@ -63,7 +63,7 @@ bool InputTypeRadio::OnAttributeChange(const Core::AttributeNameList& changed_at
 
 		Rocket::Core::Dictionary parameters;
 		parameters["value"] = Rocket::Core::String(checked ? GetValue() : "");
-		element->DispatchEvent("change", parameters, false, true, Core::DefaultActionPhase::None);
+		element->DispatchEvent(Core::EventId::Change, parameters);
 	}
 
 	return true;

+ 1 - 1
Source/Controls/WidgetDropDown.cpp

@@ -205,7 +205,7 @@ void WidgetDropDown::SetSelection(int selection, bool force)
 
 		Rocket::Core::Dictionary parameters;
 		parameters["value"] = value;
-		parent_element->DispatchEvent("change", parameters, false, true, Core::DefaultActionPhase::None);
+		parent_element->DispatchEvent(Core::EventId::Change, parameters);
 	}
 }
 

+ 1 - 1
Source/Controls/WidgetSlider.cpp

@@ -186,7 +186,7 @@ void WidgetSlider::SetBarPosition(float _bar_position)
 
 	Rocket::Core::Dictionary parameters;
 	parameters["value"] = bar_position;
-	parent->DispatchEvent("change", parameters, false, true, Core::DefaultActionPhase::None);
+	parent->DispatchEvent(Core::EventId::Change, parameters);
 }
 
 // Returns the current position of the bar.

+ 1 - 1
Source/Controls/WidgetTextInput.cpp

@@ -258,7 +258,7 @@ void WidgetTextInput::DispatchChangeEvent(bool linebreak)
 	Rocket::Core::Dictionary parameters;
 	parameters["value"] = GetElement()->GetAttribute< Rocket::Core::String >("value", "");
 	parameters["linebreak"] = Core::Variant(linebreak);
-	GetElement()->DispatchEvent("change", parameters, false, true, Core::DefaultActionPhase::None);
+	GetElement()->DispatchEvent(Core::EventId::Change, parameters);
 }
 
 // Processes the "keydown" and "textinput" event to write to the input field, and the "focus" and "blur" to set

+ 35 - 35
Source/Core/Context.cpp

@@ -118,7 +118,7 @@ void Context::SetDimensions(const Vector2i& _dimensions)
 			{
 				document->DirtyLayout();
 				document->DirtyPosition();
-				document->DispatchEvent(RESIZE, Dictionary(), false, false, DefaultActionPhase::None);
+				document->DispatchEvent(EventId::Resize, Dictionary());
 			}
 		}
 		
@@ -274,7 +274,7 @@ ElementDocument* Context::LoadDocument(Stream* stream)
 	// values and layouting are not performed yet, resulting in default values when
 	// querying such information in the event handler.
 	PluginRegistry::NotifyDocumentLoad(document);
-	document->DispatchEvent(LOAD, Dictionary(), false, false, DefaultActionPhase::None);
+	document->DispatchEvent(EventId::Load, Dictionary());
 
 	document->UpdateDocument();
 
@@ -314,7 +314,7 @@ void Context::UnloadDocument(ElementDocument* _document)
 	if (document->GetParentNode() == root)
 	{
 		// Dispatch the unload notifications.
-		document->DispatchEvent(UNLOAD, Dictionary(), false, false, DefaultActionPhase::None);
+		document->DispatchEvent(EventId::Unload, Dictionary());
 		PluginRegistry::NotifyDocumentUnload(document);
 
 		// Remove the document from the context.
@@ -493,9 +493,9 @@ bool Context::ProcessKeyDown(Input::KeyIdentifier key_identifier, int key_modifi
 	GenerateKeyModifierEventParameters(parameters, key_modifier_state);
 
 	if (focus)
-		return focus->DispatchEvent(KEYDOWN, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return focus->DispatchEvent(EventId::Keydown, parameters);
 	else
-		return root->DispatchEvent(KEYDOWN, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return root->DispatchEvent(EventId::Keydown, parameters);
 }
 
 // Sends a key up event into Rocket.
@@ -507,9 +507,9 @@ bool Context::ProcessKeyUp(Input::KeyIdentifier key_identifier, int key_modifier
 	GenerateKeyModifierEventParameters(parameters, key_modifier_state);
 
 	if (focus)
-		return focus->DispatchEvent(KEYUP, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return focus->DispatchEvent(EventId::Keyup, parameters);
 	else
-		return root->DispatchEvent(KEYUP, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return root->DispatchEvent(EventId::Keyup, parameters);
 }
 
 // Sends a single character of text as text input into Rocket.
@@ -520,9 +520,9 @@ bool Context::ProcessTextInput(word character)
 	parameters["data"] = character;
 
 	if (focus)
-		return focus->DispatchEvent(TEXTINPUT, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return focus->DispatchEvent(EventId::Textinput, parameters);
 	else
-		return root->DispatchEvent(TEXTINPUT, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return root->DispatchEvent(EventId::Textinput, parameters);
 }
 
 // Sends a string of text as text input into Rocket.
@@ -537,9 +537,9 @@ bool Context::ProcessTextInput(const String& string)
 		parameters["data"] = string[i];
 
 		if (focus)
-			consumed = focus->DispatchEvent(TEXTINPUT, parameters, true, true, DefaultActionPhase::TargetAndBubble) && consumed;
+			consumed = focus->DispatchEvent(EventId::Textinput, parameters) && consumed;
 		else
-			consumed = root->DispatchEvent(TEXTINPUT, parameters, true, true, DefaultActionPhase::TargetAndBubble) && consumed;
+			consumed = root->DispatchEvent(EventId::Textinput, parameters) && consumed;
 	}
 
 	return consumed;
@@ -576,11 +576,11 @@ void Context::ProcessMouseMove(int x, int y, int key_modifier_state)
 	{
 		if (hover)
 		{
-			hover->DispatchEvent(MOUSEMOVE, parameters, true, true, DefaultActionPhase::None);
+			hover->DispatchEvent(EventId::Mousemove, parameters);
 
 			if (drag_hover &&
 				drag_verbose)
-				drag_hover->DispatchEvent(DRAGMOVE, drag_parameters, true, true, DefaultActionPhase::Target);
+				drag_hover->DispatchEvent(EventId::Dragmove, drag_parameters);
 		}
 	}
 }
@@ -628,7 +628,7 @@ void Context::ProcessMouseButtonDown(int button_index, int key_modifier_state)
 		
 		// Call 'onmousedown' on every item in the hover chain, and copy the hover chain to the active chain.
 		if (hover)
-			propogate = hover->DispatchEvent(MOUSEDOWN, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+			propogate = hover->DispatchEvent(EventId::Mousedown, parameters);
 
 		if (propogate)
 		{
@@ -639,7 +639,7 @@ void Context::ProcessMouseButtonDown(int button_index, int key_modifier_state)
 				float(click_time - last_click_time) < DOUBLE_CLICK_TIME)
 			{
 				if (hover)
-					propogate = hover->DispatchEvent(DBLCLICK, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+					propogate = hover->DispatchEvent(EventId::Dblclick, parameters);
 
 				last_click_element = NULL;
 				last_click_time = 0;
@@ -678,7 +678,7 @@ void Context::ProcessMouseButtonDown(int button_index, int key_modifier_state)
 	{
 		// Not the primary mouse button, so we're not doing any special processing.
 		if (hover)
-			hover->DispatchEvent(MOUSEDOWN, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+			hover->DispatchEvent(EventId::Mousedown, parameters);
 	}
 }
 
@@ -694,13 +694,13 @@ void Context::ProcessMouseButtonUp(int button_index, int key_modifier_state)
 	{
 		// The elements in the new hover chain have the 'onmouseup' event called on them.
 		if (hover)
-			hover->DispatchEvent(MOUSEUP, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+			hover->DispatchEvent(EventId::Mouseup, parameters);
 
 		// If the active element (the one that was being hovered over when the mouse button was pressed) is still being
 		// hovered over, we click it.
 		if (hover && active && active == FindFocusElement(*hover))
 		{
-			active->DispatchEvent(CLICK, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+			active->DispatchEvent(EventId::Click, parameters);
 		}
 
 		// Unset the 'active' pseudo-class on all the elements in the active chain; because they may not necessarily
@@ -722,12 +722,12 @@ void Context::ProcessMouseButtonUp(int button_index, int key_modifier_state)
 				{
 					if (drag_verbose)
 					{
-						drag_hover->DispatchEvent(DRAGDROP, drag_parameters, true, true, DefaultActionPhase::Target);
-						drag_hover->DispatchEvent(DRAGOUT, drag_parameters, true, true, DefaultActionPhase::Target);
+						drag_hover->DispatchEvent(EventId::Dragdrop, drag_parameters);
+						drag_hover->DispatchEvent(EventId::Dragout, drag_parameters);
 					}
 				}
 
-				drag->DispatchEvent(DRAGEND, drag_parameters, true, true, DefaultActionPhase::None);
+				drag->DispatchEvent(EventId::Dragend, drag_parameters);
 
 				ReleaseDragClone();
 			}
@@ -741,7 +741,7 @@ void Context::ProcessMouseButtonUp(int button_index, int key_modifier_state)
 	{
 		// Not the left mouse button, so we're not doing any special processing.
 		if (hover)
-			hover->DispatchEvent(MOUSEUP, parameters, true, true, DefaultActionPhase::None);
+			hover->DispatchEvent(EventId::Mouseup, parameters);
 	}
 }
 
@@ -754,7 +754,7 @@ bool Context::ProcessMouseWheel(float wheel_delta, int key_modifier_state)
 		GenerateKeyModifierEventParameters(scroll_parameters, key_modifier_state);
 		scroll_parameters["wheel_delta"] = wheel_delta;
 
-		return hover->DispatchEvent(MOUSESCROLL, scroll_parameters, true, true, DefaultActionPhase::TargetAndBubble);
+		return hover->DispatchEvent(EventId::Mousescroll, scroll_parameters);
 	}
 
 	return true;
@@ -840,7 +840,7 @@ void Context::OnElementRemove(Element* element)
 
 	Dictionary parameters;
 	GenerateMouseEventParameters(parameters, -1);
-	SendEvents(old_hover_chain, hover_chain, MOUSEOUT, parameters, true, true, DefaultActionPhase::Target);
+	SendEvents(old_hover_chain, hover_chain, EventId::Mouseout, parameters);
 }
 
 // Internal callback for when a new element gains focus
@@ -876,8 +876,8 @@ bool Context::OnFocusChange(Element* new_focus)
 	Dictionary parameters;
 
 	// Send out blur/focus events.
-	SendEvents(old_chain, new_chain, BLUR, parameters, false, false, DefaultActionPhase::Target);
-	SendEvents(new_chain, old_chain, FOCUS, parameters, false, false, DefaultActionPhase::Target);
+	SendEvents(old_chain, new_chain, EventId::Blur, parameters);
+	SendEvents(new_chain, old_chain, EventId::Focus, parameters);
 
 	focus = new_focus;
 
@@ -911,7 +911,7 @@ void Context::GenerateClickEvent(Element* element)
 	Dictionary parameters;
 	GenerateMouseEventParameters(parameters, 0);
 
-	element->DispatchEvent(CLICK, parameters, true, true, DefaultActionPhase::TargetAndBubble);
+	element->DispatchEvent(EventId::Click, parameters);
 }
 
 // Updates the current hover elements, sending required events.
@@ -929,7 +929,7 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
 				Dictionary drag_start_parameters = drag_parameters;
 				drag_start_parameters["mouse_x"] = old_mouse_position.x;
 				drag_start_parameters["mouse_y"] = old_mouse_position.y;
-				drag->DispatchEvent(DRAGSTART, drag_start_parameters, false, true, DefaultActionPhase::Target);
+				drag->DispatchEvent(EventId::Dragstart, drag_start_parameters);
 				drag_started = true;
 
 				if (drag->GetComputedValues().drag == Style::Drag::Clone)
@@ -939,7 +939,7 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
 				}
 			}
 
-			drag->DispatchEvent(DRAG, drag_parameters, false, true, DefaultActionPhase::Target);
+			drag->DispatchEvent(EventId::Drag, drag_parameters);
 		}
 	}
 
@@ -969,8 +969,8 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
 	}
 
 	// Send mouseout / mouseover events.
-	SendEvents(hover_chain, new_hover_chain, MOUSEOUT, parameters, true, true, DefaultActionPhase::Target);
-	SendEvents(new_hover_chain, hover_chain, MOUSEOVER, parameters, true, true, DefaultActionPhase::Target);
+	SendEvents(hover_chain, new_hover_chain, EventId::Mouseout, parameters);
+	SendEvents(new_hover_chain, hover_chain, EventId::Mouseover, parameters);
 
 	// Send out drag events.
 	if (drag)
@@ -1001,8 +1001,8 @@ void Context::UpdateHoverChain(const Dictionary& parameters, const Dictionary& d
 			drag_verbose)
 		{
 			// Send out ondragover and ondragout events as appropriate.
-			SendEvents(drag_hover_chain, new_drag_hover_chain, DRAGOUT, drag_parameters, true, false, DefaultActionPhase::Target);
-			SendEvents(new_drag_hover_chain, drag_hover_chain, DRAGOVER, drag_parameters, true, false, DefaultActionPhase::Target);
+			SendEvents(drag_hover_chain, new_drag_hover_chain, EventId::Dragout, drag_parameters);
+			SendEvents(new_drag_hover_chain, drag_hover_chain, EventId::Dragover, drag_parameters);
 		}
 
 		drag_hover_chain.swap(new_drag_hover_chain);
@@ -1192,11 +1192,11 @@ void Context::ReleaseUnloadedDocuments()
 }
 
 // Sends the specified event to all elements in new_items that don't appear in old_items.
-void Context::SendEvents(const ElementSet& old_items, const ElementSet& new_items, const String& event, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+void Context::SendEvents(const ElementSet& old_items, const ElementSet& new_items, EventId id, const Dictionary& parameters)
 {
 	ElementList elements;
 	std::set_difference(old_items.begin(), old_items.end(), new_items.begin(), new_items.end(), std::back_inserter(elements));
-	RKTEventFunctor func(event, parameters, interruptible, bubbles, default_action_phase);
+	RKTEventFunctor func(id, parameters);
 	std::for_each(elements.begin(), elements.end(), func);
 }
 

+ 3 - 1
Source/Core/Core.cpp

@@ -27,7 +27,7 @@
 
 #include "precompiled.h"
 #include "../../Include/Rocket/Core.h"
-#include <algorithm>
+#include "EventSpecification.h"
 #include "FileInterfaceDefault.h"
 #include "GeometryDatabase.h"
 #include "PluginRegistry.h"
@@ -96,6 +96,8 @@ bool Initialise()
 
 	Log::Initialise();
 
+	EventSpecificationInterface::Initialize();
+
 	TextureDatabase::Initialise();
 
 	FontDatabase::Initialise();

+ 17 - 7
Source/Core/Element.cpp

@@ -40,6 +40,7 @@
 #include "ElementDefinition.h"
 #include "ElementStyle.h"
 #include "EventDispatcher.h"
+#include "EventSpecification.h"
 #include "ElementDecoration.h"
 #include "FontFaceHandle.h"
 #include "LayoutEngine.h"
@@ -1088,7 +1089,7 @@ void Element::SetScrollLeft(float scroll_left)
 	scroll->UpdateScrollbar(ElementScroll::HORIZONTAL);
 	DirtyOffset();
 
-	DispatchEvent("scroll", Dictionary(), false, true, DefaultActionPhase::None);
+	DispatchEvent(EventId::Scroll, Dictionary());
 }
 
 // Gets the top scroll offset of the element.
@@ -1104,7 +1105,7 @@ void Element::SetScrollTop(float scroll_top)
 	scroll->UpdateScrollbar(ElementScroll::VERTICAL);
 	DirtyOffset();
 
-	DispatchEvent("scroll", Dictionary(), false, true, DefaultActionPhase::None);
+	DispatchEvent(EventId::Scroll, Dictionary());
 }
 
 // Gets the width of the scrollable content of the element; it includes the element padding but not its margin.
@@ -1297,19 +1298,28 @@ void Element::Click()
 // Adds an event listener
 void Element::AddEventListener(const String& event, EventListener* listener, bool in_capture_phase)
 {
-	event_dispatcher->AttachEvent(event, listener, in_capture_phase);
+	EventId id = EventSpecificationInterface::GetIdOrDefineDefault(event);
+	event_dispatcher->AttachEvent(id, listener, in_capture_phase);
 }
 
 // Removes an event listener from this element.
 void Element::RemoveEventListener(const String& event, EventListener* listener, bool in_capture_phase)
 {
-	event_dispatcher->DetachEvent(event, listener, in_capture_phase);
+	EventId id = EventSpecificationInterface::GetIdOrDefineDefault(event);
+	event_dispatcher->DetachEvent(id, listener, in_capture_phase);
 }
 
 // Dispatches the specified event
-bool Element::DispatchEvent(const String& event, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+bool Element::DispatchEvent(const String& event, const Dictionary& parameters)
 {
-	return event_dispatcher->DispatchEvent(this, event, parameters, interruptible, bubbles, default_action_phase);
+	EventId id = EventSpecificationInterface::GetIdOrDefineDefault(event);
+	return event_dispatcher->DispatchEvent(this, id, parameters);
+}
+
+// Dispatches the specified event
+bool Element::DispatchEvent(EventId event_id, const Dictionary& parameters)
+{
+	return event_dispatcher->DispatchEvent(this, event_id, parameters);
 }
 
 // Scrolls the parent element's contents so that this element is visible.
@@ -2465,7 +2475,7 @@ void Element::AdvanceAnimations()
 		animations.erase(it_completed, animations.end());
 
 		for (size_t i = 0; i < dictionary_list.size(); i++)
-			DispatchEvent(is_transition[i] ? TRANSITIONEND : ANIMATIONEND, dictionary_list[i], true, true, DefaultActionPhase::None);
+			DispatchEvent(is_transition[i] ? EventId::Transitionend : EventId::Animationend, dictionary_list[i]);
 	}
 }
 

+ 2 - 2
Source/Core/ElementDocument.cpp

@@ -235,7 +235,7 @@ void ElementDocument::Show(int focus_flags)
 		}
 	}
 
-	DispatchEvent("show", Dictionary(), false, false, DefaultActionPhase::None);
+	DispatchEvent(EventId::Show, Dictionary());
 }
 
 void ElementDocument::Hide()
@@ -245,7 +245,7 @@ void ElementDocument::Hide()
 	// We should update the document now, so that the focusing below will get the correct visibility
 	UpdateDocument();
 
-	DispatchEvent("hide", Dictionary(), false, false, DefaultActionPhase::None);
+	DispatchEvent(EventId::Hide, Dictionary());
 	
 	if (context)
 	{

+ 1 - 1
Source/Core/ElementHandle.cpp

@@ -136,7 +136,7 @@ void ElementHandle::ProcessDefaultAction(Event& event)
 			Dictionary parameters;
 			parameters["handle_x"] = x;
 			parameters["handle_y"] = y;
-			DispatchEvent("handledrag", parameters, false, true, DefaultActionPhase::None);
+			DispatchEvent(EventId::Handledrag, parameters);
 		}
 	}
 }

+ 30 - 14
Source/Core/Event.cpp

@@ -28,24 +28,25 @@
 #include "precompiled.h"
 #include "../../Include/Rocket/Core/Event.h"
 #include "../../Include/Rocket/Core/EventInstancer.h"
+#include "EventSpecification.h"
 
 namespace Rocket {
 namespace Core {
 
-Event::Event()
+Event::Event() : specification(EventSpecificationInterface::Get(EventId::Invalid))
 {
 	phase = EventPhase::None;
-	interruped = false;
-	interruptible = false;
-	current_element = NULL;
-	target_element = NULL;
+	interrupted = false;
+	current_element = nullptr;
+	target_element = nullptr;
 }
 
-Event::Event(Element* _target_element, const String& _type, const Dictionary& _parameters, bool _interruptible) : type(_type), parameters(_parameters), target_element(_target_element), parameters_backup(_parameters), interruptible(_interruptible)
+Event::Event(Element* _target_element, EventId id, const Dictionary& _parameters) 
+	: specification(EventSpecificationInterface::Get(id)), parameters(_parameters), target_element(_target_element), parameters_backup(_parameters)
 {
 	phase = EventPhase::None;
-	interruped = false;
-	current_element = NULL;
+	interrupted = false;
+	current_element = nullptr;
 }
 
 Event::~Event()
@@ -70,12 +71,12 @@ Element* Event::GetTargetElement() const
 
 const String& Event::GetType() const
 {
-	return type;
+	return specification.type;
 }
 
 bool Event::operator==(const String& _type) const
 {
-	return type == _type;
+	return specification.type == _type;
 }
 
 void Event::SetPhase(EventPhase _phase)
@@ -90,15 +91,15 @@ EventPhase Event::GetPhase() const
 
 bool Event::IsPropagating() const
 {
-	return !interruped;
+	return !interrupted;
 }
 
 void Event::StopPropagation()
 {
-	// Set interruped to true if we can be interruped
-	if (interruptible) 
+	// Set interrupted to true if we can be interrupted
+	if (specification.interruptible)
 	{
-		interruped = true;
+		interrupted = true;
 	}
 }
 
@@ -112,6 +113,21 @@ void Event::OnReferenceDeactivate()
 	instancer->ReleaseEvent(this);
 }
 
+EventId Event::GetId() const
+{
+	return specification.id;
+}
+
+DefaultActionPhase Event::GetDefaultActionPhase() const
+{
+	return specification.default_action_phase;
+}
+
+bool Event::GetBubbles() const
+{
+	return specification.bubbles;
+}
+
 void Event::ProjectMouse(Element* element)
 {
 	if (element)

+ 18 - 11
Source/Core/EventDispatcher.cpp

@@ -31,6 +31,7 @@
 #include "../../Include/Rocket/Core/Event.h"
 #include "../../Include/Rocket/Core/EventListener.h"
 #include "../../Include/Rocket/Core/Factory.h"
+#include "EventSpecification.h"
 
 namespace Rocket {
 namespace Core {
@@ -52,15 +53,15 @@ EventDispatcher::~EventDispatcher()
 	}
 }
 
-void EventDispatcher::AttachEvent(const String& type, EventListener* listener, bool in_capture_phase)
+void EventDispatcher::AttachEvent(EventId id, EventListener* listener, bool in_capture_phase)
 {
 	// See if event type exists already
-	Events::iterator event_itr = events.find(type);
+	Events::iterator event_itr = events.find(id);
 
 	if (event_itr == events.end())
 	{
 		// No, add listener to new event type entry
-		event_itr = events.emplace(type, Listeners{ Listener(listener, in_capture_phase) }).first;
+		event_itr = events.emplace(id, Listeners{ Listener(listener, in_capture_phase) }).first;
 	}
 	else
 	{
@@ -71,10 +72,10 @@ void EventDispatcher::AttachEvent(const String& type, EventListener* listener, b
 	listener->OnAttach(element);
 }
 
-void EventDispatcher::DetachEvent(const String& type, EventListener* listener, bool in_capture_phase)
+void EventDispatcher::DetachEvent(EventId id, EventListener* listener, bool in_capture_phase)
 {
 	// Look up the event
-	Events::iterator event_itr = events.find(type);
+	Events::iterator event_itr = events.find(id);
 
 	// Bail if we can't find the event
 	if (event_itr == events.end())
@@ -112,12 +113,16 @@ void EventDispatcher::DetachAllEvents()
 		element->GetChild(i)->GetEventDispatcher()->DetachAllEvents();
 }
 
-bool EventDispatcher::DispatchEvent(Element* target_element, const String& name, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
+bool EventDispatcher::DispatchEvent(Element* target_element, EventId id, const Dictionary& parameters)
 {
-	Event* event = Factory::InstanceEvent(target_element, name, parameters, interruptible);
-	if (event == NULL)
+	Event* event = Factory::InstanceEvent(target_element, id, parameters);
+	if (!event)
 		return false;
 
+	const DefaultActionPhase default_action_phase = event->GetDefaultActionPhase();
+	const bool bubbles = event->GetBubbles();
+
+
 	// Build the element traversal from the tree
 	typedef std::vector<Element*> Elements;
 	Elements elements;
@@ -169,7 +174,8 @@ String EventDispatcher::ToString() const
 	String result;
 	for (auto nvp : events)
 	{
-		result += CreateString(nvp.first.size() + 32, "%s (%d), ", nvp.first.c_str(), static_cast<int>(nvp.second.size()));
+		const EventSpecification& specification = EventSpecificationInterface::Get(nvp.first);
+		result += CreateString(specification.type.size() + 32, "%s (%d), ", specification.type.c_str(), static_cast<int>(nvp.second.size()));
 	}
 	if (result.size() > 2) 
 	{
@@ -181,10 +187,9 @@ String EventDispatcher::ToString() const
 void EventDispatcher::TriggerEvents(Event* event, DefaultActionPhase default_action_phase)
 {
 	const EventPhase phase = event->GetPhase();
-	const bool do_default_action = ((int)phase & (int)default_action_phase);
 
 	// Look up the event
-	Events::iterator itr = events.find(event->GetType());
+	Events::iterator itr = events.find(event->GetId());
 
 	if (itr != events.end())
 	{
@@ -201,6 +206,8 @@ void EventDispatcher::TriggerEvents(Event* event, DefaultActionPhase default_act
 		}
 	}
 
+	const bool do_default_action = ((int)phase & (int)default_action_phase);
+
 	if (do_default_action)
 	{
 		element->ProcessDefaultAction(*event);

+ 4 - 4
Source/Core/EventDispatcher.h

@@ -59,13 +59,13 @@ public:
 	/// @param[in] type Type of the event to attach to
 	/// @param[in] event_listener The event listener to be notified when the event fires
 	/// @param[in] in_capture_phase Should the listener be notified in the capture phase
-	void AttachEvent(const String& type, EventListener* event_listener, bool in_capture_phase);
+	void AttachEvent(EventId id, EventListener* event_listener, bool in_capture_phase);
 
 	/// Detaches a listener from the specified event name
 	/// @param[in] type Type of the event to attach to
 	/// @para[in]m event_listener The event listener to be notified when the event fires
 	/// @param[in] in_capture_phase Should the listener be notified in the capture phase
-	void DetachEvent(const String& type, EventListener* listener, bool in_capture_phase);
+	void DetachEvent(EventId id, EventListener* listener, bool in_capture_phase);
 
 	/// Detaches all events from this dispatcher and all child dispatchers.
 	void DetachAllEvents();
@@ -76,7 +76,7 @@ public:
 	/// @param[in] parameters The event parameters
 	/// @param[in] interruptible Can the event propagation be stopped
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
-	bool DispatchEvent(Element* element, const String& name, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase);
+	bool DispatchEvent(Element* element, EventId id, const Dictionary& parameters);
 
 	/// Returns event types with number of listeners for debugging.
 	/// @return Summary of attached listeners.
@@ -92,7 +92,7 @@ private:
 		bool in_capture_phase;
 	};
 	typedef std::vector< Listener > Listeners;
-	typedef SmallUnorderedMap< String, Listeners > Events;
+	typedef SmallUnorderedMap< EventId, Listeners > Events;
 	Events events;
 
 	void TriggerEvents(Event* event, DefaultActionPhase default_action_phase);

+ 2 - 2
Source/Core/EventInstancerDefault.cpp

@@ -40,9 +40,9 @@ EventInstancerDefault::~EventInstancerDefault()
 {
 }
 
-Event* EventInstancerDefault::InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible)
+Event* EventInstancerDefault::InstanceEvent(Element* target, EventId id, const Dictionary& parameters)
 {
-	return new Event(target, name, parameters, interruptible);
+	return new Event(target, id, parameters);
 }
 
 // Releases an event instanced by this instancer.

+ 3 - 3
Source/Core/EventInstancerDefault.h

@@ -50,14 +50,14 @@ public:
 	/// @param[in] name Name of this event.
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] interruptible If the event propagation can be stopped.
-	virtual Event* InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible);
+	virtual Event* InstanceEvent(Element* target, EventId id, const Dictionary& parameters) override;
 
 	/// Releases an event instanced by this instancer.
 	/// @param[in] event The event to release.
-	virtual void ReleaseEvent(Event* event);
+	virtual void ReleaseEvent(Event* event) override;
 
 	/// Releases this event instancer.
-	virtual void Release();
+	virtual void Release() override;
 };
 
 }

+ 3 - 6
Source/Core/EventIterators.h

@@ -43,19 +43,16 @@ namespace Core {
 class RKTEventFunctor
 {
 public:
-	RKTEventFunctor(const String& event, const Dictionary& parameters, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase)
-		: event(event), parameters(&parameters), interruptible(interruptible), bubbles(bubbles), default_action_phase(default_action_phase) {}
+	RKTEventFunctor(EventId id, const Dictionary& parameters) : id(id), parameters(&parameters) {}
 
 	void operator()(ElementReference& element)
 	{
-		element->DispatchEvent(event, *parameters, interruptible, bubbles, default_action_phase);
+		element->DispatchEvent(id, *parameters);
 	}
 
 private:
-	String event;
+	EventId id;
 	const Dictionary* parameters;
-	bool interruptible, bubbles;
-	DefaultActionPhase default_action_phase;
 };
 
 /**

+ 133 - 0
Source/Core/EventSpecification.cpp

@@ -0,0 +1,133 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "precompiled.h"
+#include "EventSpecification.h"
+#include "../../Include/Rocket/Core/ID.h"
+
+
+namespace Rocket {
+namespace Core {
+
+// An EventId is an index into the specifications vector
+static std::vector<EventSpecification> specifications = { { EventId::Invalid, "invalid", false, false, DefaultActionPhase::None } };
+
+static UnorderedMap<String, EventId> type_lookup;
+
+namespace EventSpecificationInterface {
+
+void Initialize()
+{
+	// Must be specified in the same order as their event_id
+	specifications = {
+		{EventId::Invalid       , "invalid"       , false , false , DefaultActionPhase::None},
+		{EventId::Mousedown     , "mousedown"     , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Mousescroll   , "mousescroll"   , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Mouseover     , "mouseover"     , true  , true  , DefaultActionPhase::Target},
+		{EventId::Mouseout      , "mouseout"      , true  , true  , DefaultActionPhase::Target},
+		{EventId::Focus         , "focus"         , false , false , DefaultActionPhase::Target},
+		{EventId::Blur          , "blur"          , false , false , DefaultActionPhase::Target},
+		{EventId::Keydown       , "keydown"       , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Keyup         , "keyup"         , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Textinput     , "textinput"     , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Mouseup       , "mouseup"       , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Click         , "click"         , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Dblclick      , "dblclick"      , true  , true  , DefaultActionPhase::TargetAndBubble},
+		{EventId::Load          , "load"          , false , false , DefaultActionPhase::None},
+		{EventId::Unload        , "unload"        , false , false , DefaultActionPhase::None},
+		{EventId::Show          , "show"          , false , false , DefaultActionPhase::None},
+		{EventId::Hide          , "hide"          , false , false , DefaultActionPhase::None},
+		{EventId::Mousemove     , "mousemove"     , true  , true  , DefaultActionPhase::None},
+		{EventId::Dragmove      , "dragmove"      , true  , true  , DefaultActionPhase::Target},
+		{EventId::Drag          , "drag"          , false , true  , DefaultActionPhase::Target},
+		{EventId::Dragstart     , "dragstart"     , false , true  , DefaultActionPhase::Target},
+		{EventId::Dragover      , "dragstart"     , true  , false , DefaultActionPhase::Target},
+		{EventId::Dragdrop      , "dragdrop"      , true  , false , DefaultActionPhase::Target},
+		{EventId::Dragout       , "dragout"       , true  , false , DefaultActionPhase::Target},
+		{EventId::Dragend       , "dragend"       , true  , true  , DefaultActionPhase::None},
+		{EventId::Handledrag    , "handledrag"    , false , true  , DefaultActionPhase::None},
+		{EventId::Resize        , "resize"        , false , false , DefaultActionPhase::None},
+		{EventId::Scroll        , "scroll"        , false , true  , DefaultActionPhase::None},
+		{EventId::Scrollchange  , "scrollchange"  , false , true  , DefaultActionPhase::None},
+		{EventId::Animationend  , "animationend"  , true  , true  , DefaultActionPhase::None},
+		{EventId::Transitionend , "transitionend" , true  , true  , DefaultActionPhase::None},
+								 				 
+		{EventId::Change        , "change"        , false , true  , DefaultActionPhase::None},
+		{EventId::Submit        , "submit"        , true  , true  , DefaultActionPhase::None},
+		{EventId::Tabchange     , "tabchange"     , false , true  , DefaultActionPhase::None},
+		{EventId::Columnadd     , "tabchange"     , false , true  , DefaultActionPhase::None},
+		{EventId::Rowadd        , "rowadd"        , false , true  , DefaultActionPhase::None},
+		{EventId::Rowchange     , "rowchange"     , false , true  , DefaultActionPhase::None},
+		{EventId::Rowremove     , "rowremove"     , false , true  , DefaultActionPhase::None},
+		{EventId::Rowupdate     , "rowupdate"     , false , true  , DefaultActionPhase::None},
+	};
+
+	type_lookup.clear();
+	type_lookup.reserve(specifications.size());
+	for (auto& specification : specifications)
+		type_lookup.emplace(specification.type, specification.id);
+
+#ifdef ROCKET_DEBUG
+	// Verify all event ids specified
+	ROCKET_ASSERT((int)specifications.size() == (int)EventId::NumDefinedIds);
+
+	for (int i = 0; i < (int)specifications.size(); i++)
+	{
+		// Verify correct order
+		ROCKET_ASSERT(i == (int)specifications[i].id);
+	}
+#endif
+}
+
+const EventSpecification& Get(EventId id)
+{
+	size_t i = static_cast<size_t>(id);
+	if (i < specifications.size())
+		return specifications[i];
+	return specifications[0];
+}
+
+EventId GetIdOrDefineDefault(const String& event_type)
+{
+	auto it = type_lookup.find(event_type);
+
+	if (it != type_lookup.end())
+		return it->second;
+
+	// No specification found for this name, insert a new entry with default values
+	EventId new_id = static_cast<EventId>(specifications.size());
+	specifications.push_back(EventSpecification{ new_id, event_type, true, true, DefaultActionPhase::None });
+	type_lookup.emplace(event_type, new_id);
+	return new_id;
+}
+
+
+
+
+}
+}
+}

+ 66 - 0
Source/Core/EventSpecification.h

@@ -0,0 +1,66 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef ROCKETCOREEVENTSPECIFICATION_H
+#define ROCKETCOREEVENTSPECIFICATION_H
+
+#include "../../Include/Rocket/Core/Header.h"
+#include "../../Include/Rocket/Core/Event.h"
+#include "../../Include/Rocket/Core/ID.h"
+
+namespace Rocket {
+namespace Core {
+
+
+struct EventSpecification {
+	EventId id;
+	String type;
+	bool interruptible;
+	bool bubbles;
+	DefaultActionPhase default_action_phase;
+};
+
+namespace EventSpecificationInterface {
+
+	void Initialize();
+
+	// Get event specification for the given id.
+	// Returns the 'invalid' event type if no specification exists for id.
+	const EventSpecification& Get(EventId id);
+
+	// Get event id for the given name.
+	// Inserts a new entry with default values if event type is not already specified.
+	EventId GetIdOrDefineDefault(const String& event_type);
+
+}
+
+
+
+}
+}
+
+#endif

+ 2 - 2
Source/Core/Factory.cpp

@@ -547,9 +547,9 @@ EventInstancer* Factory::RegisterEventInstancer(EventInstancer* instancer)
 }
 
 // Instance an event object.
-Event* Factory::InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible)
+Event* Factory::InstanceEvent(Element* target, EventId id, const Dictionary& parameters)
 {
-	Event* event = event_instancer->InstanceEvent(target, name, parameters, interruptible);
+	Event* event = event_instancer->InstanceEvent(target, id, parameters);
 	if (event != NULL)
 		event->instancer = event_instancer;
 

+ 1 - 1
Source/Core/Lua/Element.cpp

@@ -146,7 +146,7 @@ int ElementDispatchEvent(lua_State* L, Element* obj)
             break;
         }
     }
-    obj->DispatchEvent(event,params, false, true, DefaultActionPhase::None);
+    obj->DispatchEvent(event, params);
     return 0;
 }
 

+ 1 - 1
Source/Core/WidgetSlider.cpp

@@ -192,7 +192,7 @@ void WidgetSlider::SetBarPosition(float _bar_position)
 	PositionBar();
 
 	Dictionary parameters = { {"value", bar_position} };
-	parent->DispatchEvent("scrollchange", parameters, false, true, DefaultActionPhase::None);
+	parent->DispatchEvent(EventId::Scrollchange, parameters);
 }
 
 // Returns the current position of the bar.

+ 32 - 0
readme.md

@@ -46,6 +46,38 @@ If upgrading from the original libRocket branch, some breaking changes should be
 - Removed RenderInterface::GetPixelsPerInch, instead the pixels per inch value has been fixed to 96 PPI, as per CSS specs. To achieve a scalable user interface, instead use the 'dp' unit.
 - Removed 'top' and 'bottom' from z-index property.
 
+### Events
+
+Events have some differences, however the existing API should mostly still work.
+
+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. 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.
+
+
+Each event type now has an associated EventId. Each event type has the following specifications:
+
+- `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.
+
+For example, the event type `click` has the following specification:
+```
+id: EventId::Click
+name: "click"
+interruptable: true
+bubbles: true
+default_action_phase: BubbleAndTarget
+```
+
+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.
+
+Breaking change:
+
+- `EventInstancer::InstanceEvent` now takes an `EventId` instead of a `String`.
+
 ## 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.