Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/GUI/IMUI/im_element.cpp')
-rw-r--r--Source/GUI/IMUI/im_element.cpp1737
1 files changed, 1737 insertions, 0 deletions
diff --git a/Source/GUI/IMUI/im_element.cpp b/Source/GUI/IMUI/im_element.cpp
new file mode 100644
index 00000000..864dc1cf
--- /dev/null
+++ b/Source/GUI/IMUI/im_element.cpp
@@ -0,0 +1,1737 @@
+//-----------------------------------------------------------------------------
+// Name: im_element.cpp
+// Developer: Wolfire Games LLC
+// Description: Base class for all AdHoc Gui elements
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+#include "im_element.h"
+
+#include <GUI/IMUI/imgui.h>
+
+#include <algorithm>
+#include <map>
+#include <cmath>
+
+/*******************************************************************************************/
+/**
+ * @brief Constructor
+ *
+ * @param _name Name for this object (incumbent on the programmer to make sure they're unique)
+ *
+ */
+IMElement::IMElement( std::string const& _name ) :
+ size( UNDEFINEDSIZE,UNDEFINEDSIZE ),
+ defaultSize( UNDEFINEDSIZE,UNDEFINEDSIZE ),
+ drawDisplacement( 0.0f, 0.0f ),
+ zOrdering(1),
+ parent(NULL),
+ owner(NULL),
+ show(true),
+ shouldClip(true),
+ color(1.0,1.0,1.0,1.0),
+ effectColor(1.0,1.0,1.0,1.0),
+ isColorEffected(false),
+ border(false),
+ borderSize(1),
+ borderColor(1.0,1.0,1.0,1.0),
+ mouseOver(false),
+ mouse_clicking(false),
+ mouseDownForChildren(false),
+ mouseOverForChildren(false),
+ refCount(1),
+ numBehaviors(0),
+ pauseBehaviors(false),
+ scriptMouseOver(false)
+{
+ name = _name;
+ setPadding( 0.0, 0.0, 0.0, 0.0 );
+
+ imevents.RegisterListener(this);
+}
+
+std::vector<IMElement*> IMElement::deletion_schedule;
+
+/*******
+ *
+ * Angelscript memory management boilerplate
+ *
+ */
+void IMElement::AddRef()
+{
+ // Increase the reference counter
+ refCount++;
+}
+
+void IMElement::Release()
+{
+ refCount--;
+ // Decrease ref count and delete if it reaches 0
+ if( refCount == 0 ) {
+ for(IMElement* element : IMElement::deletion_schedule) {
+ if(element == this) {
+ return;
+ }
+ }
+ IMElement::deletion_schedule.push_back(this);
+ } else if( refCount < 0 ) {
+ LOGE << "Negative refcount" << std::endl;
+ }
+}
+
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the name of the type of this element — for autonaming and debugging
+ *
+ * @returns name of the element type as a string
+ *
+ */
+std::string IMElement::getElementTypeName() {
+ return "Element";
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Set’s this element’s parent (and does nessesary logic)
+ *
+ * @param _parent New parent
+ *
+ */
+void IMElement::setOwnerParent( IMGUI* _owner, IMElement* _parent ) {
+ owner = _owner;
+ parent = _parent;
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Set the color for the element
+ *
+ * @param _color 4 component vector for the color
+ *
+ */
+void IMElement::setColor( vec4 _color ) {
+ color = _color;
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the current color
+ *
+ * If the color is effected, it'll return the effected color
+ *
+ * @returns 4 component vector of the color
+ *
+ */
+ vec4 IMElement::getColor() {
+ if(isColorEffected ) {
+ return effectColor;
+ }
+ else {
+ return color;
+ }
+ }
+
+ /*******************************************************************************************/
+/**
+ * @brief Gets the current color -- ignoring the effect color
+ *
+ * @returns 4 component vector of the color
+ *
+ */
+ vec4 IMElement::getBaseColor() {
+ return color;
+ }
+
+
+/*******************************************************************************************/
+/**
+ * @brief Set the effect color for the element
+ *
+ * @param _color 4 component vector for the color
+ *
+ */
+void IMElement::setEffectColor( vec4 _color ) {
+ isColorEffected = true;
+ effectColor = _color;
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the effect current color
+ *
+ * @returns 4 component vector of the color
+ *
+ */
+ vec4 IMElement::getEffectColor() {
+ if( !isColorEffected ) {
+ return color;
+ }
+ return effectColor;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Clears any effect color (reseting to the base)
+ *
+ */
+ void IMElement::clearColorEffect() {
+ isColorEffected = false;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the red value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setR( float value ) {
+ color.x() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the red value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getR() {
+ return color.x();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the green value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setG( float value ) {
+ color.y() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the green value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getG() {
+ return color.y();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the blue value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setB( float value ) {
+ color.z() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the blue value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getB() {
+ return color.y();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the alpha value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setAlpha( float value ) {
+ color.a() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the alpha value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getAlpha() {
+ return color.a();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the effect red value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setEffectR( float value ) {
+ if( !isColorEffected ) {
+ effectColor = color;
+ }
+ isColorEffected = true;
+ effectColor.x() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the effect red value
+ *
+ * @returns Color value
+ *
+ */
+float IMElement::getEffectR() {
+ if( !isColorEffected ) {
+ return color.x();
+ }
+ else {
+ return effectColor.x();
+ }
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Clear effect red value
+ *
+ */
+void IMElement::clearEffectR() {
+ isColorEffected = false;
+ effectColor.x() = color.x();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the effect green value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setEffectG( float value ) {
+ if( !isColorEffected ) {
+ effectColor = color;
+ }
+ isColorEffected = true;
+ effectColor.y() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the effect green value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getEffectG() {
+ if( !isColorEffected ) {
+ return color.y();
+ }
+ else {
+ return effectColor.y();
+ }
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Clear effect green value
+ *
+ */
+void IMElement::clearEffectG() {
+ isColorEffected = false;
+ effectColor.y() = color.y();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the blue value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setEffectB( float value ) {
+ if( !isColorEffected ) {
+ effectColor = color;
+ }
+ isColorEffected = true;
+ effectColor.z() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the blue value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getEffectB() {
+ if( !isColorEffected ) {
+ return color.z();
+ }
+ else {
+ return effectColor.z();
+ }
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Clear effect blue value
+ *
+ */
+void IMElement::clearEffectB() {
+ isColorEffected = false;
+ effectColor.z() = color.z();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the alpha value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setEffectAlpha( float value ) {
+ if( !isColorEffected ) {
+ effectColor = color;
+ }
+ isColorEffected = true;
+ effectColor.a() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the alpha value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getEffectAlpha() {
+ if( !isColorEffected ) {
+ return color.z();
+ }
+ else {
+ return effectColor.z();
+ }
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Clear effect alpha value
+ *
+ */
+void IMElement::clearEffectAlpha() {
+ isColorEffected = false;
+ effectColor = color;
+}
+
+
+/*******************************************************************************************/
+/**
+ * @brief Should this element have a border
+ *
+ * @param _border Show this border or not
+ *
+ */
+ void IMElement::showBorder( bool _border ) {
+ border = _border;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the border thickness
+ *
+ * @param thickness Thickness of the border in GUI space pixels
+ *
+ */
+ void IMElement::setBorderSize( float _borderSize ) {
+ borderSize = _borderSize;
+ }
+
+
+
+/*******************************************************************************************/
+/**
+ * @brief Set the color for the border
+ *
+ * @param _color 4 component vector for the color
+ *
+ */
+void IMElement::setBorderColor( vec4 _color ) {
+ borderColor = _color;
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the current border color
+ *
+ * @returns 4 component vector of the color
+ *
+ */
+ vec4 IMElement::getBorderColor() {
+ return borderColor;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the border red value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setBorderR( float value ) {
+ borderColor.x() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the border red value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getBorderR() {
+ return borderColor.x();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the border green value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setBorderG( float value ) {
+ borderColor.y() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the border green value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getBorderG() {
+ return borderColor.y();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the border blue value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setBorderB( float value ) {
+ borderColor.z() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the border blue value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getBorderB() {
+ return borderColor.y();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the border alpha value
+ *
+ * @param value Color value
+ *
+ */
+ void IMElement::setBorderAlpha( float value ) {
+ borderColor.a() = value;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the border alpha value
+ *
+ * @returns Color value
+ *
+ */
+ float IMElement::getBorderAlpha() {
+ return borderColor.a();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the z ordering (order of drawing, higher is drawing on top of lower)
+ *
+ * @param z new Z ordering value (expected to be greater then 0 and the parent container)
+ *
+ */
+ void IMElement::setZOrdering( int z ) {
+ zOrdering = z;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the z ordering (order of drawing - higher is drawing on top of lower)
+ *
+ * @returns current Z ordering value
+ *
+ */
+ int IMElement::getZOrdering() {
+ return zOrdering;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Set the z ordering of this element to be higher than the given element
+ *
+ * @param element Element to be below this one
+ *
+ */
+ void IMElement::renderAbove( IMElement* element ) {
+ setZOrdering( element->getZOrdering() + 1 );
+ element->Release();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Set the z ordering of this element to be lower than the given element
+ *
+ * (note that if the element parameter has a z value within 1 of the parent container
+ * this element will be assigned to the same value, which may not look nice )
+ *
+ * @param element Element to be below this one
+ *
+ */
+ void IMElement::renderBelow( IMElement* element ) {
+
+ int minZ;
+ // See if we're a root container
+ if( parent != NULL ) {
+ // not a root
+ minZ = parent->getZOrdering();
+ }
+ else {
+ // we're a root
+ minZ = 1;
+ }
+
+ // set the z ordering, respecting the derived minimum
+ setZOrdering( max( minZ, element->getZOrdering() - 1 ) );
+
+ element->Release();
+
+ }
+
+
+/*******************************************************************************************/
+/**
+ * @brief Show or hide this element
+ *
+ * @param _show Show this element or not
+ *
+ */
+ void IMElement::setVisible( bool _show ) {
+ show = _show;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Should this element be including in the container clipping?
+ *
+ * @param _clip Clip this element or not
+ *
+ */
+ void IMElement::setClip( bool _clip ) {
+ shouldClip = _clip;
+ }
+
+
+/*******************************************************************************************/
+/**
+ * @brief Draw this object on the screen
+ *
+ * @param drawOffset Absolute offset from the upper lefthand corner (GUI space)
+ * @param clipPos pixel location of upper lefthand corner of clipping region
+ * @param clipSize size of clipping region
+ *
+ */
+void IMElement::render( vec2 drawOffset, vec2 currentClipPos, vec2 currentClipSize ) {
+
+ // see if we're visible
+ if( !show ) return;
+
+ // See if we're supposed to draw a border
+ if( border ) {
+
+ vec2 borderCornerUL = drawOffset + drawDisplacement + vec2(2,2) + vec2( paddingL, paddingU );
+ vec2 borderCornerLR = drawOffset + drawDisplacement + size - vec2(1,1) + vec2( paddingR, paddingD );
+
+ vec2 screenCornerUL = screenMetrics.GUIToScreen( borderCornerUL );
+ vec2 screenCornerLR = screenMetrics.GUIToScreen( borderCornerLR );
+
+ // figure out the thickness in screen pixels (minimum 1)
+ float thickness = max( borderSize * screenMetrics.GUItoScreenXScale, 1.0f );
+
+ // top
+ owner->drawBox( screenCornerUL,
+ vec2( screenCornerLR.x() - screenCornerUL.x(), thickness ),
+ borderColor,
+ zOrdering + 1,
+ shouldClip,
+ currentClipPos,
+ currentClipSize );
+
+ // bottom
+ owner->drawBox( vec2( screenCornerUL.x(), screenCornerLR.y() - thickness ),
+ vec2( screenCornerLR.x() - screenCornerUL.x(), thickness ),
+ borderColor,
+ zOrdering + 1,
+ shouldClip,
+ currentClipPos,
+ currentClipSize );
+
+ // left
+ owner->drawBox( vec2( screenCornerUL.x(), screenCornerUL.y() + thickness ),
+ vec2( thickness, screenCornerLR.y() - screenCornerUL.y() - (2 * thickness) ),
+ borderColor,
+ zOrdering + 1,
+ shouldClip,
+ currentClipPos,
+ currentClipSize );
+ // right
+ owner->drawBox( vec2( screenCornerLR.x() - thickness, screenCornerUL.y() + thickness ),
+ vec2( thickness, screenCornerLR.y() - screenCornerUL.y() - (2 * thickness) ),
+ borderColor,
+ zOrdering + 1,
+ shouldClip,
+ currentClipPos,
+ currentClipSize );
+
+ }
+}
+
+vec2 IMElement::getScreenPosition( ){
+
+ vec2 GUIRenderPos = lastDrawOffset + vec2( paddingL, paddingU ) + drawDisplacement;
+
+ vec2 screenRenderPos = screenMetrics.GUIToScreen( GUIRenderPos );
+
+ return vec2( screenRenderPos.x(), screenRenderPos.y() );
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Checks to see if a point is inside this element
+ *
+ * @param drawOffset The upper left hand corner of where the boundary is drawn
+ * @param point point in question
+ *
+ * @returns true if inside, false otherwise
+ *
+ */
+bool IMElement::pointInElement( vec2 drawOffset, vec2 point ) {
+
+ vec2 UL = drawOffset + drawDisplacement;
+ vec2 LR = UL + size;
+
+ if( UL.x() <= point.x() && UL.y() <= point.y() &&
+ LR.x() > point.x() && LR.y() > point.y() ) {
+ return true;
+ }
+ else {
+ return false;
+ }
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Add an update behavior
+ *
+ * @param behavior Handle to behavior in question
+ * @param behaviorName name to identify the behavior
+ *
+ */
+void IMElement::addUpdateBehavior( IMUpdateBehavior* behavior, std::string const& behaviorName ) {
+
+ std::string bName = behaviorName;
+
+ // if they haven't given us a name, generate one
+ if( bName == "" ) {
+ std::ostringstream oss;
+ oss << "behavior" << numBehaviors;
+ bName = oss.str();
+ }
+ else if( updateBehaviors.find( bName) != updateBehaviors.end() ) {
+ removeUpdateBehavior( bName );
+ }
+
+ // add a reference as we're storing it
+ behavior->AddRef();
+
+ updateBehaviors[bName] = behavior;
+
+ // get rid of the reference we were given
+ behavior->Release();
+
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Removes a named update behavior
+ *
+ * @param behaviorName name to identify the behavior
+ *
+ * @returns true if there was a behavior to remove, false otherwise
+ *
+ */
+ bool IMElement::removeUpdateBehavior( std::string const& behaviorName ) {
+ // see if there is already a behavior with this name
+ UBMap::iterator it = updateBehaviors.find(behaviorName);
+ if( it != updateBehaviors.end() ) {
+ // if so clean it up if its been initialized
+ IMUpdateBehavior* updater = it->second;
+
+ if( updater->initialized ) {
+ updater->cleanUp( this );
+ }
+
+ // and remove it, deref it
+ updater->Release();
+ updateBehaviors.erase( it );
+
+ return true;
+ }
+ else {
+ // Let the caller know
+ return false;
+ }
+
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Indicates if a behavior exists, can be used to see if its finished.
+ *
+ * @param behaviorName name to identify the behavior
+ *
+ * @returns true if there was a behavior
+ *
+ */
+bool IMElement::hasUpdateBehavior(std::string const& behaviorName)
+{
+
+ UBMap::iterator it = updateBehaviors.find(behaviorName);
+ if( it != updateBehaviors.end() ) {
+ return true;
+ }
+ else {
+ // Let the caller know
+ return false;
+ }
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Clear update behaviors
+ *
+ */
+ void IMElement::clearUpdateBehaviors() {
+
+ // iterate through all the behaviors and clean them up
+ for( UBMap::iterator it = updateBehaviors.begin();
+ it != updateBehaviors.end();
+ ++it ) {
+ IMUpdateBehavior* updater = it->second;
+ updater->cleanUp( this );
+ updater->Release();
+ }
+
+ updateBehaviors.clear();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Add a mouse over behavior
+ *
+ * @param behavior Handle to behavior in question
+ * @param behaviorName name to identify the behavior
+ *
+ */
+ void IMElement::addMouseOverBehavior( IMMouseOverBehavior* behavior, std::string const& behaviorName ) {
+
+ std::string bName = behaviorName;
+
+ // if they haven't given us a name, generate one
+ if( bName == "" ) {
+
+ std::ostringstream oss;
+ oss << "behavior" << numBehaviors;
+ bName = oss.str();
+ numBehaviors++;
+
+ }
+ else if( mouseOverBehaviors.find( bName) != mouseOverBehaviors.end() ) {
+ // Remove old behavior with this name (if any)
+ removeMouseOverBehavior( bName );
+ }
+
+ // add a reference as we're storing it
+ behavior->AddRef();
+
+ mouseOverBehaviors[bName] = behavior;
+
+ // get rid of the reference we were given
+ behavior->Release();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Removes a named update behavior
+ *
+ * @param behaviorName name to identify the behavior
+ *
+ * @returns true if there was a behavior to remove, false otherwise
+ *
+ */
+ bool IMElement::removeMouseOverBehavior( std::string const& behaviorName ) {
+ // see if there is already a behavior with this name
+ MOBMap::iterator it = mouseOverBehaviors.find(behaviorName);
+ if( it != mouseOverBehaviors.end() ) {
+
+ // if so clean it up
+ IMMouseOverBehavior* updater = it->second;
+ updater->cleanUp( this );
+
+ // and remove/deref it
+ updater->Release();
+ mouseOverBehaviors.erase( it );
+
+ return true;
+ }
+ else {
+ // Let the caller know
+ return false;
+ }
+
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Clear mouse over behaviors
+ *
+ */
+ void IMElement::clearMouseOverBehaviors() {
+
+ // iterate through all the behaviors and clean them up
+ for( MOBMap::iterator it = mouseOverBehaviors.begin();
+ it != mouseOverBehaviors.end();
+ ++it ) {
+ IMMouseOverBehavior* updater = it->second;
+ updater->Release();
+ updater->cleanUp( this );
+ }
+
+ mouseOverBehaviors.clear();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Add a click behavior
+ *
+ * @param behavior Handle to behavior in question
+ * @param behaviorName name to identify the behavior
+ *
+ */
+void IMElement::addLeftMouseClickBehavior( IMMouseClickBehavior* behavior, std::string const& behaviorName ) {
+ std::string bName = behaviorName;
+
+ // if they haven't given us a name, generate one
+ if( bName == "" ) {
+ std::ostringstream oss;
+ oss << "behavior" << numBehaviors;
+ bName = oss.str();
+ numBehaviors++;
+ }
+ else if( leftMouseClickBehaviors.find( bName) != leftMouseClickBehaviors.end() ) {
+ removeLeftMouseClickBehavior( bName );
+ }
+
+ // add a reference as we're storing it
+ behavior->AddRef();
+
+ leftMouseClickBehaviors[bName] = behavior;
+
+ // get rid of the reference we were given
+ behavior->Release();
+}
+
+
+/*******************************************************************************************/
+/**
+ * @brief Removes a named click behavior
+ *
+ * @param behaviorName name to identify the behavior
+ *
+ * @returns true if there was a behavior to remove, false otherwise
+ *
+ */
+ bool IMElement::removeLeftMouseClickBehavior( std::string const& behaviorName ) {
+
+ // see if there is already a behavior with this name
+ MCBMap::iterator it = leftMouseClickBehaviors.find(behaviorName);
+ if( it != leftMouseClickBehaviors.end() ) {
+
+ // if so clean it up
+ IMMouseClickBehavior* updater = it->second;
+ updater->cleanUp( this );
+
+ // and remove/deref it
+ updater->Release();
+ leftMouseClickBehaviors.erase( it );
+
+ return true;
+ }
+ else {
+ // Let the caller know
+ return false;
+ }
+
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Clear mouse over behaviors
+ *
+ */
+void IMElement::clearLeftMouseClickBehaviors() {
+
+ // iterate through all the behaviors and clean them up
+ for( MCBMap::iterator it = leftMouseClickBehaviors.begin();
+ it != leftMouseClickBehaviors.end();
+ ++it ) {
+ IMMouseClickBehavior* updater = it->second;
+ updater->cleanUp( this );
+ updater->Release();
+ }
+
+ leftMouseClickBehaviors.clear();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Updates the element
+ *
+ * @param delta Number of millisecond elapsed since last update
+ * @param drawOffset Absolute offset from the upper lefthand corner (GUI space)
+ * @param guistate The state of the GUI at this update
+ *
+ */
+void IMElement::update( uint64_t delta, vec2 drawOffset, GUIState& guistate ) {
+ // Update behaviors
+ if(!pauseBehaviors)
+ {
+ std::vector<std::string> remove_list;
+
+ for( UBMap::iterator it = updateBehaviors.begin();
+ it != updateBehaviors.end();
+ ++it ) {
+
+ IMUpdateBehavior* updater = it->second;
+
+ // See if this behavior has been initialized
+ if( !updater->initialized ) {
+
+ if( !updater->initialize( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it should not begin remove it
+ remove_list.push_back(it->first);
+ continue;
+ }
+ else {
+ updater->initialized = true;
+ }
+ }
+
+ if( !updater->update( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it is done
+ remove_list.push_back(it->first);
+ }
+ }
+
+ std::vector<std::string>::iterator rmit;
+ for( rmit = remove_list.begin(); rmit != remove_list.end(); rmit++) {
+ removeUpdateBehavior(*rmit);
+ }
+ lastDrawOffset = drawOffset;
+ }
+
+ // Now do mouse behaviors
+
+ // Mouse overs
+
+ if( (scriptMouseOver || (pointInElement( drawOffset, guistate.mousePosition ) || guistate.inheritedMouseOver)) && !pauseBehaviors) {
+ if( mouseOverForChildren ) {
+ guistate.inheritedMouseOver = true;
+ }
+
+ if( !mouseOver ) {
+ mouseOver = true;
+
+ // Update behaviors
+ for( MOBMap::iterator it = mouseOverBehaviors.begin();
+ it != mouseOverBehaviors.end();
+ ++it ) {
+
+ IMMouseOverBehavior* behavior = it->second;
+ behavior->onStart( this, delta, drawOffset, guistate );
+ }
+ }
+ else {
+
+ // Update behaviors
+ for( MOBMap::iterator it = mouseOverBehaviors.begin();
+ it != mouseOverBehaviors.end();
+ ++it ) {
+
+ IMMouseOverBehavior* behavior = it->second;
+ behavior->onContinue( this, delta, drawOffset, guistate );
+ }
+
+ }
+ }
+ else {
+ // See if this is an 'exit'
+ if( mouseOver )
+ {
+ std::vector<std::string> remove_list;
+ for( MOBMap::iterator it = mouseOverBehaviors.begin();
+ it != mouseOverBehaviors.end();
+ ++it ) {
+ IMMouseOverBehavior* behavior = it->second;
+
+ if( !behavior->onFinish( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it is done
+ remove_list.push_back(it->first);
+
+ }
+ }
+ mouseOver = false;
+ std::vector<std::string>::iterator rm_it;
+ for( rm_it = remove_list.begin(); rm_it != remove_list.end(); rm_it++ ) {
+ removeMouseOverBehavior( *rm_it );
+ }
+
+ }
+ }
+
+ if(mouse_clicking && guistate.leftMouseState == IMUIContext::kMouseStillUp){
+ mouse_clicking = false;
+ }
+
+ // Mouse click status
+ if( !pauseBehaviors && (!guistate.clickHandled && ( pointInElement( drawOffset, guistate.mousePosition ) || guistate.inheritedMouseDown )) ) {
+
+ IMUIContext::ButtonState effectiveState = guistate.leftMouseState;
+
+ // See if we have an override from our parent
+ if( guistate.inheritedMouseDown ) {
+ effectiveState = guistate.inheritedMouseState;
+ }
+
+ switch( effectiveState ) {
+
+ case IMUIContext::kMouseDown: {
+
+ if( mouseDownForChildren ) {
+ guistate.inheritedMouseDown = true;
+ }
+
+ mouse_clicking = true;
+
+ std::vector<std::string> remove_list;
+ // Update behaviors
+ for( MCBMap::iterator it = leftMouseClickBehaviors.begin();
+ it != leftMouseClickBehaviors.end();
+ ++it ) {
+
+ guistate.clickHandled = true;
+
+ IMMouseClickBehavior* behavior = it->second;
+ if( !behavior->onDown( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it is done
+ remove_list.push_back(it->first);
+ }
+ }
+
+ std::vector<std::string>::iterator rm_it;
+ for( rm_it = remove_list.begin(); rm_it != remove_list.end(); rm_it++ ) {
+ removeLeftMouseClickBehavior( *rm_it );
+ }
+
+ }
+ break;
+
+ case IMUIContext::kMouseStillDown: {
+
+ if( mouseDownForChildren ) {
+ guistate.inheritedMouseDown = true;
+ }
+
+ std::vector<std::string> remove_list;
+ for( MCBMap::iterator it = leftMouseClickBehaviors.begin();
+ it != leftMouseClickBehaviors.end();
+ ++it ) {
+
+ guistate.clickHandled = true;
+
+ IMMouseClickBehavior* behavior = it->second;
+
+ if( !behavior->onStillDown( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it is done
+ remove_list.push_back(it->first);
+
+ }
+ }
+
+ std::vector<std::string>::iterator rm_it;
+ for( rm_it = remove_list.begin(); rm_it != remove_list.end(); rm_it++ ){
+ removeLeftMouseClickBehavior( *rm_it );
+ }
+
+ }
+ break;
+
+ case IMUIContext::kMouseUp: {
+
+ if(mouse_clicking){
+ for( MCBMap::iterator it = leftMouseClickBehaviors.begin();
+ it != leftMouseClickBehaviors.end();
+ ++it ) {
+
+ guistate.clickHandled = true;
+
+ IMMouseClickBehavior* behavior = it->second;
+
+ if( !behavior->onUp( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it is done
+ removeLeftMouseClickBehavior( it->first );
+ }
+
+ }
+ }
+ mouse_clicking = false;
+
+ // Consider this no longer hovering
+ mouseOver = false;
+ {
+ std::vector<std::string> remove_list;
+ for( MOBMap::iterator it = mouseOverBehaviors.begin();
+ it != mouseOverBehaviors.end();
+ ++it ) {
+
+ guistate.clickHandled = true;
+
+ IMMouseOverBehavior* behavior = it->second;
+ if( !behavior->onFinish( this, delta, drawOffset, guistate ) ) {
+ // If the behavior has indicated it is done
+ remove_list.push_back( it->first );
+
+ }
+ }
+
+ std::vector<std::string>::iterator rm_it;
+ for( rm_it = remove_list.begin(); rm_it != remove_list.end(); rm_it++ ) {
+ removeMouseOverBehavior( *rm_it );
+ }
+ }
+ }
+ break;
+
+ case IMUIContext::kMouseStillUp:
+ default:
+
+ break;
+
+ }
+
+ }
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief When this element is resized, moved, etc propagate this signal upwards
+ *
+ */
+ void IMElement::onRelayout() {
+
+ if( owner != NULL ) {
+ owner->onRelayout();
+ }
+
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief When this element has an error, propagate it upwards
+ *
+ * @param newError Error message
+ *
+ */
+void IMElement::onError( std::string const& newError ) {
+
+ if( owner != NULL ) {
+ owner->reportError( newError );
+ }
+
+ }
+
+
+/*******************************************************************************************/
+/**
+ * @brief When a resize, move, etc has happened do whatever is necessary
+ *
+ */
+ void IMElement::doRelayout() {
+ // Nothing to do in the base class
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Do whatever is necessary when the resolution changes
+ *
+ */
+ void IMElement::doScreenResize() {
+ // Nothing to do in the base class
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Set the name of this element
+ *
+ * @param _name New name (incumbent on the programmer to make sure they're unique)
+ *
+ */
+void IMElement::setName( std::string const& _name ) {
+
+ name = _name;
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the name of this element
+ *
+ * @returns name of this element
+ *
+ */
+std::string IMElement::getName() {
+ return name;
+}
+
+
+/*******************************************************************************************/
+/**
+ * @brief Set the padding for each direction on the element
+ *
+ * UNDEFINEDSIZE will cause no change
+ *
+ * @param U (minimum) Padding between the element and the upper boundary
+ * @param D (minimum) Padding between the element and the lower boundary
+ * @param L (minimum) Padding between the element and the left boundary
+ * @param R (minimum) Padding between the element and the right boundary
+ *
+ */
+void IMElement::setPadding( float U, float D, float L, float R) {
+
+ if( U != UNDEFINEDSIZE ) { paddingU = U; }
+ if( D != UNDEFINEDSIZE ) { paddingD = D; }
+ if( L != UNDEFINEDSIZE ) { paddingL = L; }
+ if( R != UNDEFINEDSIZE ) { paddingR = R; }
+
+ onRelayout();
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Set the padding above the element
+ *
+ * @param paddingSize The number of pixels (in GUI space) for the padding
+ *
+ */
+void IMElement::setPaddingU( float paddingSize ) {
+ paddingU = paddingSize;
+ onRelayout();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Set the padding below the element
+ *
+ * @param paddingSize The number of pixels (in GUI space) for the padding
+ *
+ */
+void IMElement::setPaddingD( float paddingSize ) {
+ paddingD = paddingSize;
+ onRelayout();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Set the padding to the left of the element
+ *
+ * @param paddingSize The number of pixels (in GUI space) for the padding
+ *
+ */
+void IMElement::setPaddingL( float paddingSize ) {
+ paddingL = paddingSize;
+ onRelayout();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Set the padding to the right of the element
+ *
+ * @param paddingSize The number of pixels (in GUI space) for the padding
+ *
+ */
+void IMElement::setPaddingR( float paddingSize ) {
+ paddingR = paddingSize;
+ onRelayout();
+}
+
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the drawing displacement (mostly used for tweening)
+ *
+ * @param newDisplacement newValues for the displacement
+ *
+ */
+ void IMElement::setDisplacement( vec2 newDisplacement ) {
+ drawDisplacement = newDisplacement;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the drawing displacement x component (mostly used for tweening)
+ *
+ * @param newDisplacement newValues for the displacement
+ *
+ */
+ void IMElement::setDisplacementX( float newDisplacement ) {
+ drawDisplacement.x() = newDisplacement;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the drawing displacement y component (mostly used for tweening)
+ *
+ * @param newDisplacement newValues for the displacement
+ *
+ */
+ void IMElement::setDisplacementY( float newDisplacement ) {
+ drawDisplacement.y() = newDisplacement;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the drawing displacement (mostly used for tweening)
+ *
+ * @returns Displacement vector
+ *
+ */
+ vec2 IMElement::getDisplacement( vec2 newDisplacement ) {
+ return drawDisplacement;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the drawing displacement x component (mostly used for tweening)
+ *
+ * @returns Displacement value
+ *
+ */
+ float IMElement::getDisplacementX() {
+ return drawDisplacement.x();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the drawing displacement y component (mostly used for tweening)
+ *
+ * @returns Displacement value
+ *
+ */
+ float IMElement::getDisplacementY() {
+ return drawDisplacement.y();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the default size
+ *
+ * @param newDefault the new default size
+ *
+ */
+void IMElement::setDefaultSize( vec2 newDefault ) {
+
+ defaultSize = newDefault;
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Retrieves the default size
+ *
+ * @returns 2d integer vector of the default size
+ *
+ */
+ vec2 IMElement::getDefaultSize() {
+ return defaultSize;
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief For container type classes - resize event, called internally
+ *
+ */
+ void IMElement::onChildResize( IMElement* child ) {
+ // nothing to do in the base class
+ child->Release();
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief For container type classes - resize event, called internally
+ *
+ */
+
+ void IMElement::onParentResize() {
+ // nothing to do in the base class
+ }
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the size of the region (not including padding)
+ *
+ * @param _size 2d size vector (-1 element implies undefined - or use UNDEFINEDSIZE)
+ *
+ */
+void IMElement::setSize( const vec2 _size ) {
+
+ vec2 oldSize = size;
+
+ size = _size;
+
+ size.x() += paddingL + paddingR;
+ size.y() += paddingU + paddingD;
+
+ // make sure the size has actually changed
+ if( size.x() != oldSize.x() || size.y() != oldSize.y() ) {
+ // Signal that something has changed
+ onRelayout();
+ }
+
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the x dimension of a region
+ *
+ * @param x x dimension size (-1 implies undefined - or use UNDEFINEDSIZE)
+ *
+ */
+void IMElement::setSizeX( const float x ) {
+ setSize( vec2( x, size.y() ) );
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Sets the y dimension of a region
+ *
+ * @param y y dimension size (-1 implies undefined - or use UNDEFINEDSIZE)
+ *
+ */
+void IMElement::setSizeY( const float y ) {
+ setSize( vec2( size.x(), y ) );
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the size vector
+ *
+ * @returns The size vector
+ *
+ */
+vec2 IMElement::getSize() {
+ return size;
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the size x component
+ *
+ * @returns The x size
+ *
+ */
+float IMElement::getSizeX() {
+ return size.x();
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Gets the size y component
+ *
+ * @returns The y size
+ *
+ */
+float IMElement::getSizeY() {
+ return size.y();
+}
+
+
+/*******************************************************************************************/
+/**
+ * @brief Sends a message to the owning GUI
+ *
+ * @param theMessage the message
+ *
+ */
+ void IMElement::sendMessage( IMMessage* theMessage ) {
+ if( owner != NULL ) {
+ // receiveMessage releases the message for us
+ owner->receiveMessage( theMessage );
+ } else {
+ theMessage->Release();
+ }
+ }
+
+
+/*******************************************************************************************/
+/**
+ * @brief Finds an element by a given name
+ *
+ * @param elementName the name of the element
+ *
+ * @returns handle to the element (NULL if not found)
+ *
+ */
+IMElement* IMElement::findElement( std::string const& elementName ) {
+ // Check if this is the droid we're looking for
+ if( name == elementName ) {
+ AddRef();
+ return this;
+ }
+ else {
+ return NULL;
+ }
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Remove all referenced object without releaseing references
+ *
+ */
+void IMElement::clense() {
+ owner = NULL;
+ parent = NULL;
+
+ updateBehaviors.clear();
+ mouseOverBehaviors.clear();
+ leftMouseClickBehaviors.clear();
+}
+
+void IMElement::setPauseBehaviors( bool pause ) {
+ pauseBehaviors = pause;
+}
+
+bool IMElement::isMouseOver() {
+ return mouseOver;
+}
+
+void IMElement::setScriptMouseOver(bool mouseOver) {
+ scriptMouseOver = mouseOver;
+}
+
+/*******************************************************************************************/
+/**
+ * @brief Destructor
+ *
+ */
+IMElement::~IMElement() {
+ LOG_ASSERT(refCount == 0);
+ for( UBMap::iterator it = updateBehaviors.begin();
+ it != updateBehaviors.end();
+ ++it ) {
+ it->second->Release();
+ }
+ updateBehaviors.clear();
+
+ for( MOBMap::iterator it = mouseOverBehaviors.begin();
+ it != mouseOverBehaviors.end();
+ ++it ) {
+ it->second->Release();
+ }
+ mouseOverBehaviors.clear();
+
+ for( MCBMap::iterator it = leftMouseClickBehaviors.begin();
+ it != leftMouseClickBehaviors.end();
+ ++it ) {
+ it->second->Release();
+ }
+ leftMouseClickBehaviors.clear();
+
+ imevents.DeRegisterListener(this);
+ imevents.TriggerDestroyed(this);
+}
+
+void IMElement::DestroyQueuedIMElements() {
+ while (!IMElement::deletion_schedule.empty()) {
+ IMElement* element = IMElement::deletion_schedule[0];
+ if(element != nullptr) {
+ delete element;
+ element = nullptr;
+ }
+
+ IMElement::deletion_schedule.erase(IMElement::deletion_schedule.begin());
+ }
+}
+
+void IMElement::DestroyedIMElement( IMElement* element ) {
+ if( element == parent ) {
+ parent = NULL;
+ }
+}
+
+void IMElement::DestroyedIMGUI( IMGUI* imgui) {
+ if( owner == imgui ) {
+ owner = NULL;
+ }
+}