Changes from Version 1 of documentation/C++Manual/Elements

Show
Ignore:
Author:
peterc (IP: 192.168.0.1)
Timestamp:
12/20/07 12:16:34 (10 years ago)
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • documentation/C++Manual/Elements

    v0 v1  
     1[[PageOutline(1-5, Contents)]] 
     2= Elements = 
     3 
     4An element is the smallest subdivision of functionality within a document. 
     5 
     6== Size == 
     7 
     8Elements are made up of zero or more boxes, each of which has a rectangular content area surrounded by three areas of varying thicknesses; padding, borders and margin (see the [wiki:documentation/RCSS/BoxModel box model documentation] for more details). 
     9 
     10You can query the current size of an element with the GetBox() and GetNumBoxes() functions: 
     11 
     12{{{ 
     13// Returns one of the boxes describing the size of the element. 
     14// @param[in] index The index of the desired box. 
     15/ @return The requested box. 
     16const Rocket::Core::Box& GetBox(int index = 0) const; 
     17 
     18// Returns the number of boxes making up this element's geometry. 
     19// @return the number of boxes making up this element's geometry. 
     20int GetNumBoxes() const; 
     21}}} 
     22 
     23Most elements will have one box, except if they represent inline content split over multiple lines. 
     24 
     25== Position == 
     26 
     27Elements measure their position as a pixel offset from a containing ancestor element. The containing element, referred to as the ''offset parent'', is generally the element's closest ancestor with a 'position' value other than 'static'. To get an element's offset parent, call the GetOffsetParent() function. 
     28 
     29{{{ 
     30// Returns the element from which all offset calculations are currently computed. 
     31// @return This element's offset parent. 
     32Rocket::Core::Element* GetOffsetParent(); 
     33}}} 
     34 
     35To retrieve the element's offset, use GetRelativeOffset() or GetAbsoluteOffset(): 
     36 
     37{{{ 
     38// Returns the position of the top-left corner of one of the areas of this element's primary box, relative to its 
     39// offset parent's top-left border corner. 
     40// @param[in] area The desired area position. 
     41// @return The relative offset. 
     42EMP::Core::Vector2f GetRelativeOffset(Rocket::Core::Box::Area area = Box::CONTENT) const; 
     43 
     44// Returns the position of the top-left corner of one of the areas of this element's primary box, relative to 
     45// the element root. 
     46// @param[in] area The desired area position. 
     47// @return The absolute offset. 
     48EMP::Core::Vector2f GetAbsoluteOffset(Rocket::Core::Box::Area area = Box::CONTENT) const; 
     49}}} 
     50 
     51GetRelativeOffset() will return the offset from the element's offset parent's top-left border to one of the areas of the element's primary box. GetAbsoluteOffset() will return the offset from the top-left corner of the context the element is part of. 
     52 
     53You can also use the [wiki:documentation/C++Manual/Elements#DOMinterface DOM functions] GetClientLeft() and GetClientTop(). 
     54 
     55== Pseudo-classes == 
     56 
     57Elements may have one or more ''pseudo-classes'' active on them at any one time. Pseudo-classes represent minor, temporary changes of state (such as input focus or mouse hovering) that can be used to change the value of RCSS properties. 
     58 
     59To check if a pseudo-class is set on a particular element, you can use either the IsPseudoClassSet() or GetActivePseudoClasses() function. 
     60 
     61{{{ 
     62// Checks if a specific pseudo-class has been set on the element. 
     63// @param[in] pseudo_class The name of the pseudo-class to check for. 
     64// @return True if the pseudo-class is set on the element, false if not. 
     65bool IsPseudoClassSet(const EMP::Core::String& pseudo_class) const; 
     66 
     67// Gets a list of the current active pseudo-classes. 
     68// @return The list of active pseudo-classes. 
     69const Rocket::Core::PseudoClassList& GetActivePseudoClasses() const; 
     70}}} 
     71 
     72IsPseudoClassSet() will check for the prescence of a particular pseudo-class on the element, while GetActivePseudoClasses() will return an STL set containing all pseudo-classes. 
     73 
     74To set or remove a pseudo-class, call SetPseudoClass(). 
     75 
     76{{{ 
     77// Sets or removes a pseudo-class on the element. 
     78// @param[in] pseudo_class The pseudo class to activate or deactivate. 
     79// @param[in] activate True if the pseudo-class is to be activated, false to be deactivated. 
     80void SetPseudoClass(const EMP::Core::String& pseudo_class, bool activate); 
     81}}} 
     82 
     83Applications can make use of any pseudo-classes they wish for their own styling needs. However, Rocket maintains several pseudo-classes internally and it is not recommended you set or clear them yourself. These classes are: 
     84 * ''hover'': Set when the mouse cursor is positioned over the element. 
     85 * ''active'': Set when the primary mouse button is depressed, and was positioned over the element when it was pressed. 
     86 * ''focus'': Set if an element has input focus. Usually this occurs when the element is clicked on. 
     87 * ''disabled'': Set on a disabled form control. 
     88 
     89== DOM interface == 
     90 
     91Rocket elements support the majority of [http://developer.mozilla.org/en/docs/DOM:element Gecko's HTML DOM element interface], so web developers should be familiar with most of an element's functionality. 
     92 
     93|| '''Rocket functions''' || '''Brief description''' || '''Equivalent DOM property''' || 
     94|| ''GetAbsoluteLeft()'' || The distance from the context's left edge and the element's left border. || || 
     95|| ''GetAbsoluteTop()'' || The distance from the context's top edge and the element's top border. || || 
     96|| ''SetAttribute()'', ''GetAttribute()'' || All attributes associated with an element. || ''attributes'' || 
     97|| ''GetChild()'', ''GetNumChildren()'' || All child nodes of an element. || ''childNodes'' || 
     98|| ''IsClassSet()'', ''SetClass()'' || Gets/sets the class of the element. || ''className'' || 
     99|| ''GetClientHeight()'' || The inner height of an element. || ''clientHeight'' || 
     100|| ''GetClientLeft()'' || The width of the left border of an element. || ''clientLeft'' || 
     101|| ''GetClientTop()'' || The width of the top border of an element. || ''clientTop'' || 
     102|| ''GetClientWidth()'' || The inner width of an element. || ''clientWidth'' || 
     103|| ''GetFirstChild()'' || The first direct child node of an element. || ''firstChild'' || 
     104|| ''GetID()'', ''SetID()'' || Gets/sets the id of the element. || ''id'' || 
     105|| ''GetInnerRML()'', ''SetInnerRML()'' || Gets/sets the markup and content of the element. || ''innerHTML'' || 
     106|| ''GetLastChild()'' || The last direct child node of an element. || ''lastChild'' || 
     107|| ''GetNextSibling()'' || The node immediately following the given one in the tree. || ''nextSibling'' || 
     108|| ''GetOffsetHeight()'' || The height of an element, relative to the layout. || ''offsetHeight'' || 
     109|| ''GetOffsetLeft()'' || The distance from this element's left border to its offset parent's left border. || ''offsetLeft'' || 
     110|| ''GetOffsetParent()'' || The element from which all offset calculations are currently computed. || ''offsetParent'' || 
     111|| ''GetOffsetTop()'' || The distance from this element's top border to its offset parent's top border. || ''offsetTop'' || 
     112|| ''GetOffsetWidth()'' || The width of an element, relative to the layout. || ''offsetWidth'' || 
     113|| ''GetOwnerDocument()'' || The document that this node is in. || ''ownerDocument'' || 
     114|| ''GetParentNode()'' || The parent element of this node. || ''parentNode'' || 
     115|| ''GetPreviousSibling()'' || The node immediately preceding the given one in the tree. || ''previousSibling'' || 
     116|| ''GetScrollHeight()'' || The scroll view height of an element. || ''scrollHeight'' || 
     117|| ''GetScrollLeft()'' || Gets/sets the left scroll offset of an element. || ''scrollLeft'' || 
     118|| ''GetScrollTop()'' || Gets/sets the top scroll offset of an element. || ''scrollTop'' || 
     119|| ''GetScrollWidth()'' || The scroll view width of an element. || ''scrollWidth'' || 
     120|| ''GetProperty()'', ''SetProperty()'' || An object representing the declarations of an element's style attributes. || ''style'' || 
     121|| ''GetTagName()'' || The name of the tag for the given element. || ''tagName'' || 
     122 
     123Supported methods have simply had their initial letter capitalised to match the rest of the Rocket API. 
     124 
     125|| '''Rocket function''' || '''Brief description''' || '''Equivalent DOM method''' || 
     126|| ''AddEventListener()'' || Register an event handler to a specific event type on the element. || ''addEventListener()'' || 
     127|| ''AppendChild()'' || Insert a node as the last child node of this element. || ''appendChild()'' || 
     128|| ''Blur()'' || Removes keyboard focus from the current element. || ''blur()'' || 
     129|| ''Click()'' || Simulates a click on the current element. || ''click()'' || 
     130|| ''DispatchEvent()'' || Dispatch an event to this node in the DOM. || ''dispatchEvent()'' || 
     131|| ''Focus()'' || Gives keyboard focus to the current element. || ''focus()'' || 
     132|| ''GetAttribute()'' || Retrieve the value of the named attribute from the current node. || ''getAttribute()'' || 
     133|| ''GetElementById()'' || Returns an object reference to the identified element. || ''getElementById()'' || 
     134|| ''GetElementsByTagName()'' || Retrieve a set of all descendant elements, of a particular tag name, from the current element. || ''getElementsByTagName()'' || 
     135|| ''HasAttribute()'' || Check if the element has the specified attribute, or not. || ''hasAttribute()'' || 
     136|| ''HasChildNodes()'' || Check if the element has any child nodes, or not. || ''hasChildNodes()'' || 
     137|| ''InsertBefore()'' || Inserts the first node before the second, child, node in the DOM. || ''insertBefore()'' || 
     138|| ''RemoveAttribute()'' || Remove the named attribute from the current node. || ''removeAttribute()'' || 
     139|| ''RemoveChild()'' || Removes a child node from the current element. || ''removeChild()'' || 
     140|| ''RemoveEventListener()'' || Removes an event listener from the element. || ''removeEventListener()'' || 
     141|| ''ReplaceChild()'' || Replaces one child node in the current element with another. || ''replaceChild()'' || 
     142|| ''ScrollIntoView()'' || Scrolls the page until the element gets into the view. || ''scrollIntoView()'' || 
     143|| ''SetAttribute()'' || Set the value of the named attribute from the current node. || ''setAttribute()'' || 
     144 
     145== Dynamically creating elements == 
     146 
     147Elements should not be created with the 'new' operator; in order to be properly reference counted and released, in C++ they need to be created either through a document (using the CreateElement() or CreateTextNode() function) or through the Rocket factory (Rocket::Core::Factory) using the factory's static InstanceElement() function. 
     148 
     149=== Using the factory === 
     150 
     151Creating an element through the factory allows more control. The InstanceElement() function is detailed below: 
     152 
     153{{{ 
     154// Instances a single element. 
     155// @param[in] parent The parent of the new element, or NULL for a root tag. 
     156// @param[in] instancer The name of the instancer to create the element with. 
     157// @param[in] tag The tag of the element to be instanced. 
     158// @param[in] attributes The attributes to instance the element with. 
     159// @return The instanced element, or NULL if the instancing failed. 
     160static Rocket::Core::Element* InstanceElement(Rocket::Core::Element* parent, 
     161                                              const EMP::Core::String& instancer, 
     162                                              const EMP::Core::String& tag, 
     163                                              const EMP::Core::XMLAttributes& attributes); 
     164}}} 
     165 
     166The function's parameters are: 
     167 
     168 * ''parent'': The element you intend to parent the element to once it has been created. This is only used by custom instancers; if you're instancing a generic element you can leave this out. Note that the new element will not be automatically parented to this element, you still need to do that yourself once the element has been created. 
     169 * ''instancer'': The tag name the instancer you want to create the element was registered against. For creating generic elements, this can be the same as the third parameter, 'tag'. For more information, see the section on [wiki:documentation/C++Manual/Elements#Registeringaninstancer custom element instancers]. 
     170 * ''tag'': The tag the new element should have. 
     171 * ''attributes'': Any attributes you want the new element to be constructed with. This is a dictionary type. The attributes will be passed into the instancer and set on the element if instancing was successful. 
     172 
     173For example, the following will instance a 'div' element: 
     174 
     175{{{ 
     176Rocket::Core::Element* div_element = Rocket::Core::Factory::InstanceElement(NULL, 
     177                                                                            "div", 
     178                                                                            "div", 
     179                                                                            EMP::Core::XMLAttributes()); 
     180}}} 
     181 
     182The following will instance a radio button element using the Controls plugin input instancer, but gives it a tag of 'radio': 
     183{{{ 
     184EMP::Core::XMLAttributes attributes; 
     185attributes.Set("type", "radio"); 
     186attributes.Set("name", "graphics"); 
     187attributes.Set("value", "OK"); 
     188Rocket::Core::Element* radio_element = Rocket::Core::Factory::InstanceElement(div_element, 
     189                                                                              "input", 
     190                                                                              "radio", 
     191                                                                              attributes); 
     192}}} 
     193 
     194If the element is instanced successfully, it will be returned. If not, NULL (0) will be returned. All elements are reference counted, and the newly instanced element will be returned with one initial reference owned by the instancing code; be sure to remove it once you have parented the element to another. 
     195 
     196=== Using a document === 
     197 
     198To create an element through a document use one of the following functions: 
     199 
     200{{{ 
     201// Creates the named element. 
     202// @param[in] name The tag name of the element. 
     203Rocket::Core::Element* CreateElement(const EMP::Core::String& name); 
     204 
     205// Create a text element with the given text content. 
     206// @param[in] text The text content of the text element. 
     207Rocket::Core::ElementText* CreateTextNode(const EMP::Core::String& text); 
     208}}} 
     209 
     210CreateElement() takes a single parameter, ''name'', the tag name of the new element. This will be used to both look up the instancer and tag the element. Like instancing the element through the factory, the new element will be returned if it was created successfully, or NULL if not. 
     211 
     212CreateTextNode() creates a single text element containing the text given in the parameter ''text''. 
     213 
     214Note that elements returned by these functions are not affiliated with the document itself. The new element will have an initial reference count of one owned by the constructing code, so remember to remove the reference once the element has been attached to another element. 
     215 
     216The following example adds a paragraph element with a text node under it to a document: 
     217 
     218{{{ 
     219bool AddSampleText(Rocket::Core::Document* document) 
     220{ 
     221        Rocket::Core::Element* new_element = document->CreateElement("p"); 
     222        if (new_element == NULL) 
     223                return false; 
     224 
     225        Rocket::Core::TextElement* new_text_element = document->CreateTextNode("Sample text."); 
     226        if (new_text_element == NULL) 
     227        { 
     228                new_element->RemoveReference(); 
     229                return false; 
     230        } 
     231 
     232        new_element->AppendChild(new_text_element); 
     233        document->AddChild(new_element); 
     234        return true; 
     235} 
     236}}} 
     237 
     238== Destroying elements == 
     239 
     240Elements are reference-counted objects, so are not meant to be deleted directly. If an element is part of a hierarchy, simply remove it from its parent to destroy it with the RemoveChild() function. If it is a loose element, remove the initial reference on the object with the RemoveReference() function. 
     241 
     242== Custom elements == 
     243 
     244If you need special functionality on an element that you can't easily manage through the event system, you have the option of creating a custom element. Custom elements derive directly from the core Element class definition and are created through a custom element instancer. Custom elements can: 
     245 
     246 * Respond to property and attribute changes. 
     247 * Manage the layout of hidden internal elements. 
     248 * Execute custom update or rendering code. 
     249 * Respond to events inline. 
     250 * Respond to descendant add / remove events. 
     251 
     252=== Creating a custom element === 
     253 
     254All custom elements are classes derived (not necessarily directly) from Rocket::Core::Element. The constructor for Element takes one parameter, the tag of the element; a derived element's constructor can either pass a constant string down to the base constructor, or take a string themselves to pass down. 
     255 
     256The virtual functions that can be overridden in a custom element are: 
     257 
     258{{{ 
     259// Returns the baseline of the element, in pixels offset from the bottom of the element's content area. 
     260// @return The element's baseline. A negative baseline will be further 'up' the element, a positive on further 'down'. 
     261virtual float GetBaseline() const; 
     262 
     263// Gets the intrinsic dimensions of this element, if it is of a type that has an inherent size. 
     264// @param[in] dimensions The dimensions to size, if appropriate. 
     265// @return True if the element has intrinsic dimensions, false otherwise. 
     266virtual bool GetIntrinsicDimensions(EMP::Core::Vector2f& dimensions); 
     267 
     268// Called for every event sent to this element or one of its descendants. 
     269// @param[in] event The event to process. 
     270virtual void ProcessEvent(Rocket::Core::Event& event); 
     271 
     272// Called during the update loop after children are updated. 
     273virtual void OnUpdate(); 
     274// Called during render after backgrounds, borders, decorators, but before children, are rendered. 
     275virtual void OnRender(); 
     276 
     277// Called when attributes on the element are changed. 
     278// @param[in] changed_attributes The attributes changed on the element. 
     279virtual void OnAttributeChange(const Rocket::Core::AttributeNameList& changed_attributes); 
     280// Called when properties on the element are changed. 
     281// @param[in] changed_properties The properties changed on the element. 
     282virtual void OnPropertyChange(const Rocket::Core::PropertyNameList& changed_properties); 
     283 
     284// Called when a child node has been added somewhere in the hierarchy. 
     285// @param[in] child The element that has been added. This may be this element. 
     286virtual void OnChildAdd(Rocket::Core::Element* child); 
     287// Called when a child node has been removed somewhere in the hierarchy. 
     288// @param[in] child The element that has been removed. This may be this element. 
     289virtual void OnChildRemove(Rocket::Core::Element* child); 
     290 
     291// Gets the markup and content of the element. 
     292// @param[out] content The content of the element. 
     293virtual void GetInnerRML(EMP::Core::String& content) const; 
     294// Returns the RML of this element and all children. 
     295// @param[out] content The content of this element and those under it, in XML form. 
     296virtual void GetRML(EMP::Core::String& content); 
     297}}} 
     298 
     299==== Layout ==== 
     300 
     301A custom element can override the GetIntrinsicDimensions() function if it wants to be laid out as a ''replaced element''. Replaced elements are elements with intrinsic dimensions that can be positioned like inline or block content. Examples of replaced elements are images and form controls. 
     302 
     303{{{ 
     304// Gets the intrinsic dimensions of this element, if it is of a type that has an inherent size. 
     305// @param[in] dimensions The dimensions to size, if appropriate. 
     306// @return True if the element has intrinsic dimensions, false otherwise. 
     307virtual bool GetIntrinsicDimensions(EMP::Core::Vector2f& dimensions); 
     308}}} 
     309 
     310If a custom element is to be a replaced element, it should override this function and return true. The actual intrinsic dimensions of the element should be put into the ''dimensions'' parameter. This function will be called every time the element is laid out, so the dimension can be a dynamic value. The default element returns false. 
     311 
     312A custom replaced element (ie, one with intrinsic dimensions) can override the GetBaseline() function if it wants to change its reference point for horizontal positioning on a line. 
     313 
     314{{{ 
     315// Returns the baseline of the element, in pixels offset from the bottom of the element's content area. 
     316// @return The element's baseline. A negative baseline will be further 'up' the element, a positive on further 'down'. 
     317virtual float GetBaseline() const; 
     318}}} 
     319 
     320The GetBaseline() function returns the pixel offset from the bottom of the element's content area that neighbouring text should, by default, line their baselines up with. This will only affect the element's positioning if it is placed inline. 
     321 
     322 
     323==== Event processing ==== 
     324 
     325A custom element can override the ProcessEvent() function to intercept all events sent to this element, or one of its descendants. The element receives the event between the bubble and capture phases. 
     326 
     327Note that the element receives ''every'' event, and it is not necessary the target element! Be sure to check the target element of the event and the event type. 
     328 
     329'''Important:''' You must remember to call the base class's ProcessEvent() function with any unprocessed events! The base element responds to many events in its ProcessEvent() function, and all manner of strange behaviour may result if you don't do this. 
     330 
     331{{{ 
     332// Called for every event sent to this element or one of its descendants. 
     333// @param[in] event The event to process. 
     334virtual void ProcessEvent(Rocket::Core::Event& event); 
     335}}} 
     336 
     337==== Hooks into update and render loops ==== 
     338 
     339A custom element can override the OnUpdate() or OnRender() functions to hook functionality into the update or render loops. 
     340 
     341The OnUpdate() function is called from the base element's update loop after the element's children have been updated. Child elements will therefore have their OnUpdate() functions call before their parents. There is no need to call the base element's OnUpdate() function if you do not wish to. 
     342 
     343{{{ 
     344// Called during the update loop after children are updated. 
     345virtual void OnUpdate(); 
     346}}} 
     347 
     348The OnRender() function is called from the base element's render loop after the following has occurred: 
     349 * Descendant elements in the element's stacking context with a z-index of lower than 0 have been rendered. 
     350 * The clipping region has been set for the element (if appropriate). 
     351 * The elements background, border and all appropriate decorators have been rendered. 
     352 
     353There is no need to call the base element's OnRender() function is you do not wish to. Note that most custom rendering can be accomplished through the use of [wiki:documentation/C++Manual/Decorators custom decorators]; this is recommended rather than overriding OnRender() for reusability. 
     354 
     355{{{ 
     356// Called during render after backgrounds, borders, decorators, but before children, are rendered. 
     357virtual void OnRender(); 
     358}}} 
     359 
     360==== Changes to properties or attributes ==== 
     361 
     362A custom element can override the OnAttributeChange() or OnPropertyChange() functions to respond to changes to its attributes or properties. 
     363 
     364OnAttributeChange() is called whenever an attribute is added, removed or redefined. The names of the changed attributes are passed into the function in the 'changed_attributes' variable, which is an STL set of strings. To check if a specific attribute has been altered, look for its presence in the list with the find() function. 
     365 
     366{{{ 
     367// Called when attributes on the element are changed. 
     368// @param[in] changed_attributes The attributes changed on the element. 
     369virtual void OnAttributeChange(const Rocket::Core::AttributeNameList& changed_attributes); 
     370}}} 
     371 
     372OnPropertyChange() is called whenever the value of a property (or group of properties) is changed. The names of the changed properties are passed into the function in the 'changed_properties' variable, which (like for OnAttributeChange()) is an STL set of strings. 
     373 
     374{{{ 
     375// Called when properties on the element are changed. 
     376// @param[in] changed_properties The properties changed on the element. 
     377virtual void OnPropertyChange(const Rocket::Core::PropertyNameList& changed_properties); 
     378}}} 
     379 
     380'''Important:''' If you override either of these functions, you must remember to call the base class's corresponding function! As with ProcessEvent(), the base element responds to many attribute and property changes, and all manner of strange behaviour may result if you don't do this. 
     381 
     382==== Hierarchy changes ==== 
     383 
     384A custom element can override the OnChildAdd() or OnChildRemove() functions to respond to changes in the element's hierarchy. When an element is added or removed from another, the appropriate function is called on it immediately. The base implementation then passes the call onto its parent, thereby informing the whole hierarchy. If you want to prevent messages from propagating up the element hierarchy, you can choose to not call the base element's OnChildAdd() or OnChildRemove() function as appropriate. 
     385 
     386{{{ 
     387// Called when a child node has been added somewhere in the hierarchy. 
     388// @param[in] child The element that has been added. This may be this element. 
     389virtual void OnChildAdd(Rocket::Core::Element* child); 
     390 
     391// Called when a child node has been removed somewhere in the hierarchy. 
     392// @param[in] child The element that has been removed. This may be this element. 
     393virtual void OnChildRemove(Rocket::Core::Element* child); 
     394}}} 
     395 
     396==== RML generation ==== 
     397 
     398A custom element can override the GetRML() and GetInnerRML() function if the default RML generation functions are inadequate. This is generally not needed, unless an element rearranges its child elements internally, or makes heavy use of custom XML node handlers; in this case, the default functions may generate nonsensical RML. 
     399 
     400GetInnerRML() is meant to return the ''internal'' RML of the element; ie, only the RML needed to generate the element's content, not the element itself. By default, it calls GetRML() on all of its DOM children and concatenates the result. 
     401 
     402GetRML() is meant to return the RML required to generate the entire element. This therefore includes the element's tag and all of its attributes as well as all of its descendant's RML. By default, it generates its open tag, appends to that the the result of GetInnerRML(), and finally appends its closing tag. 
     403 
     404{{{ 
     405// Gets the markup and content of the element. 
     406// @param[out] content The content of the element. 
     407virtual void GetInnerRML(EMP::Core::String& content) const; 
     408 
     409// Returns the RML of this element and all children. 
     410// @param[out] content The content of this element and those under it, in XML form. 
     411virtual void GetRML(EMP::Core::String& content); 
     412}}} 
     413 
     414=== Creating a custom element instancer === 
     415 
     416In order to have a custom element created through the Rocket factory, an instancer for the element needs to be registered with the factory against the appropriate RML tag names. An element instancer is responsible for creating and destroying its elements when required, and also destroying itself when Rocket is shut down. 
     417 
     418A custom element instancer needs to be derived from Rocket::Core::ElementInstancer, and implement the required pure virtual methods: 
     419 
     420{{{ 
     421// Instances an element given the tag name and attributes. 
     422// @param[in] parent The element the new element is destined to be parented to. 
     423// @param[in] tag The tag of the element to instance. 
     424// @param[in] attributes Dictionary of attributes. 
     425virtual Rocket:Core::Element* InstanceElement(Rocket::Core::Element* parent, 
     426                                              const EMP::Core::String& tag, 
     427                                              const EMP::Core::XMLAttributes& attributes) = 0; 
     428 
     429// Releases an element instanced by this instancer. 
     430// @param[in] element The element to release. 
     431virtual void ReleaseElement(Rocket::Core::Element* element) = 0; 
     432 
     433// Release the instancer. 
     434virtual void Release() = 0; 
     435}}} 
     436 
     437InstanceElement() will be called whenever the factory is called upon to instance an element with a tag that the instancer was registered against. The parameters to the function are: 
     438 * ''parent'': The element that the new element will be parented to if it is created successfully; you do not need to actually do the parenting! This will only be non-NULL if the element is instanced from RML. 
     439 * ''tag'': The string that whoever is creating the element wants the element's tag to be; due to the way elements are constructed through the factory, this may not be one of the tags the instancer was registered against. It is recommended you pass this through to the element to be its tag name, but this is not required. 
     440 * ''attributes'': The attributes defined on the element's tag in RML or passed into the factory. You do not need to set these attributes on the element yourself; that will be done automatically if the instancing is successful. You only need to use these if element instancing is dependent on the values (for example, the instancer for 'input' elements instances different types depending on the value of the 'type' attribute). 
     441 
     442If InstanceElement() is successful, return the new element. Otherwise, return NULL (0) to indicate an instancing error. 
     443 
     444ReleaseElement() will be called when an element instanced through the instancer is no longer required by the system. It should be deleted appropriately. 
     445 
     446Release() will be called when the element instancer is no longer required, usually when Rocket is shut down. The instancer should delete itself as appropriate. 
     447 
     448==== Registering an instancer ==== 
     449 
     450To register a custom instancer with Rocket, call the RegisterElementInstancer() function on the Rocket factory (Rocket::Core::Factory) after Rocket has been initialised. 
     451 
     452{{{ 
     453Rocket::Core::ElementInstancer* custom_instancer = new ElementInstancerCustom(); 
     454Rocket::Core::Factory::RegisterElementInstancer("custom", custom_instancer); 
     455custom_instancer->RemoveReference(); 
     456}}} 
     457 
     458The first parameter to RegisterElementInstancer() is the tag name the instancer is bound to. In the above example, the custom instancer will be called to instance an element whenever an element with the tag 'custom' is encountered while parsing an RML stream, or as otherwise required by the factory. You can register an instancer as many times as you like with the factory against different tag names. 
     459 
     460Instancers are reference counted, and begin with a reference count of one which belongs to the constructing process. The factory itself will add a reference for every tag name it is bound to. Therefore, remember to remove a reference from the object after it has been registered with the factory. If you don't, it will never be released. 
     461 
     462==== Using a generic instancer ==== 
     463 
     464If a custom element does not require any special behaviour from its instancer, the easiest way to generate an instancer for it is to use the templated ElementInstancerGeneric. Instead of deriving your own instancer class, simply construct a new Rocket::Core::ElementInstancerGeneric templated to the type of the custom element you'd like to instance, and register it with the factory as you would a normal instancer. 
     465 
     466{{{ 
     467Rocket::Core::ElementInstancer* custom_instancer = new Rocket::Core::ElementInstancerGeneric< CustomElement >(); 
     468Rocket::Core::Factory::RegisterElementInstancer("custom", custom_instancer); 
     469custom_instancer->RemoveReference(); 
     470}}} 
     471 
     472The only requirement on the element type that it is templated to is that the constructor take a string (the tag name) like the base element. 
     473 
     474=== Custom XML node handling === 
     475 
     476For some complex custom elements, the RML required to generate the element is not indicative of the actual internal hierarchy. For example, in the [wiki:documentation/C++Manual/Controls Controls plugin], columns in a data grid element are specified by <col> tags immediately beneath the <datagrid> tag. If the standard XML parsing was being executed, an element would be instanced and parented to the data grid for each column tag - but this isn't what is wanted. So a custom XML node handler is used for data grids that processes the column tag differently. 
     477 
     478Node handlers are registered against RML tag names. When an RML file is being parsed, the XML parser maintains a stack of node handlers. Whenever a new tag is encountered, the parser checks if a specific node handler is registered against that tag; if so, that handler is pushed onto the stack and takes over the parsing until its associated tag is closed. If no handler is associated with a particular element, the current node handler continues parsing. 
     479 
     480==== Creating a custom XML node handler ==== 
     481 
     482Custom node handlers derive from the Rocket::Core::XMLNodeHandler class and implement the pure virtual functions: 
     483 
     484{{{ 
     485// Called when a new element tag is opened. 
     486// @param parser The parser executing the parse. 
     487// @param name The XML tag name. 
     488// @param attributes The tag attributes. 
     489// @return The new element, may be NULL if no element was created. 
     490virtual Rocket::Core::Element* ElementStart(Rocket::Core::XMLParser* parser, 
     491                                            const EMP::Core::String& name, 
     492                                            const EMP::Core::XMLAttributes& attributes) = 0; 
     493 
     494// Called when an element is closed. 
     495// @param parser The parser executing the parse. 
     496// @param name The XML tag name. 
     497virtual bool ElementEnd(Rocket::Core::XMLParser* parser, 
     498                        const EMP::Core::String& name) = 0; 
     499 
     500// Called for element data. 
     501// @param parser The parser executing the parse. 
     502// @param data The element data. 
     503virtual bool ElementData(Rocket::Core::XMLParser* parser, 
     504                         const EMP::Core::String& data) = 0; 
     505 
     506// Called to release the node handler. 
     507virtual void Release() = 0; 
     508}}} 
     509 
     510Release() is called when Rocket is shut down; the node handler should delete itself as appropriate. 
     511 
     512ElementStart(), ElementEnd() and ElementData() are called on the node handler for the appropriate XML parse events that occur while it is the active node handler. A self-closing tag will result in a call to ElementEnd() immediately after ElementStart(). ElementData() is called when loose non-whitespace data is encountered between two tags. 
     513 
     514Each of these functions is passed a pointer to the XML parser running the parse. From the parser the current ''parse frame'' can be requested with the GetParseFrame() function; the parse frame object contains the current element and tag being processed, as well as the active node handler. 
     515 
     516{{{ 
     517struct ParseFrame 
     518{ 
     519        // Tag being parsed. 
     520        EMP::Core::String tag; 
     521 
     522        // Element representing this frame. 
     523        Rocket::Core::Element* element; 
     524 
     525        // Handler used for this frame. 
     526        XMLNodeHandler* node_handler; 
     527 
     528        // The default handler used for this frame's children. 
     529        XMLNodeHandler* child_handler; 
     530}; 
     531}}} 
     532 
     533ElementStart() is called with the name and attributes of the opening tag. If the node handler creates a new element and wants it on the top parse frame, it should return the element. Otherwise, it should return NULL to keep the current element on top of the parse frame stack. This is useful if the node handler creates internal elements for the current element, but doesn't want any further parsing executed on them. 
     534 
     535If the node handler wants to change the node handler for the new element, it can push a new handler onto the XML parser's stack using PushHandler() or PushDefaultHandler() from ElementStart(). The default handler will instance elements as described previously in the documentation. 
     536 
     537{{{ 
     538// Pushes an element handler onto the parse stack for parsing child elements. 
     539// @param[in] tag The tag the handler was registered with. 
     540// @return True if an appropriate handler was found and pushed onto the stack, false if not. 
     541bool PushHandler(const EMP::Core::String& tag); 
     542 
     543// Pushes the default element handler onto the parse stack. 
     544void PushDefaultHandler(); 
     545}}} 
     546 
     547If it doesn't call either of these methods, it will remain the node handler for any child elements it creates. 
     548 
     549==== Registering a custom node handler ==== 
     550 
     551Register a custom node handler with Rocket's XML parser with the static RegisterNodeHandler() function on Rocket::Core::XMLParser. You can register the same handler multiple times with the parser against different tag names. RegisterNodeHandler() adds a reference to the node handler, so be sure to remove the initial reference from the handler once it has been registered. 
     552 
     553{{{ 
     554// Registers a custom node handler to be used to a given tag. 
     555// @param[in] tag The tag the custom parser will handle. 
     556// @param[in] handler The custom handler. 
     557// @return The registered XML node handler. 
     558static Rocket::Core::XMLNodeHandler* RegisterNodeHandler(const EMP::Core::String& tag, 
     559                                                         Rocket::Core::XMLNodeHandler* handler); 
     560}}} 
     561 
     562==== Samples ==== 
     563 
     564Custom XML node handlers are used extensively in the Controls plugin; consult the source for the XMLNodeHandlerDataGrid, XMLNodeHandlerTabSet and XMLNodeHandlerTextArea classes for demonstrations of their use.