summaryrefslogtreecommitdiff
path: root/Runtime/IMGUI
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/IMGUI')
-rw-r--r--Runtime/IMGUI/GUIButton.cpp77
-rw-r--r--Runtime/IMGUI/GUIButton.h16
-rw-r--r--Runtime/IMGUI/GUIClip.cpp419
-rw-r--r--Runtime/IMGUI/GUIClip.h107
-rw-r--r--Runtime/IMGUI/GUIContent.cpp58
-rw-r--r--Runtime/IMGUI/GUIContent.h36
-rw-r--r--Runtime/IMGUI/GUIContentTests.cpp28
-rw-r--r--Runtime/IMGUI/GUILabel.cpp29
-rw-r--r--Runtime/IMGUI/GUILabel.h15
-rw-r--r--Runtime/IMGUI/GUIManager.cpp571
-rw-r--r--Runtime/IMGUI/GUIManager.h135
-rw-r--r--Runtime/IMGUI/GUIState.cpp519
-rw-r--r--Runtime/IMGUI/GUIState.h231
-rw-r--r--Runtime/IMGUI/GUIStyle.cpp1154
-rw-r--r--Runtime/IMGUI/GUIStyle.h297
-rw-r--r--Runtime/IMGUI/GUITest.cpp209
-rw-r--r--Runtime/IMGUI/GUIToggle.cpp76
-rw-r--r--Runtime/IMGUI/GUIToggle.h16
-rw-r--r--Runtime/IMGUI/GUIWindows.cpp748
-rw-r--r--Runtime/IMGUI/GUIWindows.h95
-rw-r--r--Runtime/IMGUI/IDList.cpp164
-rw-r--r--Runtime/IMGUI/IDList.h69
-rw-r--r--Runtime/IMGUI/IMGUIUtils.cpp101
-rw-r--r--Runtime/IMGUI/IMGUIUtils.h64
-rw-r--r--Runtime/IMGUI/NamedKeyControlList.cpp27
-rw-r--r--Runtime/IMGUI/NamedKeyControlList.h40
-rw-r--r--Runtime/IMGUI/TextFormatting.cpp337
-rw-r--r--Runtime/IMGUI/TextFormatting.h85
-rw-r--r--Runtime/IMGUI/TextMeshGenerator2.cpp871
-rw-r--r--Runtime/IMGUI/TextMeshGenerator2.h96
-rw-r--r--Runtime/IMGUI/TextUtil.cpp277
-rw-r--r--Runtime/IMGUI/TextUtil.h110
32 files changed, 7077 insertions, 0 deletions
diff --git a/Runtime/IMGUI/GUIButton.cpp b/Runtime/IMGUI/GUIButton.cpp
new file mode 100644
index 0000000..d83462b
--- /dev/null
+++ b/Runtime/IMGUI/GUIButton.cpp
@@ -0,0 +1,77 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+static const int kGUIButtonHash = 2001146706;
+
+namespace IMGUI
+{
+
+bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style, int id)
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+ switch (GetEventTypeForControl (state, evt, id))
+ {
+ case InputEvent::kMouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ GrabMouseControl (state, id);
+ evt.Use ();
+ }
+ break;
+ case InputEvent::kMouseUp:
+ if (HasMouseControl (state, id))
+ {
+ ReleaseMouseControl (state);
+
+ // If we got the mousedown, the mouseup is ours as well
+ // (no matter if the click was in the button or not)
+ evt.Use ();
+
+ // toggle the passed-in value if the mouse was over the button & return true
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ state.m_OnGUIState.m_Changed = true;
+ return true;
+ }
+ }
+ break;
+ case InputEvent::kKeyDown:
+ if (evt.character == 32 && state.m_MultiFrameGUIState.m_KeyboardControl == id)
+ {
+ evt.Use ();
+ state.m_OnGUIState.m_Changed = true;
+ return true;
+ }
+ break;
+ case InputEvent::kMouseDrag:
+ if (HasMouseControl (state, id))
+ evt.Use ();
+ break;
+
+ case InputEvent::kRepaint:
+ style.Draw (state, position, content, id, false);
+ break;
+ }
+ return false;
+}
+
+bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style)
+{
+ int id = GetControlID (state, kGUIButtonHash, kNative, position);
+ return GUIButton (state, position, content, style, id);
+}
+
+
+}
diff --git a/Runtime/IMGUI/GUIButton.h b/Runtime/IMGUI/GUIButton.h
new file mode 100644
index 0000000..29b2404
--- /dev/null
+++ b/Runtime/IMGUI/GUIButton.h
@@ -0,0 +1,16 @@
+#ifndef GUIBUTTON_H
+#define GUIBUTTON_H
+
+#include "Runtime/Math/Rect.h"
+
+struct GUIState;
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style, int id);
+ bool GUIButton (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style);
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIClip.cpp b/Runtime/IMGUI/GUIClip.cpp
new file mode 100644
index 0000000..203c1e9
--- /dev/null
+++ b/Runtime/IMGUI/GUIClip.cpp
@@ -0,0 +1,419 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIClip.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Math/Quaternion.h"
+
+static const float ko1 = -10000, ko2 = 10000, ko3= 0, ko4 = 0;
+
+GUIClipState::GUIClipState()
+{
+ m_Enabled = 0;
+}
+
+GUIClipState::~GUIClipState ()
+{
+}
+
+
+/// Push a clip rect to the stack with pixel offsets.
+/// This is the low-level function for doing clipping rectangles. Unless you're working with embedded
+/// temporary render buffers, this is most likely not what you want.
+/// /absoluteRect/ is the absolute device coordinates this element will be mapped to.
+/// /scrollOffset/ is a scrolling offset to apply.
+/// /renderOffset/ is the rendering offset of the absoluteRect from source to destination. Used to map from an on-screen rectangle to a
+/// destination inside a render buffer.
+void GUIClipState::Push (InputEvent& event, const Rectf& screenRect, Vector2f scrollOffset, const Vector2f& renderOffset, bool resetOffset)
+{
+ if (m_GUIClips.empty())
+ {
+ ErrorString("GUIClip pushing empty stack not allowed.");
+ return;
+ }
+
+ GUIClip& topmost = m_GUIClips.back ();
+ // build absolute offsets by adding parent's position & scroll to the screenRect's positions
+ float physicalxMin = screenRect.x + topmost.physicalRect.x + topmost.scrollOffset.x;
+ float physicalxMax = screenRect.GetXMax() + topmost.physicalRect.x + topmost.scrollOffset.x;
+ float physicalyMin = screenRect.y + topmost.physicalRect.y + topmost.scrollOffset.y;
+ float physicalyMax = screenRect.GetYMax() + topmost.physicalRect.y + topmost.scrollOffset.y;
+
+ // If the user tries to push a GUIClip with an xMin that goes outside the parent's clipping, we cannot allow that
+ // so we move the xMin in and use scrollOffset to hide this.
+ if (physicalxMin < topmost.physicalRect.x)
+ {
+ scrollOffset.x += physicalxMin - topmost.physicalRect.x;
+ physicalxMin = topmost.physicalRect.x;
+ }
+
+ // Clip top side (yMin) to the parent as well.
+ if (physicalyMin < topmost.physicalRect.y)
+ {
+ scrollOffset.y += physicalyMin - topmost.physicalRect.y;
+ physicalyMin = topmost.physicalRect.y;
+ }
+
+ // Clip right side (xMax) as well.
+ if (physicalxMax > topmost.physicalRect.GetXMax())
+ {
+ physicalxMax = topmost.physicalRect.GetXMax();
+ }
+
+ // Clip bottom side (yMax) as well.
+ if (physicalyMax > topmost.physicalRect.GetYMax())
+ {
+ physicalyMax = topmost.physicalRect.GetYMax();
+ }
+
+ // if the new GUIClip is completely outside parent, sizes can get negative.
+ // We just make them be 0, so no rendering is performed.
+ if (physicalxMax <= physicalxMin)
+ physicalxMax = physicalxMin;
+ if (physicalyMax <= physicalyMin)
+ physicalyMax = physicalyMin;
+
+ // Build the rect straight away
+ Rectf absoluteRect = MinMaxRect (physicalxMin, physicalyMin, physicalxMax, physicalyMax);
+
+ if (!resetOffset)
+ {
+ // Maintian global render offset
+ m_GUIClips.push_back(GUIClip (screenRect, absoluteRect, scrollOffset, topmost.renderOffset + renderOffset, topmost.globalScrollOffset + scrollOffset));
+ }
+ else
+ {
+ // Maintian global scroll offset
+ m_GUIClips.push_back (GUIClip (screenRect, absoluteRect, scrollOffset,
+ Vector2f(absoluteRect.x + scrollOffset.x + renderOffset.x, absoluteRect.y + scrollOffset.y + renderOffset.y),
+ topmost.globalScrollOffset + scrollOffset));
+ }
+
+ Apply(event, m_GUIClips.back());
+}
+
+/// Removes the topmost clipping rectangle, undoing the effect of the latest GUIClip.Push
+void GUIClipState::Pop (InputEvent& event)
+{
+ if (m_GUIClips.size() < 2)
+ {
+ ErrorString("Invalid GUIClip stack popping");
+ return;
+ }
+
+ m_GUIClips.pop_back();
+ Apply(event, m_GUIClips.back());
+}
+
+Vector2f GUIClipState::Unclip (const Vector2f& pos)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+ Vector3f res;
+ m_Matrix.PerspectiveMultiplyPoint3 (Vector3f (pos.x, pos.y, 0.0F), res);
+ return Vector2f(res.x, res.y) + topmost.scrollOffset + Vector2f (topmost.physicalRect.x, topmost.physicalRect.y);
+ }
+ else
+ {
+ return Vector2f (0,0);
+ }
+}
+
+Rectf GUIClipState::Unclip (const Rectf& rect)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+
+ return Rectf (rect.x + topmost.scrollOffset.x + topmost.physicalRect.x,
+ rect.y + topmost.scrollOffset.y + topmost.physicalRect.y,
+ rect.width, rect.height);
+ }
+ else
+ {
+ return Rectf (0,0,0,0);
+ }
+}
+
+/// Clips /absolutePos/ to drawing coordinates
+/// Used for reconverting values calculated from ::ref::Unclip
+Vector2f GUIClipState::Clip (const Vector2f& absolutePos)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+
+ Vector3f transformedPoint;
+ m_InverseMatrix.PerspectiveMultiplyPoint3(Vector3f(absolutePos.x, absolutePos.y, 0.0F), transformedPoint);
+
+ // return (Vector2)s_InverseMatrix.MultiplyPoint (absolutePos) - topmost.globalScrollOffset - new Vector2 (topmost.physicalRect.x, topmost.physicalRect.y);
+ Vector2f res = Vector2f(transformedPoint.x, transformedPoint.y) - topmost.scrollOffset - Vector2f (topmost.physicalRect.x, topmost.physicalRect.y);
+ return Vector2f(res.x, res.y);
+ }
+ else
+ {
+ return Vector2f (0,0);
+ }
+}
+
+/// Convert /absoluteRect/ to drawing coordinates
+/// Used for reconverting values calculated from ::ref::Unclip
+Rectf GUIClipState::Clip (const Rectf& absoluteRect)
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+
+ return Rectf (absoluteRect.x - topmost.globalScrollOffset.x - topmost.physicalRect.x,
+ absoluteRect.y - topmost.globalScrollOffset.y - topmost.physicalRect.y,
+ absoluteRect.width, absoluteRect.height);
+ }
+ else
+ {
+ return Rectf (0,0,0,0);
+ }
+}
+
+// Return the rect for the topmost clip in screen space
+Rectf GUIClipState::GetTopRect ()
+{
+ if (!m_GUIClips.empty())
+ {
+ GUIClip& topmost = m_GUIClips.back();
+ return topmost.screenRect;
+ }
+ else
+ {
+ return Rectf (0,0,0,0);
+ }
+}
+
+
+/// Reapply the clipping info.
+/// Call this after switching render buffers.
+void GUIClipState::Reapply (InputEvent& event)
+{
+ if (!m_GUIClips.empty())
+ Apply (event, m_GUIClips.back());
+}
+
+
+void GUIClipState::SetMatrix (InputEvent& event, const Matrix4x4f& m)
+{
+ m_Matrix = m;
+
+ Matrix4x4f inverse;
+ bool success = Matrix4x4f::Invert_Full(m, inverse);
+ if (!success)
+ {
+ ErrorString ("Ignoring invalid matrix assinged to GUI.matrix - the matrix needs to be invertible. Did you scale by 0 on Z-axis?");
+ return;
+ }
+
+ m_Matrix = m; // Store the value
+ m_InverseMatrix = inverse;
+ Reapply (event); // Reapply the toplevel cliprect.
+}
+
+/// constructor
+GUIClip::GUIClip (const Rectf& iscreenRect, const Rectf& iphysicalRect, const Vector2f& iscrollOffset, const Vector2f& irenderOffset, const Vector2f& iglobalScrollOffset)
+{
+ // Debug.Log ("GUIClipping: " + physicalRect + scrollOffset + renderOffset + globalScrollOffset);
+
+ screenRect = iscreenRect;
+ physicalRect = iphysicalRect;
+ scrollOffset = iscrollOffset;
+ renderOffset = irenderOffset;
+ globalScrollOffset = iglobalScrollOffset;
+}
+
+// Recalculate the mouse values from the absolute screen position into local GUI coordinates, taking cliprects & all into account.
+void GUIClipState::CalculateMouseValues (InputEvent& event)
+{
+ if (!m_GUIClips.empty())
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ event.touch.pos = Clip (m_AbsoluteMousePosition);
+#else
+ event.mousePosition = Clip (m_AbsoluteMousePosition);
+#endif
+
+ // Check if we're outside the cliprect & set the event ignore flag
+ Vector3f res;
+ m_InverseMatrix.PerspectiveMultiplyPoint3 (Vector3f(m_AbsoluteMousePosition.x, m_AbsoluteMousePosition.y, 0.0F), res);
+
+ GUIClip& topmost = m_GUIClips.back();
+ m_Enabled = topmost.physicalRect.Contains (res.x, res.y) ? -1 : 0;
+
+ // scrollwheel is a specialcase
+ if (event.type != InputEvent::kScrollWheel)
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ event.touch.deltaPos = event.touch.pos - Clip (m_AbsoluteLastMousePosition);
+#else
+ event.delta = event.mousePosition - Clip (m_AbsoluteLastMousePosition);
+#endif
+ }
+ }
+}
+
+/// Apply the current clip rect to OpenGL's viewport & scissor rects.
+void GUIClipState::Apply (InputEvent& event, GUIClip &topmost)
+{
+ // Warp the current event to the correct place in the new coordinate space
+
+ CalculateMouseValues (event);
+
+ m_VisibleRect = Rectf (-topmost.scrollOffset.x, -topmost.scrollOffset.y, topmost.physicalRect.width, topmost.physicalRect.height);
+
+ // From here on out, we're only setting up OpenGL, so abort if we're not repainting
+ if (event.type != InputEvent::kRepaint)
+ return;
+
+ // Calculate the viewport (where we end up on screen)
+ Rectf r = topmost.physicalRect;
+
+ if (r.width < 0) r.width = 0;
+ if (r.height < 0) r.height = 0;
+
+ r.x -= topmost.renderOffset.x;
+ r.y -= topmost.renderOffset.y;
+
+
+ r.x = RoundfToInt(r.x);
+ r.y = RoundfToInt (r.y);
+ r.width = RoundfToIntPos (r.width);
+ r.height = RoundfToIntPos (r.height);
+
+ Matrix4x4f viewportMatrix;
+ viewportMatrix.SetIdentity();
+
+ float width, height;
+ RenderTexture *rTex = RenderTexture::GetActive ();
+ if (rTex)
+ {
+ width = rTex->GetWidth ();
+ height = rTex->GetHeight ();
+ }
+ else
+ {
+ width = GetScreenManager ().GetWidth ();
+ height = GetScreenManager ().GetHeight ();
+ }
+
+ Vector3f scaleFac = Vector3f (r.width / width, r.height / height,1);
+ Vector3f move;
+ m_Matrix.PerspectiveMultiplyPoint3 (Vector3f (r.x, r.y,0), move);
+
+ ///@TODO: OPTIMIZE non rotation
+ viewportMatrix.SetTRS(Vector3f(move.x * scaleFac.x, move.y * scaleFac.y, 0.0F), Quaternionf::identity(), scaleFac);
+
+ SetGLViewport (Rectf (0,0, width, height));
+
+ // The ortho bounds passed to LoadPixelMatrix gets multiplied by the matrix as well
+ // We need to counter that for the scroll offsets - so stuff like scale doesn't apply to scrolling
+ Vector3f sOffset;
+ m_Matrix.PerspectiveMultiplyPoint3 (Vector3f(-topmost.scrollOffset.x, -topmost.scrollOffset.y, 0.0F), sOffset);
+ sOffset.x *= scaleFac.x;
+ sOffset.y *= scaleFac.y;
+
+ // Scale X & Y
+ // Upload the client coordinate system.
+ // Here we multiply in the scaleFac on the scrollOffsets - its something with Matrix ordering - a case of "iterative debugging": I kept chanigng it (at random) until it worked...
+ Matrix4x4f pixelMatrix;
+ MultiplyMatrices4x4 (&viewportMatrix, &m_Matrix, &pixelMatrix);
+ LoadPixelMatrix(
+ sOffset.x, Roundf (topmost.physicalRect.width) + sOffset.x,
+ Roundf (topmost.physicalRect.height) + sOffset.y, sOffset.y,
+ pixelMatrix
+ );
+ GUIStyle::SetGUIClipRect(m_VisibleRect);
+}
+
+/// Load the pixel matrix and also multiply in a GUIMatrix
+void GUIClipState::LoadPixelMatrix(float left, float right, float bottom, float top, const Matrix4x4f& mat)
+{
+ Rectf rect( left, bottom, right-left, top-bottom );
+ Matrix4x4f matrix;
+ CalcPixelMatrix (rect, matrix);
+
+ // Important: apply half-texel offsets after multiplying the matrix!
+ matrix *= mat;
+ ApplyTexelOffsetsToPixelMatrix( true, matrix );
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetProjectionMatrix(matrix);
+ device.SetViewMatrix (Matrix4x4f::identity.GetPtr()); // implicitly sets world to identity
+}
+
+Rectf GUIClipState::GetTopMostPhysicalRect ()
+{
+ return m_GUIClips.back().physicalRect;
+}
+
+//////@TODO:
+// CSRAW public override string ToString () {
+// return System.String.Format ("GUIClip: physicalRect {0}, scrollOffset {1}, renderOffset {2}, globalScrollOffset{3}", physicalRect, scrollOffset, renderOffset, globalScrollOffset);
+// }
+
+/// Set up the clip rect to contain the entire display.
+/// called by GUI.Begin to initialize the view.
+void GUIClipState::BeginOnGUI (InputEvent& event)
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ m_AbsoluteMousePosition = event.touch.pos;
+ m_AbsoluteLastMousePosition = m_AbsoluteMousePosition - event.touch.deltaPos;
+#else
+ m_AbsoluteMousePosition = event.mousePosition;
+ m_AbsoluteLastMousePosition = m_AbsoluteMousePosition - event.delta;
+#endif
+ m_Matrix.SetIdentity();
+ m_InverseMatrix.SetIdentity();
+
+ m_GUIClips.resize (0);
+ // Push in a really large screen, so GUI.matrix scales doesn't crop the root level.
+ m_GUIClips.push_back (GUIClip (Rectf (ko1, ko1, 40000,40000), Rectf (ko1, ko1, 40000,40000), Vector2f (ko2, ko2), Vector2f (ko3,ko3), Vector2f (ko4,ko4)));
+
+ Apply (event, m_GUIClips.back());
+}
+
+void GUIClipState::SetAbsoluteMousePosition (const Vector2f& val)
+{
+ m_AbsoluteMousePosition = val;
+}
+
+/// *End the current GUI stuff.
+void GUIClipState::EndOnGUI (InputEvent &event)
+{
+ InputEvent::Type eventType = event.type;
+ if (m_GUIClips.size() != 1 && eventType != InputEvent::kIgnore && eventType != InputEvent::kUsed)
+ {
+ if (m_GUIClips.size() > 1)
+ {
+ ErrorString ("GUI Error: You are pushing more GUIClips than you are popping. Make sure they are balanced)");
+ }
+ else
+ {
+ ErrorString ("GUI Error: You are popping more GUIClips than you are pushing. Make sure they are balanced)");
+ return;
+ }
+ }
+ m_GUIClips.pop_back();
+ // Make sure we restore the mouse values. Otherwise things like GUi.Matrix's modifications to
+ // MouseEvents will seep into the next OnGUI
+#if ENABLE_NEW_EVENT_SYSTEM
+ event.touch.deltaPos = m_AbsoluteMousePosition - m_AbsoluteLastMousePosition;
+ event.touch.pos = m_AbsoluteMousePosition;
+#else
+ event.delta = m_AbsoluteMousePosition - m_AbsoluteLastMousePosition;
+ event.mousePosition = m_AbsoluteMousePosition;
+#endif
+}
+
+void GUIClipState::EndThroughException ()
+{
+ m_GUIClips.resize(0);
+}
diff --git a/Runtime/IMGUI/GUIClip.h b/Runtime/IMGUI/GUIClip.h
new file mode 100644
index 0000000..3ef5e05
--- /dev/null
+++ b/Runtime/IMGUI/GUIClip.h
@@ -0,0 +1,107 @@
+#pragma once
+
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Misc/InputEvent.h"
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+class GUIClip
+{
+ public:
+
+ GUIClip () {}
+
+
+ /// The rectangle of this clipping rect.
+ /// This is stored absolute coordinates
+ Rectf physicalRect;
+
+ // The rectangle of the clip in screen space
+ Rectf screenRect;
+
+ /// physical scrolling offset for coordinates - this is relative to parent.
+ Vector2f scrollOffset;
+ /// This is absolute
+ Vector2f globalScrollOffset;
+
+ /// rendering offset. This is the global GUIClip->buffer coordinates
+ Vector2f renderOffset;
+
+ /// constructor
+ GUIClip (const Rectf& iscreenRect, const Rectf& iphysicalRect, const Vector2f& iscrollOffset, const Vector2f& irenderOffset, const Vector2f& iglobalScrollOffset);
+};
+
+// MUST BYTE-MATCH CORRESPONDING STRUCT IN GUISTATEMONO
+struct GUIClipState
+{
+ typedef std::vector<GUIClip> ClipStack;
+ ClipStack m_GUIClips;
+
+ private:
+ Matrix4x4f m_Matrix;
+ Matrix4x4f m_InverseMatrix;
+
+ // Where is the mouse onscreen, and where was it last frame (so we can calculate deltas correctly)
+ Vector2f m_AbsoluteMousePosition;
+ Vector2f m_AbsoluteLastMousePosition;
+
+ Rectf m_VisibleRect;
+
+ // Should we disable events?
+ int m_Enabled;
+
+ public:
+ GUIClipState ();
+ ~GUIClipState ();
+
+ Rectf GetTopMostPhysicalRect ();
+
+ /// Set up the clip rect to contain the entire display.
+ /// called by GUI.Begin to initialize the view.
+ void BeginOnGUI (InputEvent& ievent);
+ void EndOnGUI (InputEvent& ievent);
+ void EndThroughException ();
+
+ /// This is the low-level function for doing clipping rectangles. Unless you're working with embedded temporary render buffers, this is most likely not what you want.
+ /// /absoluteRect/ is the absolute device coordinates this element will be mapped to.
+ /// /scrollOffset/ is a scrolling offset to apply.
+ /// /renderOffset/ is the rendering offset of the absoluteRect from source to destination. Used to map from an on-screen rectangle to a destination inside a render buffer.
+ void Push (InputEvent& ievent, const Rectf& screenRect, Vector2f scrollOffset, const Vector2f& renderOffset, bool resetOffset);
+ /// Removes the topmost clipping rectangle, undoing the effect of the latest GUIClip.Push
+ void Pop (InputEvent& ievent);
+
+ /// Unclips /pos/ to physical device coordinates.
+ Vector2f Unclip (const Vector2f& pos);
+ Rectf Unclip (const Rectf& rect);
+
+ /// Clips /absolutePos/ to drawing coordinates
+ Vector2f Clip (const Vector2f& absolutePos);
+ Rectf Clip (const Rectf& absoluteRect);
+
+ // Return the rect for the topmost clip in screen space
+ Rectf GetTopRect();
+
+
+ /// Reapply the clipping info. Call this after switching render buffers.
+ void Reapply (InputEvent& ievent);
+ // Set the GUIMatrix. This is here as this class handles all coordinate transforms anyways.
+ const Matrix4x4f& GetMatrix () { return m_Matrix; }
+ void SetMatrix (InputEvent& ievent, const Matrix4x4f& m);
+
+ Vector2f GetAbsoluteMousePosition () { return m_AbsoluteMousePosition; }
+ Rectf GetVisibleRect () { return m_VisibleRect; }
+
+ bool GetEnabled () const { return m_Enabled; }
+ private:
+ // Recalculate the mouse values from the absolute screen position into local GUI coordinates, taking cliprects & all into account.
+ void CalculateMouseValues (InputEvent& ievent);
+ static void LoadPixelMatrix(float left, float right, float bottom, float top, const Matrix4x4f& mat);
+ void SetAbsoluteMousePosition (const Vector2f& absoluteMousePosition);
+
+
+ // Apply the current clip rect to event pointer positions & render settings for culling, etc.
+ void Apply (InputEvent& ievent, GUIClip &topmost);
+};
diff --git a/Runtime/IMGUI/GUIContent.cpp b/Runtime/IMGUI/GUIContent.cpp
new file mode 100644
index 0000000..6b811c7
--- /dev/null
+++ b/Runtime/IMGUI/GUIContent.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if ENABLE_SCRIPTING
+static GUIContent s_TempGUIContent;
+#if UNITY_WINRT
+void MonoGUIContentToTempNativeCallback(Platform::String^ text, Platform::String^ tooltip, long long image)
+{
+ s_TempGUIContent.m_Text.BorrowString (text);
+ s_TempGUIContent.m_Tooltip.BorrowString (tooltip);
+ s_TempGUIContent.m_Image = ScriptingObjectToObject<Texture> (ScriptingObjectPtr(image));
+}
+BridgeInterface::ScriptingGUIContentToTempNativeDelegateGC^ GetMonoGUIContentToTempNativeCallback()
+{
+ static BridgeInterface::ScriptingGUIContentToTempNativeDelegateGC^ s_Callback = ref new BridgeInterface::ScriptingGUIContentToTempNativeDelegateGC(MonoGUIContentToTempNativeCallback);
+ return s_Callback;
+}
+
+#endif
+
+GUIContent &MonoGUIContentToTempNative (ScriptingObjectPtr scriptingContent)
+{
+ MonoGUIContentToNative (scriptingContent, s_TempGUIContent);
+ return s_TempGUIContent;
+}
+
+void MonoGUIContentToNative (ScriptingObjectPtr scriptingContent, GUIContent& cppContent)
+{
+ if (scriptingContent == SCRIPTING_NULL)
+ {
+ WarningString("GUIContent is null. Use GUIContent.none.");
+ cppContent.m_Text = (UTF16String) "";
+ cppContent.m_Tooltip = (UTF16String) "";
+ cppContent.m_Image = NULL;
+
+ return;
+ }
+
+#if UNITY_WINRT
+ GetWinRTUtils()->ScriptingGUIContentToTempNativeGC(scriptingContent.GetHandle(), GetMonoGUIContentToTempNativeCallback());
+#else
+ MonoGUIContent nativeContent;
+ MarshallManagedStructIntoNative(scriptingContent, &nativeContent);
+
+# if ENABLE_MONO
+ cppContent.m_Text.BorrowString (nativeContent.m_Text);
+ cppContent.m_Tooltip.BorrowString (nativeContent.m_Tooltip);
+# elif UNITY_FLASH
+ Ext_WriteTextAndToolTipIntoUTF16Strings(scriptingContent,&cppContent.m_Text,&cppContent.m_Tooltip);
+# endif
+
+ cppContent.m_Image = ScriptingObjectToObject<Texture> (nativeContent.m_Image);
+#endif
+ return;
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIContent.h b/Runtime/IMGUI/GUIContent.h
new file mode 100644
index 0000000..5570bf8
--- /dev/null
+++ b/Runtime/IMGUI/GUIContent.h
@@ -0,0 +1,36 @@
+#ifndef GUIContent_H
+#define GUIContent_H
+
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Misc/UTF8.h"
+
+struct GUIContent
+{
+ UTF16String m_Text;
+ UTF16String m_Tooltip;
+ PPtr<Texture> m_Image;
+
+ void operator = (const GUIContent& other)
+ {
+ m_Text.CopyString(other.m_Text);
+ m_Tooltip.CopyString(other.m_Tooltip);
+ m_Image = other.m_Image;
+ }
+};
+
+#if ENABLE_SCRIPTING
+
+struct MonoGUIContent
+{
+ ScriptingStringPtr m_Text;
+ ScriptingObjectPtr m_Image;
+ ScriptingStringPtr m_Tooltip;
+};
+
+GUIContent &MonoGUIContentToTempNative (ScriptingObjectPtr monoContent);
+void MonoGUIContentToNative (ScriptingObjectPtr monoContent, GUIContent& cppContent);
+#endif
+
+#endif
diff --git a/Runtime/IMGUI/GUIContentTests.cpp b/Runtime/IMGUI/GUIContentTests.cpp
new file mode 100644
index 0000000..4f45a64
--- /dev/null
+++ b/Runtime/IMGUI/GUIContentTests.cpp
@@ -0,0 +1,28 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/Testing/TestFixtures.h"
+#include "Runtime/Testing/Testing.h"
+
+SUITE (GUIContentTests)
+{
+ typedef TestFixtureBase Fixture;
+
+ TEST_FIXTURE (Fixture, NullContentDoesNotCrash)
+ {
+ //Set expectations.
+ EXPECT (Warning, "GUIContent is null. Use GUIContent.none.");
+
+ // Do.
+ GUIContent content = MonoGUIContentToTempNative (SCRIPTING_NULL);
+
+ // Assert.
+ CHECK ((UTF16String)"" == content.m_Text);
+ CHECK ((UTF16String)"" == content.m_Tooltip);
+ CHECK (content.m_Image.IsNull());
+ }
+}
+#endif
diff --git a/Runtime/IMGUI/GUILabel.cpp b/Runtime/IMGUI/GUILabel.cpp
new file mode 100644
index 0000000..a483a27
--- /dev/null
+++ b/Runtime/IMGUI/GUILabel.cpp
@@ -0,0 +1,29 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+namespace IMGUI
+{
+void GUILabel (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style)
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+
+ if (evt.type == InputEvent::kRepaint)
+ {
+ style.Draw (state, position, content, false, false, false, false);
+
+ // Is inside label AND inside guiclip visible rect (prevents tooltips on labels that are clipped)
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (content.m_Tooltip.length != 0 && position.Contains (evt.touch.pos) &&
+ state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect().Contains(evt.touch.pos))
+#else
+ if (content.m_Tooltip.length != 0 && position.Contains (evt.mousePosition) &&
+ state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect().Contains(evt.mousePosition))
+#endif
+ GUIStyle::SetMouseTooltip (state, content.m_Tooltip, position);
+ }
+}
+
+} // namespace
diff --git a/Runtime/IMGUI/GUILabel.h b/Runtime/IMGUI/GUILabel.h
new file mode 100644
index 0000000..61b8d56
--- /dev/null
+++ b/Runtime/IMGUI/GUILabel.h
@@ -0,0 +1,15 @@
+#ifndef GUILABEL_H
+#define GUILABEL_H
+
+#include "Runtime/Math/Rect.h"
+
+struct GUIState;
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ void GUILabel (GUIState &state, const Rectf &position, GUIContent &content, GUIStyle &style);
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIManager.cpp b/Runtime/IMGUI/GUIManager.cpp
new file mode 100644
index 0000000..6270421
--- /dev/null
+++ b/Runtime/IMGUI/GUIManager.cpp
@@ -0,0 +1,571 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "GUIManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include <fstream>
+#include "Runtime/Misc/ReproductionLog.h"
+#endif
+
+#if ENABLE_UNITYGUI
+static GUIManager* s_GUIManager = NULL;
+
+void InitGUIManager ()
+{
+ AssertIf(s_GUIManager != NULL);
+ s_GUIManager = new GUIManager();
+
+ InitGUIState ();
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ InitializeDeveloperConsole ();
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+}
+
+void CleanupGUIManager ()
+{
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ CleanupDeveloperConsole();
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+ CleanupGUIState ();
+
+ AssertIf(s_GUIManager == NULL);
+ delete s_GUIManager;
+ s_GUIManager = NULL;
+}
+
+GUIManager &GetGUIManager () {
+ AssertIf(s_GUIManager == NULL);
+ return *s_GUIManager;
+}
+
+#if UNITY_EDITOR
+void GUIManager::Reset ()
+{
+ m_MasterState.Reset ();
+}
+#endif
+
+GUIManager::GUIManager () :
+ m_GUIPixelOffset (0.0f,0.0f)
+{
+ m_LastInputEventTime = 0.0f;
+ m_DidGUIWindowsEatLastEvent = false;
+ m_MouseUsed = false;
+ m_mouseButtonsDown = 0;
+}
+
+void GUIManager::AddGUIScript (MonoBehaviourListNode& beh)
+{
+ m_GUIScripts.push_back(beh);
+}
+
+PROFILER_INFORMATION(gGUIRepaintProfile, "GUI.Repaint", kProfilerGUI)
+PROFILER_INFORMATION(gGUIEventProfile, "GUI.ProcessEvents", kProfilerGUI)
+
+void GUIManager::Repaint () {
+ GetInputManager().SetTextFieldInput(false);
+
+ InputEvent ie;
+ ie = m_LastEvent;
+ ie.type = InputEvent::kRepaint;
+
+ DoGUIEvent(ie, false);
+}
+
+bool GUIManager::AnyMouseButtonsDown()
+{
+ return GetGUIManager().m_mouseButtonsDown != 0;
+}
+
+#if UNITY_EDITOR
+void GUIManager::SetEditorGUIInfo (Vector2f guiPixelOffset) {
+ m_GUIPixelOffset = guiPixelOffset;
+ if (MONO_COMMON.setViewInfo) {
+ ScriptingInvocation invocation(MONO_COMMON.setViewInfo);
+ invocation.AddStruct(&m_GUIPixelOffset);
+ invocation.Invoke();
+ }
+}
+
+Vector2f GUIManager::GetEditorGUIInfo() const
+{
+ return m_GUIPixelOffset;
+}
+#endif
+
+void GUIManager::SendQueuedEvents ()
+{
+ #if UNITY_EDITOR
+ // Inside editor: make the Game GUI behave like it does at runtime.
+ if (MONO_COMMON.setViewInfo) {
+ Vector2f screenPosition (0,0);
+
+ ScriptingInvocation invocation(MONO_COMMON.setViewInfo);
+ invocation.AddStruct(&screenPosition);
+ invocation.Invoke();
+ }
+ #endif
+
+ while (!m_Events.empty())
+ {
+ DoGUIEvent(m_Events.front(), true);
+ m_Events.pop_front();
+ }
+}
+
+bool GUIManager::GetDidGUIWindowsEatLastEvent () {
+ return GetGUIManager().m_DidGUIWindowsEatLastEvent;
+}
+
+void GUIManager::SetDidGUIWindowsEatLastEvent (bool value) {
+ GetGUIManager().m_DidGUIWindowsEatLastEvent = value;
+}
+
+struct OldSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth < rhs.depth; }
+};
+
+struct NewSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth > rhs.depth; }
+};
+
+
+
+static bool MonoBehaviourDoGUI(void* beh, MonoBehaviour::GUILayoutType layoutType, int skin)
+{
+ Assert( NULL != beh );
+ return reinterpret_cast<MonoBehaviour *>(beh)->DoGUI(layoutType, skin);
+}
+
+static ObjectGUIState& MonoBehaviourGetObjectGUIState(void* beh)
+{
+ Assert( NULL != beh );
+ return reinterpret_cast<MonoBehaviour *>(beh)->GetObjectGUIState();
+}
+
+GUIManager::GUIObjectWrapper::GUIObjectWrapper(MonoBehaviour* beh)
+ : wrapped_ptr(beh)
+ , do_gui_func(&MonoBehaviourDoGUI)
+ , get_gui_state_func(&MonoBehaviourGetObjectGUIState)
+{}
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+static bool DeveloperConsoleDoGUI(void* dev, MonoBehaviour::GUILayoutType layoutType, int skin)
+{
+ Assert( NULL != dev );
+ return reinterpret_cast<DeveloperConsole *>(dev)->DoGUI();
+}
+
+static ObjectGUIState& DeveloperConsoleGetObjectGUIState(void* dev)
+{
+ Assert( NULL != dev );
+ return reinterpret_cast<DeveloperConsole *>(dev)->GetObjectGUIState();
+}
+
+GUIManager::GUIObjectWrapper::GUIObjectWrapper(DeveloperConsole* dev)
+ : wrapped_ptr(dev)
+ , do_gui_func(&DeveloperConsoleDoGUI)
+ , get_gui_state_func(&DeveloperConsoleGetObjectGUIState)
+{}
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+
+void GUIManager::DoGUIEvent (InputEvent &eventToSend, bool frontToBack)
+{
+ #if ENABLE_PROFILER
+ ProfilerInformation* information = &gGUIEventProfile;
+ if (eventToSend.type == InputEvent::kRepaint)
+ information = &gGUIRepaintProfile;
+
+ PROFILER_AUTO(*information, NULL)
+ #endif
+
+ GUIState &state = GetGUIState();
+ state.SetEvent (eventToSend);
+ InputEvent &e = *state.m_CurrentEvent;
+
+ m_MasterState.LoadIntoGUIState (state);
+ state.BeginFrame ();
+ MonoBehaviour* authorizationDialog = GetUserAuthorizationManager().GetAuthorizationDialog();
+
+ // Update the lists of which scripts we _actually_ want to execute.
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ if (m_GUIScripts.empty() && authorizationDialog == NULL && !GetDeveloperConsole().IsVisible())
+#else
+ if (m_GUIScripts.empty() && authorizationDialog == NULL)
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+ {
+ m_MouseUsed = false;
+ return;
+ }
+
+ // Move the event mouse position away if the screen is locked. We don't want the cursor to interact
+ // with the GUI when it is in an arbitrary, fixed position.
+ if (GetScreenManager().GetLockCursor()
+#if UNITY_METRO
+ && e.touchType == InputEvent::kMouseTouch
+#endif
+ )
+ {
+#if ENABLE_NEW_EVENT_SYSTEM
+ e.touch.pos = Vector2f (-10000, -10000);
+#else
+ e.mousePosition = Vector2f (-10000, -10000);
+#endif
+ }
+
+ // ok - first we send them the layout event and find out the layering
+ InputEvent::Type originalType = e.type;
+
+ int handleTab = 0;
+ if (e.type == InputEvent::kKeyDown && (e.character == '\t' || e.character == 25))
+ handleTab = ((e.modifiers & InputEvent::kShift) == 0) ? 1 : -1;
+
+ // Execute all the no-layout scripts
+ bool eventUsed = false;
+ std::vector<GUIObjectWrapper> layoutedScripts;
+ if (authorizationDialog)
+ {
+ layoutedScripts.push_back (GUIObjectWrapper(authorizationDialog));
+ }
+ else
+ {
+ layoutedScripts.reserve(m_GUIScripts.size_slow());
+ SafeIterator<MonoBehaviourList> guiScriptIterator (m_GUIScripts);
+ while (guiScriptIterator.Next())
+ {
+ MonoBehaviour& beh = **guiScriptIterator;
+
+ if (beh.GetUseGUILayout())
+ layoutedScripts.push_back(GUIObjectWrapper(&beh));
+ else
+ {
+ if (!eventUsed)
+ eventUsed |= beh.DoGUI (MonoBehaviour::kNoLayout, 0);
+ }
+ }
+ }
+ // TODO post 3.5: We can't bail here - nolayout scripts need tab handling as well
+#if UNITY_HAS_DEVELOPER_CONSOLE
+ layoutedScripts.push_back(GUIObjectWrapper(&GetDeveloperConsole()));
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+ int current = 1;
+ bool hasModalWindow = state.m_MultiFrameGUIState.m_Windows != NULL && state.m_MultiFrameGUIState.m_Windows->m_ModalWindow != NULL;
+ m_SortedScripts.clear ();
+ if (!layoutedScripts.empty ())
+ {
+ // Send the layout event to all scripts that have that.
+ e.type = InputEvent::kLayout;
+
+ IMGUI::BeginWindows (state, true, !hasModalWindow); // We need to enable clipping as otherwise it hasn't been set up.
+
+ for (std::vector<GUIObjectWrapper>::iterator i = layoutedScripts.begin (); i != layoutedScripts.end (); ++i)
+ {
+ GUIObjectWrapper beh = *i;
+ if (beh)
+ {
+ beh.DoGUI (MonoBehaviour::kGameLayout, 0);
+ m_SortedScripts.push_back (SortedScript (GetGUIState().m_OnGUIState.m_Depth, beh));
+ current++;
+ }
+ }
+ // Remove any unused windows
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*state.m_CurrentEvent);
+ IMGUI::EndWindows (state, !hasModalWindow);
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+
+ OldSortScript sort;
+ // Next, we sort by depth
+ m_SortedScripts.sort (sort);
+ e.type = originalType;
+ }
+
+ bool hasSentEndWindows = false;
+
+ current = 1; // reset the count so we can send the DoWindows.
+ if (frontToBack)
+ {
+ // This gets cleared during REPAINT event. For normal event processing, we'll just re-read that.
+ state.m_CanvasGUIState.m_IsMouseUsed = m_MouseUsed;
+
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++)
+ {
+ // If this script used the event, we terminate the loop
+ if (eventUsed)
+ break;
+
+ // If this is the first script in layer 0, Put BeginWindows / EndWindows around the OnGUI call
+ bool endWindows = false;
+ if ((hasModalWindow || (current == m_SortedScripts.size() || i->depth > 0)) && !hasSentEndWindows)
+ {
+ IMGUI::BeginWindows (state, true, !hasModalWindow);
+ if (eventUsed)
+ break;
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ current++;
+ eventUsed = i->beh.DoGUI (MonoBehaviour::kGameLayout, 0);
+ if (endWindows)
+ IMGUI::EndWindows (state, !hasModalWindow);
+ }
+
+ // Remove keyboard focus when clicking on empty GUI area
+ // (so text fields don't take away game view input).
+ if (originalType == InputEvent::kMouseDown && !eventUsed)
+ {
+ GUIState &state = GetGUIState();
+ state.m_MultiFrameGUIState.m_KeyboardControl = 0;
+ }
+
+ // Handle mouse focus: We want the new GUI system to eat any mouse events. This means that we need to check this during repaint or mouseDown/Up
+ // and set a global variable accordingly.
+ if (originalType == InputEvent::kMouseDown || originalType == InputEvent::kMouseUp)
+ state.m_CanvasGUIState.m_IsMouseUsed = (bool)state.m_CanvasGUIState.m_IsMouseUsed | eventUsed;
+ }
+ else
+ {
+ // We clear mouse used at repaint time, then let it run through any event processing for next frame, before reading it back
+ m_MouseUsed = state.m_CanvasGUIState.m_IsMouseUsed = false;
+
+ // I'm iterating backwards here - used by repainting
+ // this time, users can't bail out of the event processing
+ IMGUI::BeginWindows (state, true, !hasModalWindow);
+ for (SortedScripts::iterator i = m_SortedScripts.end(); i != m_SortedScripts.begin();)
+ {
+ i--;
+ bool endWindows = false;
+
+
+ // If this window is the last, OR the next one is above 0 depth, we should do repaint all popup windows NOW
+ if (!hasSentEndWindows) // If we haven't already sent it
+ {
+ if (current == m_SortedScripts.size()) // If this is the last script, we must call it now
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ else
+ {
+ // If this is the last script with a depth > 0, we must call it now.
+ SortedScripts::iterator j = i;
+ j--;
+ if (j->depth <= 0)
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ }
+ }
+ i->beh.DoGUI (MonoBehaviour::kGameLayout, 0);
+ if (endWindows)
+ {
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*state.m_CurrentEvent);
+ IMGUI::EndWindows (state, !hasModalWindow); // don't ignore modal windows if we have them
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+ }
+
+ current++;
+ }
+
+ if(hasModalWindow)
+ {
+ // Ensure modal windows are always on top by painting them last.
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*state.m_CurrentEvent);
+ IMGUI::RepaintModalWindow (state);
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+ }
+ }
+
+ if (handleTab != 0 && !eventUsed && m_SortedScripts.size() != 0)
+ {
+ // Build the list of IDLists to cycle through
+ std::vector<IDList*> keyIDLists;
+
+ IMGUI::GUIWindow* focusedWindow = IMGUI::GetFocusedWindow (state);
+ if (focusedWindow)
+ keyIDLists.push_back (&focusedWindow->m_ObjectGUIState.m_IDList);
+ else
+ {
+ keyIDLists.reserve (m_SortedScripts.size());
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++)
+ {
+ keyIDLists.push_back (&i->beh.GetObjectGUIState().m_IDList);
+ }
+ }
+
+ state.CycleKeyboardFocus(keyIDLists, handleTab == 1);
+ }
+
+ m_MasterState.SaveFromGUIState (state);
+ m_MasterState.EndFrame ();
+ m_MouseUsed = state.m_CanvasGUIState.m_IsMouseUsed;
+}
+
+void GUIManager::QueueEvent (InputEvent &ie)
+{
+ QueueEventImmediate(ie);
+}
+
+
+void GUIManager::QueueEventImmediate (InputEvent &ie)
+{
+ // MouseMove events are not sent.
+ // The same info can be obtained from repaint events.
+ if (ie.type == InputEvent::kMouseMove)
+ {
+ // We still use them as last event to update the cursor position.
+ m_LastEvent = ie;
+ return;
+ }
+ if (ie.type == InputEvent::kIgnore)
+ return;
+
+ if ( ie.type == InputEvent::kMouseDown )
+ m_mouseButtonsDown |= (1<<ie.button);
+ else if ( ie.type == InputEvent::kMouseUp )
+ m_mouseButtonsDown &= ~(1<<ie.button);
+
+ switch (ie.type) {
+ case InputEvent::kMouseDown:
+ case InputEvent::kMouseUp:
+ case InputEvent::kKeyDown:
+ ResetCursorFlash ();
+ break;
+ }
+
+ m_LastEvent = ie;
+ m_Events.push_back(ie);
+}
+
+void GUIManager::ResetCursorFlash ()
+{
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ return;
+ #endif
+
+ GetGUIManager().m_LastInputEventTime = GetTimeManager().GetRealtime ();
+}
+
+float GUIManager::GetCursorFlashTime ()
+{
+ return GetGUIManager().m_LastInputEventTime;
+}
+
+
+GUIKeyboardState &GUIManager::GetMasterGUIState ()
+{
+ return GetGUIManager().m_MasterState;
+}
+
+#if SUPPORT_REPRODUCE_LOG
+void WriteInputEvent (InputEvent& event, std::ostream& out)
+{
+ out << (int&)event.type << ' ';
+ WriteFloat(out, event.mousePosition.x); out << ' ';
+ WriteFloat(out, event.mousePosition.y); out << ' ';
+ WriteFloat(out, event.delta.x); out << ' ';
+ WriteFloat(out, event.delta.y); out << ' ';
+
+ out << event.button << ' ' << event.modifiers << ' ';
+ WriteFloat(out, event.pressure); out << ' ';
+ out << event.clickCount << ' ' << event.character << ' ' << event.keycode << ' ';
+
+ // if (event.commandString)
+ // WriteReproductionString(out, event.commandString);
+ // else
+ // WriteReproductionString(out, "");
+}
+
+void ReadInputEvent (InputEvent& event, std::istream& in, int version)
+{
+ event.Init();
+
+ in >> (int&)event.type;
+ ReadFloat(in, event.mousePosition.x);
+ ReadFloat(in, event.mousePosition.y);
+ ReadFloat(in, event.delta.x);
+ ReadFloat(in, event.delta.y);
+ in >> event.button >> event.modifiers;
+ ReadFloat(in, event.pressure);
+ in >> event.clickCount >> event.character >> event.keycode;
+
+ //if (version >= 6)
+ //{
+ // std::string commandString;
+ // ReadReproductionString(in, commandString);
+ // event.commandString = new char[commandString.size() + 1];
+ // memcpy(event.commandString, commandString.c_str(), commandString.size() + 1);
+ //}
+}
+
+void GUIManager::WriteLog (std::ofstream& out)
+{
+ out << "Events" << std::endl;
+
+ out << m_Events.size() << std::endl;
+
+ for (int i=0;i<m_Events.size();i++)
+ {
+ InputEvent& event = m_Events[i];
+ WriteInputEvent(event, out);
+ }
+
+ // Hover events seem to require the last event as well!
+ InputEvent& event = m_LastEvent;
+ WriteInputEvent (event, out);
+
+ if (GetReproduceVersion () >= 6)
+ {
+ WriteReproductionString(out, GetInputManager().GetCompositionString());
+ out << (int)(GetInputManager().GetTextFieldInput()) << ' ';
+ }
+
+ out << std::endl;
+}
+
+void GUIManager::ReadLog (std::ifstream& in)
+{
+ CheckReproduceTagAndExit("Events", in);
+
+ int size;
+ in >> size;
+ m_Events.clear();
+ for (int i=0;i<size;i++)
+ {
+ InputEvent event;
+ ReadInputEvent (event, in, GetReproduceVersion());
+ m_Events.push_back(event);
+ }
+
+ // Hover events seem to require the last event as well!
+ ReadInputEvent (m_LastEvent, in, GetReproduceVersion());
+
+ if (GetReproduceVersion () >= 6)
+ {
+ ReadReproductionString(in, GetInputManager().GetCompositionString());
+ int textFieldInput;
+ in >> textFieldInput;
+ GetInputManager().SetTextFieldInput(textFieldInput);
+ }
+}
+#endif
+#endif
diff --git a/Runtime/IMGUI/GUIManager.h b/Runtime/IMGUI/GUIManager.h
new file mode 100644
index 0000000..eb35c45
--- /dev/null
+++ b/Runtime/IMGUI/GUIManager.h
@@ -0,0 +1,135 @@
+#ifndef GUIMANAGER_H
+#define GUIMANAGER_H
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/Utilities/MemoryPool.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+
+#include <deque>
+
+struct InputEvent;
+class GUIManager {
+ public:
+ GUIManager ();
+
+ // Issue a repaint event - used by players...
+ // Mouse position & modifiers are read from the input manager.
+ // TODO: implement Modifier keys
+ void Repaint ();
+
+ // Add/remove - called from MonoBehaviour
+ void AddGUIScript (MonoBehaviourListNode& beh);
+
+ // Send an event to all in-game GUI scripts
+ // e - the event to send.
+ // mask - GOLayer mask to match.
+ // frontToBack Send event to the frontmost GUI script first (true for normal event processing, false for repaint)
+ void DoGUIEvent (InputEvent &e, bool frontToBack);
+
+ // Send an input event (keydown, mousemove, mouseup, etc) to the GUI scripts.
+ void QueueEvent (InputEvent &ie);
+ void QueueEventImmediate (InputEvent &ie);
+
+ void SendQueuedEvents ();
+
+ InputEvent GetLastInputEvent() { return m_LastEvent;}
+
+ static bool AnyMouseButtonsDown();
+
+ // Did GUI code inside BeginWindows make it irrelevant for OnGUI code to be called at all? (e.g. mouseClick inside a GUI.window)
+ // We used to do this by calling event.Use (), but that causes repaints, so instead this function is called that sets a var to track it
+ static void SetDidGUIWindowsEatLastEvent (bool value);
+ static bool GetDidGUIWindowsEatLastEvent ();
+
+ #if UNITY_EDITOR
+ // Clear all setting for entering / exiting playmode
+ void Reset ();
+ #endif
+
+ static void ResetCursorFlash ();
+ static float GetCursorFlashTime ();
+
+ #if UNITY_EDITOR
+ void SetEditorGUIInfo (Vector2f guiPixelOffset);
+ Vector2f GetGUIPixelOffset () { return m_GUIPixelOffset; }
+ Vector2f GetEditorGUIInfo() const;
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ void WriteLog (std::ofstream& out);
+ void ReadLog (std::ifstream& in);
+ #endif
+
+ struct GUIObjectWrapper
+ {
+ public:
+ explicit GUIObjectWrapper(MonoBehaviour* beh);
+ #if UNITY_HAS_DEVELOPER_CONSOLE
+
+ explicit GUIObjectWrapper(DeveloperConsole* dev);
+
+ #endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+ // Wrapped functions for GUI objects
+ bool DoGUI(MonoBehaviour::GUILayoutType layoutType, int skin) const {
+ return do_gui_func(wrapped_ptr, layoutType, skin);
+ }
+
+ ObjectGUIState& GetObjectGUIState() const {
+ return get_gui_state_func(wrapped_ptr);
+ }
+
+ // This avoids a plethora of pitfalls in situations,
+ // where implicit conversions may happen
+ typedef void * GUIObjectWrapper::*unspecified_bool_type;
+ operator unspecified_bool_type() const { // never throws
+ return wrapped_ptr == 0? 0: &GUIObjectWrapper::wrapped_ptr;
+ }
+
+ private:
+ typedef bool (*dogui_function_type)(void*, MonoBehaviour::GUILayoutType, int);
+ typedef ObjectGUIState& (*get_gui_state_function_type)(void*);
+
+ void* wrapped_ptr;
+
+ dogui_function_type do_gui_func;
+ get_gui_state_function_type get_gui_state_func;
+ };
+
+ struct SortedScript
+ {
+ int depth;
+ GUIObjectWrapper beh;
+ SortedScript (int dep, GUIObjectWrapper b) : depth(dep), beh(b) {}
+ };
+
+ inline std::deque<InputEvent>& GetQueuedEvents() {return m_Events;}
+ bool GetMouseUsed () const { return m_MouseUsed; }
+
+ static GUIKeyboardState &GetMasterGUIState ();
+
+private:
+ typedef List<MonoBehaviourListNode> MonoBehaviourList;
+ MonoBehaviourList m_GUIScripts;
+ std::deque<InputEvent> m_Events;
+
+ bool m_MouseUsed, m_DidGUIWindowsEatLastEvent;
+ int m_mouseButtonsDown;
+ Vector2f m_GUIPixelOffset;
+ typedef std::list<SortedScript, memory_pool<SortedScript> > SortedScripts;
+ SortedScripts m_SortedScripts;
+
+ float m_LastInputEventTime;
+ InputEvent m_LastEvent;
+ GUIKeyboardState m_MasterState;
+};
+
+GUIManager &GetGUIManager ();
+
+void InitGUIManager ();
+void CleanupGUIManager ();
+
+#endif
diff --git a/Runtime/IMGUI/GUIState.cpp b/Runtime/IMGUI/GUIState.cpp
new file mode 100644
index 0000000..90db27a
--- /dev/null
+++ b/Runtime/IMGUI/GUIState.cpp
@@ -0,0 +1,519 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNITYGUI
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/NamedKeyControlList.h"
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingArguments.h"
+
+static EternalGUIState* gEternalGUIState = NULL;
+static IDList* gEternalIDList = NULL;
+static GUIState *gGUIState = NULL;
+
+GUIState &GetGUIState ()
+{
+ AssertIf (!gGUIState);
+ return *gGUIState;
+}
+
+
+
+EternalGUIState *GetEternalGUIState ()
+{
+ if (gEternalGUIState == NULL)
+ gEternalGUIState = new EternalGUIState ();
+ return gEternalGUIState;
+}
+
+static IDList* GetEternalIDList ()
+{
+ if (gEternalIDList == NULL)
+ gEternalIDList = new IDList();
+ return gEternalIDList;
+}
+
+
+MultiFrameGUIState::MultiFrameGUIState ()
+{
+ m_KeyboardControl = 0;
+ m_NamedKeyControlList = NULL;
+ m_Windows = NULL;
+}
+
+MultiFrameGUIState::~MultiFrameGUIState ()
+{
+ delete m_NamedKeyControlList;
+ delete m_Windows;
+}
+
+void MultiFrameGUIState::Reset ()
+{
+ delete m_Windows;
+ m_Windows = 0;
+
+ delete m_NamedKeyControlList;
+ m_NamedKeyControlList = 0;
+}
+
+IMGUI::NamedControl *MultiFrameGUIState::GetControlNamed (const std::string &name)
+{
+ if (m_NamedKeyControlList == NULL)
+ return NULL;
+ return m_NamedKeyControlList->GetControlNamed (name);
+}
+
+void MultiFrameGUIState::AddNamedControl (std::string &name, int id, IMGUI::GUIWindow* window)
+{
+ if (m_NamedKeyControlList == NULL)
+ m_NamedKeyControlList = new IMGUI::NamedKeyControlList ();
+ m_NamedKeyControlList->AddNamedControl(name, id, window ? window->m_ID : -1);
+}
+
+void MultiFrameGUIState::ClearNamedControls ()
+{
+ if (m_NamedKeyControlList)
+ m_NamedKeyControlList->Clear ();
+}
+
+
+ObjectGUIState::ObjectGUIState ()
+{
+}
+
+ObjectGUIState::~ObjectGUIState ()
+{
+}
+
+void ObjectGUIState::BeginOnGUI ()
+{
+ m_IDList.BeginOnGUI ();
+}
+
+void OnGUIState::SetNameOfNextKeyboardControl (const std::string &nextName)
+{
+ delete m_NameOfNextKeyboardControl;
+ m_NameOfNextKeyboardControl = new string (nextName);
+}
+
+void OnGUIState::ClearNameOfNextKeyboardControl ()
+{
+ delete m_NameOfNextKeyboardControl;
+ m_NameOfNextKeyboardControl = NULL;
+}
+
+OnGUIState::OnGUIState ()
+{
+ m_NameOfNextKeyboardControl = NULL;
+ m_MouseTooltip = m_KeyTooltip = NULL;
+ m_CaptureBlock = NULL;
+ m_Color = m_BackgroundColor = m_ContentColor = ColorRGBAf (1.0f,1.0f,1.0f,1.0f);
+ m_Enabled = 1;
+ m_Changed = 0;
+ m_Depth = 0;
+ m_ShowKeyboardControl = 1;
+
+}
+
+OnGUIState::~OnGUIState ()
+{
+ delete m_NameOfNextKeyboardControl;
+ delete m_MouseTooltip;
+ delete m_KeyTooltip;
+}
+
+void OnGUIState::BeginOnGUI ()
+{
+ m_Color = m_BackgroundColor = m_ContentColor = ColorRGBAf(1.0f,1.0f,1.0f,1.0f);
+ m_Enabled = true;
+ m_Changed = false;
+ m_Depth = 1;
+}
+
+void OnGUIState::EndOnGUI ()
+{
+ delete m_NameOfNextKeyboardControl;
+ m_NameOfNextKeyboardControl = NULL;
+ delete m_MouseTooltip;
+ m_MouseTooltip = NULL;
+ delete m_KeyTooltip;
+ m_KeyTooltip = NULL;
+}
+
+
+void OnGUIState::SetMouseTooltip (const UTF16String &tooltip)
+{
+ delete m_MouseTooltip;
+ m_MouseTooltip = new UTF16String (tooltip);
+}
+
+void OnGUIState::SetKeyTooltip (const UTF16String &tooltip)
+{
+ delete m_KeyTooltip;
+ m_KeyTooltip = new UTF16String (tooltip);
+}
+
+GUIState::GUIState ()
+{
+ m_CurrentEvent = NULL;
+ m_ObjectGUIState = NULL;
+ m_OnGUIDepth = 0;
+}
+
+GUIState::~GUIState()
+{
+}
+
+
+void GUIState::BeginFrame ()
+{
+
+}
+
+void GUIState::EndFrame ()
+{
+
+}
+
+void GUIState::BeginOnGUI (ObjectGUIState &objectGUIState)
+{
+ m_ObjectGUIState = &objectGUIState;
+
+ m_OnGUIState.BeginOnGUI ();
+ m_ObjectGUIState->BeginOnGUI ();
+ m_OnGUIDepth++;
+}
+
+void GUIState::EndOnGUI ()
+{
+ m_OnGUIState.EndOnGUI();
+ m_ObjectGUIState = NULL;
+
+ AssertIf (m_OnGUIDepth < 1);
+ m_OnGUIDepth--;
+}
+
+static int GetControlID (GUIState& state, int hint, FocusType focusType, const Rectf& rect, bool useRect)
+{
+ int id;
+
+ if (state.m_ObjectGUIState == NULL)
+ {
+ id = state.m_EternalGUIState->GetNextUniqueID ();
+
+#if (UNITY_EDITOR)
+ // Editor GUI needs some additional things to happen when calling GetControlID.
+ // While this will also be called for runtime code running in Play mode in the Editor,
+ // it won't have any effect. EditorGUIUtility.s_LastControlID will be set to the id,
+ // but this is only used inside the handling of a single control.
+ ScriptingInvocation invocation(MONO_COMMON.handleControlID);
+ invocation.AddInt(id);
+ invocation.Invoke();
+#endif
+
+ return id;
+ }
+
+ if (useRect)
+ id = state.m_ObjectGUIState->m_IDList.GetNext (state, hint, focusType, rect);
+ else
+ id = state.m_ObjectGUIState->m_IDList.GetNext (state, hint, focusType);
+
+ // maybe in future this should check for focusType == keyboard
+ // the issue currently is that there is no way to focus buttons via the keyboard
+ // this means that if you have a game with no mouse input you can not 'tab' to the
+ // button using a joystic or keys.
+ //
+ // because of this we need to allow custom named controls of all types (apart from passive)
+ // to be selectable.
+ if (focusType != kPassive && state.m_OnGUIState.GetNameOfNextKeyboardControl () != NULL)
+ {
+ IMGUI::GUIWindow* currentWindow = NULL;
+ if (state.m_MultiFrameGUIState.m_Windows)
+ currentWindow = state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow;
+ state.m_MultiFrameGUIState.AddNamedControl (*state.m_OnGUIState.GetNameOfNextKeyboardControl (), id, currentWindow);
+ state.m_OnGUIState.ClearNameOfNextKeyboardControl ();
+ }
+
+#if (UNITY_EDITOR)
+ // Same as above; see comment there.
+ ScriptingInvocation invocation(MONO_COMMON.handleControlID);
+ invocation.AddInt(id);
+ invocation.Invoke();
+#endif
+
+ return id;
+}
+
+int GUIState::GetControlID (int hint, FocusType focusType)
+{
+ return ::GetControlID (*this, hint, focusType, Rectf(), false);
+}
+
+int GUIState::GetControlID (int hint, FocusType focusType, const Rectf& rect)
+{
+ return ::GetControlID (*this, hint, focusType, rect, true);
+}
+
+int GUIState::GetIDOfNamedControl (const std::string &name)
+{
+ IMGUI::NamedControl *ctrl = m_MultiFrameGUIState.GetControlNamed (name);
+ if (ctrl)
+ return ctrl->ID;
+ return 0;
+}
+
+std::string GUIState::GetNameOfFocusedControl ()
+{
+ if (m_MultiFrameGUIState.m_NamedKeyControlList)
+ return m_MultiFrameGUIState.m_NamedKeyControlList->GetNameOfControl (m_MultiFrameGUIState.m_KeyboardControl);
+ return "";
+}
+
+void GUIState::FocusKeyboardControl (const std::string &name)
+{
+ IMGUI::NamedControl *ctrl = m_MultiFrameGUIState.GetControlNamed (name);
+ if (ctrl)
+ {
+ m_MultiFrameGUIState.m_KeyboardControl = ctrl->ID;
+ IMGUI::FocusWindow (*this, ctrl->windowID);
+ }
+ else
+ {
+ m_MultiFrameGUIState.m_KeyboardControl = 0;
+ IMGUI::FocusWindow (*this, -1);
+ }
+}
+
+void GUIState::SetEvent( const InputEvent& event )
+{
+ ScriptingInvocationNoArgs invocation;
+ invocation.method = MONO_COMMON.makeMasterEventCurrent;
+ invocation.Invoke();
+ *m_CurrentEvent = event;
+}
+
+void GUIState::Internal_SetManagedEvent (void *event)
+{
+ m_CurrentEvent = (InputEvent*)event;
+}
+
+void GUIState::MoveAllDataTo (GUIState &dest, bool saving)
+{
+ dest.m_MultiFrameGUIState = m_MultiFrameGUIState;
+ m_MultiFrameGUIState.m_NamedKeyControlList = NULL;
+ m_MultiFrameGUIState.m_Windows = NULL;
+
+ dest.m_OnGUIState = m_OnGUIState;
+ m_OnGUIState.m_CaptureBlock = NULL;
+ m_OnGUIState.m_NameOfNextKeyboardControl = NULL;
+ m_OnGUIState.m_MouseTooltip = NULL;
+ m_OnGUIState.m_KeyTooltip = NULL;
+
+ dest.m_ObjectGUIState = m_ObjectGUIState;
+
+ // Move over the clipping info.
+ dest.m_CanvasGUIState = m_CanvasGUIState;
+
+ // This one is truly eternal, so we don't want to clear it from the current.
+ dest.m_EternalGUIState = m_EternalGUIState;
+
+ // Move over the event. Since the event is shared in weird ways between Mono and C++, we'll copy it.
+ if (saving)
+ {
+ dest.m_BackupEvent = *m_CurrentEvent;
+ dest.m_CurrentEvent = m_CurrentEvent;
+ }
+ else
+ {
+
+ *(dest.m_CurrentEvent) = m_BackupEvent;
+ }
+}
+
+GUIState* GUIState::GetPushState ()
+{
+ // Set up state
+ GUIState *pushState = new GUIState ();
+ GetGUIState().MoveAllDataTo (*pushState, true);
+ return pushState;
+}
+
+void GUIState::PopAndDelete (GUIState *pushState)
+{
+ pushState->MoveAllDataTo (GetGUIState(), false);
+ delete pushState;
+}
+
+static IDList* FindWhichIDListHasKeyboardControl (std::vector<IDList *> &idListsToSearch)
+{
+ // Find which IDList has the keyboard Control.
+ for (std::vector<IDList *>::iterator i = idListsToSearch.begin(); i != idListsToSearch.end(); i++)
+ {
+ if ((*i)->HasKeyboardControl ())
+ {
+ return *i;
+ }
+ }
+ return NULL;
+}
+
+void GUIState::CycleKeyboardFocus (std::vector<IDList *> &idListsToSearch, bool searchForward)
+{
+ m_MultiFrameGUIState.m_KeyboardControl = GetNextKeyboardControlID(idListsToSearch, searchForward);
+}
+
+int GUIState::GetNextKeyboardControlID (std::vector<IDList *> &idListsToSearch, bool searchForward)
+{
+ IDList *start = FindWhichIDListHasKeyboardControl (idListsToSearch);
+
+ if (searchForward)
+ {
+ if (start && start->GetNextKeyboardControlID() != -1)
+ {
+ return start->GetNextKeyboardControlID();
+ }
+
+ // Which script did we start the keyboard search FROM.
+ int startIdx = -1;
+ // Which script are we scanning for keyboard controls.
+ int listIdx = -1;
+ if (start != NULL)
+ {
+ for (int i = 0; i < idListsToSearch.size(); i++)
+ {
+ if (idListsToSearch[i] == start)
+ {
+ startIdx = listIdx = (i + 1) % idListsToSearch.size();
+ break;
+ }
+ }
+ } else {
+ startIdx = 0;//idListsToSearch.size();
+ listIdx = 0;
+ }
+
+ do {
+ int firstInList = idListsToSearch[listIdx]->GetFirstKeyboardControlID ();
+ if (firstInList != -1)
+ {
+ return firstInList;
+ }
+ listIdx++;
+ listIdx = listIdx % idListsToSearch.size();
+ } while (listIdx != startIdx);
+ return 0;
+ }
+ else
+ {
+ if (start && start->GetPreviousKeyboardControlID() != -1)
+ {
+ return start->GetPreviousKeyboardControlID();
+ }
+
+ int startIdx = 0, listIdx = idListsToSearch.size();
+ if (start != NULL)
+ {
+ for (int i = 0; i < idListsToSearch.size(); i++)
+ {
+ if (idListsToSearch[i] == start)
+ {
+ startIdx = listIdx = i;
+ break;
+ }
+ }
+ }
+
+ do {
+ listIdx = listIdx - 1;
+ if (listIdx == -1)
+ listIdx = idListsToSearch.size() - 1;
+
+ int firstInList = idListsToSearch[listIdx]->GetLastKeyboardControlID ();
+ if (firstInList != -1)
+ {
+ return firstInList;
+ }
+ } while (listIdx != startIdx);
+ return 0;
+ }
+}
+
+GUIKeyboardState::GUIKeyboardState ()
+{
+ m_KeyboardControl = 0;
+ m_FocusedGUIWindow = -1;
+ m_ShowKeyboardControl = true;
+ m_Windows = NULL;
+ m_NamedKeyControlList = NULL;
+}
+
+GUIKeyboardState::~GUIKeyboardState ()
+{
+ delete m_Windows;
+ delete m_NamedKeyControlList;
+}
+
+void GUIKeyboardState::LoadIntoGUIState (GUIState &dest)
+{
+ dest.m_MultiFrameGUIState.m_KeyboardControl = m_KeyboardControl;
+
+ Assert (!dest.m_MultiFrameGUIState.m_NamedKeyControlList);
+ dest.m_MultiFrameGUIState.m_NamedKeyControlList = m_NamedKeyControlList;
+
+ Assert (!dest.m_MultiFrameGUIState.m_Windows);
+ dest.m_MultiFrameGUIState.m_Windows = m_Windows;
+
+ dest.m_OnGUIState.m_ShowKeyboardControl = m_ShowKeyboardControl;
+ m_Windows = NULL;
+}
+void GUIKeyboardState::SaveFromGUIState (GUIState &src)
+{
+ m_KeyboardControl = src.m_MultiFrameGUIState.m_KeyboardControl;
+ m_NamedKeyControlList = src.m_MultiFrameGUIState.m_NamedKeyControlList;
+ src.m_MultiFrameGUIState.m_NamedKeyControlList = NULL;
+ m_Windows = src.m_MultiFrameGUIState.m_Windows;
+ m_ShowKeyboardControl = src.m_OnGUIState.m_ShowKeyboardControl;
+ src.m_MultiFrameGUIState.m_Windows = NULL;
+}
+
+void GUIKeyboardState::Reset ()
+{
+ m_KeyboardControl = m_FocusedGUIWindow = 0;
+ delete m_Windows;
+ m_Windows = NULL;
+ delete m_NamedKeyControlList;
+ m_NamedKeyControlList = NULL;
+}
+
+void GUIKeyboardState::EndFrame ()
+{
+ if (m_Windows)
+ m_Windows->ReleaseScriptingObjects ();
+}
+
+void InitGUIState ()
+{
+ Assert(gGUIState == NULL);
+ gGUIState = new GUIState ();
+ gGUIState->m_EternalGUIState = GetEternalGUIState();
+ gGUIState->m_CurrentEvent = new InputEvent ();
+ gGUIState->m_CurrentEvent->Init();
+}
+
+
+void CleanupGUIState ()
+{
+ Assert(gGUIState != NULL);
+ delete gGUIState;
+ gGUIState = NULL;
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIState.h b/Runtime/IMGUI/GUIState.h
new file mode 100644
index 0000000..9ab0ad6
--- /dev/null
+++ b/Runtime/IMGUI/GUIState.h
@@ -0,0 +1,231 @@
+#ifndef GUISTATE_H
+#define GUISTATE_H
+
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/NamedKeyControlList.h"
+#include "Runtime/IMGUI/GUIClip.h"
+
+namespace IMGUI
+{
+ struct GUIWindowState;
+ struct GUIWindow;
+}
+
+
+struct InputEvent;
+struct MonoObject;
+struct GUIGraphicsCacheBlock;
+struct UTF16String;
+
+// Lasts multiple frames.
+// There is ONE for user's games and one for each Editor Window.
+struct MultiFrameGUIState
+{
+ // Which control has keyboard focus
+ int m_KeyboardControl;
+
+ // List of named keyboard controls. NULL if none
+ IMGUI::NamedKeyControlList *m_NamedKeyControlList;
+
+ // List of GUI.Window IDLists
+ IMGUI::GUIWindowState *m_Windows;
+
+
+ IMGUI::NamedControl *GetControlNamed (const std::string &name);
+ void AddNamedControl (std::string &name, int id, IMGUI::GUIWindow* window);
+ void ClearNamedControls ();
+ void Reset ();
+
+ MultiFrameGUIState ();
+ ~MultiFrameGUIState ();
+};
+
+// State that is reset each OnGUI.
+struct OnGUIState
+{
+ OnGUIState ();
+ ~OnGUIState ();
+
+ // Colors for rendering
+ ColorRGBAf m_Color, m_BackgroundColor, m_ContentColor;
+
+ // Is the GUI enabled
+ int m_Enabled;
+
+ // Has the GUI changed
+ int m_Changed;
+
+ // Depth of the current GUIBehaviour's OnGUI - not used by GUI.Window or EditorWindow
+ int m_Depth;
+
+ int m_ShowKeyboardControl;
+
+ // If IMGUI is rendered inside NewGUI, this is a pointer to the batching object. All rendering commands go into this.
+ std::vector<GUIGraphicsCacheBlock>* m_CaptureBlock;
+
+ // Name of the next keyboard control. It's a pointer to maintain size with Mono, but owned by OnGUIState
+ // Don't set this pointer (unless you're implementing nested OnGUI calls) - use the getters and setters
+ std::string *m_NameOfNextKeyboardControl;
+
+ UTF16String *m_MouseTooltip, *m_KeyTooltip;
+
+ void SetNameOfNextKeyboardControl (const std::string &name);
+ std::string *GetNameOfNextKeyboardControl () { return m_NameOfNextKeyboardControl; }
+
+ void SetMouseTooltip (const UTF16String &tooltip);
+ UTF16String *GetMouseTooltip () { return m_MouseTooltip; }
+
+ void SetKeyTooltip (const UTF16String &tooltip);
+ UTF16String *GetKeyTooltip () { return m_KeyTooltip; }
+
+ void ClearNameOfNextKeyboardControl ();
+ // Reset the values to sane defaults for an OnGUI call
+ void BeginOnGUI ();
+ void EndOnGUI ();
+};
+
+// State that is stored in the MonoBehaviour and pulled from it every OnGUI.
+struct ObjectGUIState
+{
+ IDList m_IDList; // per monobehaviour + 1 per GUI.Window
+
+ void BeginOnGUI ();
+
+ ObjectGUIState ();
+ ~ObjectGUIState ();
+};
+
+// State that exists per new-style canvas, and per old-style MonoBehaviour with an OnGUI.
+struct CanvasGUIState
+{
+ GUIClipState m_GUIClipState;
+ int m_IsMouseUsed;
+};
+
+/// This one is always available.
+struct EternalGUIState
+{
+private:
+ int m_UniqueID;
+ // Which control has touch / mouse focus
+public:
+ int m_HotControl; // TODO:should be int[kMaxSupportedPointers]
+ bool m_AllowHover;
+
+ int GetNextUniqueID ()
+ {
+ return m_UniqueID++;
+ }
+
+ EternalGUIState ()
+ {
+ m_UniqueID = 1;
+ m_HotControl = 0;
+ m_AllowHover = true;
+ }
+};
+EternalGUIState *GetEternalGUIState ();
+
+// All state used by the GUI (so be it!)
+struct GUIState
+{
+ GUIState ();
+ ~GUIState();
+
+ MultiFrameGUIState m_MultiFrameGUIState;
+ OnGUIState m_OnGUIState;
+ ObjectGUIState* m_ObjectGUIState; // The state owned by the monobehaviour we're calling on
+ CanvasGUIState m_CanvasGUIState; // The state for the surface we're rendering into
+ EternalGUIState* m_EternalGUIState;
+ InputEvent* m_CurrentEvent; // C++ side. Maps to the memory INSIDE the ManagedCurrentEvent.
+
+ InputEvent m_BackupEvent;
+
+ int m_OnGUIDepth; // How deep we are inside OnGUI calls. 0 = we haven't called at all
+
+ // Whether the current event has been marked as used
+#if ENABLE_NEW_EVENT_SYSTEM
+ bool GetIsEventUsed() const { return (m_CurrentEvent != NULL) && m_CurrentEvent->type == InputEvent::kUsed; }
+
+ // Begin and End simply make it possible to use Event.current from C# outside of OnGUI code
+ void BeginUsingEvents () { ++m_OnGUIDepth; }
+ void EndUsingEvents() { --m_OnGUIDepth; }
+#endif
+
+ // Call this to intialize a game frame or window frame - not for each event we send into the system.
+ void BeginFrame ();
+
+ // Call this when we're done with one OS-level frame.
+ void EndFrame ();
+
+ // Call this to intialize ONE onGUI call. Must be called between e.g. layout and repaint events
+ // Assumes all states have been assigned correctly.
+ void BeginOnGUI (ObjectGUIState &objectGUIState);
+
+ /// Call this to end an OnGUI call. It will call all substates with EndOnGUI, so they can clean up.
+ void EndOnGUI ();
+
+
+ int GetControlID (int hint, FocusType focusType, const Rectf &rect);
+ int GetControlID (int hint, FocusType focusType);
+
+ // Handle Named Controls
+ void SetNameOfNextKeyboardControl (const std::string &name) { m_OnGUIState.SetNameOfNextKeyboardControl (name); }
+ std::string GetNameOfFocusedControl ();
+ int GetIDOfNamedControl (const std::string &name);
+ void FocusKeyboardControl (const std::string &name);
+
+ //Not sure where this should go. It's called by GUIManager & EditorWindows. For now, I'll put it here...
+ void CycleKeyboardFocus (std::vector<IDList *> &IDListsToSearch, bool searchForward);
+ int GetNextKeyboardControlID (std::vector<IDList *> &IDListsToSearch, bool searchForward);
+
+ void SetEvent (const InputEvent& event);
+ void SetObjectGUIState (ObjectGUIState &objectGUIState);
+
+ // Make a copy of the current managed gui state (to implement nesting)
+ // When done with an OnGUI call, call PopAndDelete to restore.
+ static GUIState* GetPushState ();
+ static void PopAndDelete (GUIState *pushState);
+
+ // Move all GUI state into dest, NULLing all pointer fields in this
+ // Used by GetPushState & PopAndDelete
+ void MoveAllDataTo (GUIState &dest, bool saving);
+
+ // Called from C# whenever the user assigns into Event.current.
+ // Don't call this from C++;
+ void Internal_SetManagedEvent (void *event);
+};
+
+GUIState &GetGUIState ();
+
+// Struct for saving GUI State between in-game frames. Each editor window is also a separate "world" in this regard.
+// So E.g. Keyboard control names are shared between all scripts in a game, but each editor window has it's own separate world.
+struct GUIKeyboardState
+{
+ // Which controlID has focus last frame
+ int m_KeyboardControl;
+ // Should we show the keyboard control? false if editor window (or webplayer I guess) doesn't have focus.
+ int m_ShowKeyboardControl;
+ // IDLists for each window. Null if none
+ IMGUI::GUIWindowState* m_Windows;
+
+ IMGUI::NamedKeyControlList *m_NamedKeyControlList;
+
+ int m_FocusedGUIWindow;
+
+ void LoadIntoGUIState (GUIState &dest);
+ void SaveFromGUIState (GUIState &src);
+
+ void EndFrame ();
+
+ void Reset ();
+
+ GUIKeyboardState ();
+ ~GUIKeyboardState ();
+};
+
+void InitGUIState ();
+void CleanupGUIState ();
+#endif
diff --git a/Runtime/IMGUI/GUIStyle.cpp b/Runtime/IMGUI/GUIStyle.cpp
new file mode 100644
index 0000000..ac4e632
--- /dev/null
+++ b/Runtime/IMGUI/GUIStyle.cpp
@@ -0,0 +1,1154 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "TextMeshGenerator2.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "External/shaderlab/Library/properties.h"
+#include "Runtime/Graphics/GeneratedTextures.h"
+#include "External/shaderlab/Library/FastPropertyName.h"
+#include "Runtime/Graphics/TextureGenerator.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Shaders/Shader.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/AssetBundle.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/OptimizedGUIBlock.h"
+#include "Editor/Src/TooltipManager.h"
+#include "Editor/Src/Highlighter/HighlighterCore.h"
+#include "Editor/Src/EditorResources.h"
+#include "Editor/Src/EditorHelper.h"
+#endif
+
+const float s_TabSize = 16;
+using namespace std;
+static Rectf s_GUIClipRect (0,0,1,1);
+
+namespace GUIStyle_Static
+{
+ PPtr<Font> s_DefaultFont = NULL, s_BuiltinFont = NULL;
+} // namespace GUIStyle_Static
+
+// if these are set to 0 icons scale to fit the text
+float s_GUIStyleIconSizeX = 0;
+float s_GUIStyleIconSizeY = 0;
+
+
+// A minimal (4x4) texture is not enough because of limited subtexel precision on some cards.
+// With large clipping rectangles, one texel in clipping texture can span lots of pixels on screen,
+// and because of limited precision there can be one pixel errors.
+// E.g. Radeon HD 3850 seems to need at least 16x16 texture.
+const float kGUIClipTextureSize = 16.0f;
+
+static Material *kGUITextMaterial = NULL;
+static Material *kBlendMaterial = NULL;
+static Material *kBlitMaterial = NULL;
+
+inline void GUIClipTexture (Texture2D *tex, unsigned char *data, int x, int y, int maxX, int maxY)
+{
+ if (x == 0 || y == 0 || x == maxX - 1 || y == maxY - 1)
+ *data = 0;
+ else
+ *data = 255;
+}
+
+static Texture2D *gGUIClipTexture = NULL;
+
+Texture2D *GUIStyle::GetClipTexture ()
+{
+ return gGUIClipTexture;
+}
+
+void InitializeGUIClipTexture ()
+{
+ if (gGUIClipTexture)
+ return;
+
+ gGUIClipTexture = BuildTexture <unsigned char> (int(kGUIClipTextureSize), int(kGUIClipTextureSize), kTexFormatAlpha8, &GUIClipTexture);
+ gGUIClipTexture->SetFilterMode (kTexFilterNearest);
+ gGUIClipTexture->SetWrapMode(kTexWrapClamp);
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ ShaderLab::TexEnv *te = props->SetTexture (ShaderLab::Property ("_GUIClipTexture"),gGUIClipTexture);
+ te->SetMatrixName (ShaderLab::Property("_GUIClipTextureMatrix"));
+ te->SetTexGen (kTexGenEyeLinear);
+}
+
+Material* GetGUIBlitMaterial ()
+{
+ if (!kBlitMaterial)
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITextureBlit.shader");
+ kBlitMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ InitializeGUIClipTexture();
+ }
+
+ return kBlitMaterial;
+}
+
+Material* GetGUIBlendMaterial ()
+{
+ if (!kBlendMaterial)
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITextureClip.shader");
+ kBlendMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ InitializeGUIClipTexture();
+ }
+
+ return kBlendMaterial;
+}
+
+static Material* GetGUITextMaterial ()
+{
+ if (!kGUITextMaterial)
+ {
+ Shader* shader = GetBuiltinResource<Shader> ("Internal-GUITextureClipText.shader");
+ kGUITextMaterial = Material::CreateMaterial(*shader, Object::kHideAndDontSave);
+ InitializeGUIClipTexture();
+ }
+
+ return kGUITextMaterial;
+}
+
+GUIStyle::GUIStyle()
+{
+ m_Alignment = 0;
+ m_RichText = true;
+ m_WordWrap = 0;
+ m_Clipping = 0;
+ m_ImagePosition = 0;
+ m_ContentOffset = Vector2f(0.0f, 0.0f);
+ m_ClipOffset = Vector2f(0.0f, 0.0f);
+ m_FixedWidth = 0;
+ m_FixedHeight = 0;
+ m_FontSize = 0;
+ m_FontStyle = 0;
+ m_StretchWidth = true;
+ m_StretchHeight = false;
+
+}
+
+GUIStyle::GUIStyle (const GUIStyle &other)
+{
+ m_Name = other.m_Name;
+ m_Normal = other.m_Normal;
+ m_Hover = other.m_Hover;
+ m_Active = other.m_Active;
+ m_Focused = other.m_Focused;
+ m_OnNormal = other.m_OnNormal;
+ m_OnHover = other.m_OnHover;
+ m_OnActive = other.m_OnActive;
+ m_OnFocused = other.m_OnFocused;
+ m_Border = other.m_Border;
+ m_Margin = other.m_Margin;
+ m_Padding = other.m_Padding;
+ m_Overflow = other.m_Overflow;
+ m_Font = other.m_Font;
+ m_Alignment = other.m_Alignment;
+ m_RichText = other.m_RichText;
+ m_WordWrap = other.m_WordWrap;
+ m_Clipping = other.m_Clipping;
+ m_ImagePosition = other.m_ImagePosition;
+ m_ContentOffset = other.m_ContentOffset;
+ m_ClipOffset = other.m_ClipOffset;
+ m_FixedWidth = other.m_FixedWidth;
+ m_FixedHeight = other.m_FixedHeight;
+ m_FontSize = other.m_FontSize;
+ m_FontStyle = other.m_FontStyle;
+ m_StretchWidth = other.m_StretchWidth;
+ m_StretchHeight = other.m_StretchHeight;
+}
+
+
+void GUIStyle::SetDefaultFont (Font *font)
+{
+ using namespace GUIStyle_Static;
+ if (font != NULL)
+ s_DefaultFont = font;
+ else
+ s_DefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+}
+Font*GUIStyle::GetDefaultFont ()
+{
+ return GUIStyle_Static::s_DefaultFont;
+}
+
+void GUIStyle::Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, int controlID, bool on) const
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+ int hot = IMGUI::GetHotControl (state);
+ bool enabled = state.m_OnGUIState.m_Enabled;
+#if ENABLE_NEW_EVENT_SYSTEM
+ bool mouseOver = screenRect.Contains (evt.touch.pos);
+#else
+ bool mouseOver = screenRect.Contains (evt.mousePosition);
+#endif
+ bool isHover = mouseOver && state.m_CanvasGUIState.m_GUIClipState.GetEnabled();
+ bool isHover2 = isHover && (hot == controlID || hot == 0);
+
+ if (isHover)
+ state.m_CanvasGUIState.m_IsMouseUsed = true;
+
+ bool isActive = (controlID == hot) && enabled && mouseOver;
+ bool hasKey = state.m_MultiFrameGUIState.m_KeyboardControl == controlID && enabled && state.m_OnGUIState.m_ShowKeyboardControl;
+
+ Draw (state, screenRect, content, isHover2, isActive, on, hasKey);
+
+
+ // #if UNITY_GUI_SUPPORT_TOOLTIP
+ if (content.m_Tooltip.text != NULL && content.m_Tooltip.length != 0)
+ {
+ if (isHover || isActive || hot == controlID)
+ {
+ SetMouseTooltip (state, content.m_Tooltip, screenRect);
+ }
+ if (hasKey)
+ state.m_OnGUIState.SetKeyTooltip (content.m_Tooltip);
+ }
+ // #endif
+}
+
+void GUIStyle::SetMouseTooltip (GUIState& state, const UTF16String& tooltip, const Rectf& screenRect)
+{
+ state.m_OnGUIState.SetMouseTooltip (tooltip);
+#if UNITY_EDITOR
+ Vector2f v = GetGUIManager().GetGUIPixelOffset();
+ v += state.m_CanvasGUIState.m_GUIClipState.Unclip (Vector2f (screenRect.x, screenRect.y));
+ GetTooltipManager().SetTooltip (tooltip, Rectf (v.x, v.y, screenRect.width, screenRect.height));
+#endif
+
+}
+
+
+void GUIStyle::Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const
+{
+ #if UNITY_EDITOR
+ if (HighlighterCore::s_SearchMode == kHighlightByContent || HighlighterCore::s_SearchMode == kHighlightAuto)
+ HighlighterCore::Handle (state, screenRect, content.m_Text);
+ #endif
+
+ // always draw of pixel coordinates
+ Rectf rounded = ClampRect (screenRect);
+ float right = Roundf (rounded.x + rounded.width);
+ float bottom = Roundf (rounded.y + rounded.height);
+ rounded.x = Roundf(rounded.x);
+ rounded.y = Roundf(rounded.y);
+ rounded.width = right - rounded.x;
+ rounded.height = bottom - rounded.y;
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+
+ if (bottom < visibleRect.y || rounded.y > visibleRect.GetBottom())
+ return;
+
+ isHover = state.m_EternalGUIState->m_AllowHover && isHover;
+ const GUIStyleState *gss = GetGUIStyleState (state, isHover, isActive, on, hasKeyboardFocus);
+ DrawBackground (state, rounded, gss);
+ DrawContent (state, rounded, content, gss);
+}
+
+void GUIStyle::DrawWithTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus, bool drawSelectionAsComposition, int cursorFirst, int cursorLast, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const {
+ // always draw of pixel coordinates
+ Rectf rounded = ClampRect (screenRect);
+ rounded.x = Roundf(rounded.x);
+ rounded.y = Roundf(rounded.y);
+ rounded.width = Roundf(rounded.width);
+ rounded.height = Roundf(rounded.height);
+
+ const GUIStyleState *gss = GetGUIStyleState (state, isHover, isActive, on, hasKeyboardFocus);
+ DrawBackground (state, rounded, gss);
+ if (hasKeyboardFocus)
+ {
+ if (drawSelectionAsComposition)
+ {
+ DrawTextUnderline (state, rounded, content, cursorFirst, cursorLast, gss);
+ // draw cursor.
+ DrawTextSelection (state, rounded, content, cursorLast, cursorLast, cursorColor, selectionColor);
+ }
+ else
+ DrawTextSelection (state, rounded, content, cursorFirst, cursorLast, cursorColor, selectionColor);
+ }
+ DrawContent (state, rounded, content, gss);
+}
+
+void GUIStyle::CalcMinMaxWidth (GUIContent &content, float *minWidth, float *maxWidth) const {
+ // If we have a fixed width, that becoms both min and max
+ if (m_FixedWidth != 0) {
+ *minWidth = *maxWidth = m_FixedWidth;
+ return;
+ }
+
+ TextMeshGenerator2 &tmgen = TextMeshGenerator2::Get (content.m_Text, &GetCurrentFont(), (TextAnchor)m_Alignment, kAuto, 0, s_TabSize, 1.0f, m_RichText, true, ColorRGBA32(0xffffffff), m_Font ? m_FontSize : 0, m_Font ? m_FontStyle : 0);
+ Vector2f size = tmgen.GetSize ();
+ // If we're word wrapping, we'll never go below 32 pixels.
+ // Ideally, we should get the size of the largest word, but that is just too painful.
+ *maxWidth = size.x;
+ if (m_WordWrap)
+ *minWidth = min (32.0f, size.x);
+ else
+ *minWidth = size.x;
+
+ if (content.m_Image) {
+ float iWidth = content.m_Image->GetDataWidth();
+ switch (m_ImagePosition) {
+ case kImageLeft:
+ *minWidth += iWidth;
+ *maxWidth += iWidth;
+ break;
+ case kImageOnly:
+ *minWidth = *maxWidth = iWidth;
+ break;
+ case kImageAbove:
+ *minWidth = max (iWidth, *minWidth);
+ *maxWidth = max (iWidth, *maxWidth);
+ break;
+ case kTextOnly:
+ break;
+ }
+ }
+
+ *minWidth += m_Padding.left + m_Padding.right;
+ *maxWidth += m_Padding.left + m_Padding.right;
+}
+
+Vector2f GUIStyle::GetCursorPixelPosition (const Rectf &screenRect, GUIContent &content, int cursorStringIndex) const {
+ TextMeshGenerator2 *tmgen = GetGenerator (screenRect, content);
+ if (tmgen)
+ return tmgen->GetCursorPosition(m_Padding.Remove (screenRect), cursorStringIndex) + (m_ContentOffset + m_ClipOffset);
+ else
+ return Vector2f (0,0);
+}
+
+int GUIStyle::GetCursorStringIndex (const Rectf &screenRect, GUIContent &content, const Vector2f &cursorPixelPosition) const {
+ TextMeshGenerator2 *tmgen = GetGenerator (screenRect, content);
+ if (tmgen)
+ return tmgen->GetCursorIndexAtPosition(m_Padding.Remove (screenRect), cursorPixelPosition - (m_ContentOffset + m_ClipOffset));
+ return 0;
+}
+
+Font& GUIStyle::GetCurrentFont () const
+{
+ using namespace GUIStyle_Static;
+ Font* thisFont = m_Font;
+ if (thisFont != NULL)
+ return *thisFont;
+
+ Font* defaultFont = s_DefaultFont;
+ if (defaultFont != NULL)
+ return *defaultFont;
+
+ return GetBuiltinFont ();
+}
+
+Font &GUIStyle::GetBuiltinFont ()
+{
+ Font* builtinFont = GUIStyle_Static::s_BuiltinFont;
+ if (builtinFont != NULL)
+ return *builtinFont;
+
+ GUIStyle_Static::s_BuiltinFont = builtinFont = GetBuiltinResource<Font> (kDefaultFontName);
+ if (builtinFont == NULL)
+ {
+ LogString ("Couldn't load default font or font material!");
+ }
+
+ return *builtinFont;
+}
+
+float GUIStyle::GetLineHeight () const {
+ Font& f = GetCurrentFont();
+ return f.GetLineSpacing (m_FontSize);
+}
+
+int GUIStyle::GetNumCharactersThatFitWithinWidth (const UTF16String &text, float width) const
+{
+ Font &f = GetCurrentFont();
+
+ // Call before GetCharacterWidth to ensure character data has been setup
+ f.CacheFontForText (text.text, text.length);
+
+ width -= m_Padding.left + m_Padding.right;
+
+ float currentWidth = 0;
+ unsigned stringlen = text.length;
+ int numChars = stringlen;
+ for (int i=0; i<stringlen; i++)
+ {
+ int c = text[i];
+ float characterWidth = f.GetCharacterWidth (c, m_FontSize, m_FontStyle);
+ if (characterWidth == 0.f)
+ {
+ return -1; // failed to get character width
+ }
+
+ currentWidth += characterWidth;
+ if (currentWidth > width)
+ {
+ numChars = i;
+ break;
+ }
+ }
+
+ return numChars;
+}
+
+float GUIStyle::CalcHeight (GUIContent &content, float width) const {
+ if (m_FixedHeight != 0.0f)
+ return m_FixedHeight;
+
+ Vector2f imageSize (0,0), textSize (0,0);
+ Texture *image = content.m_Image;
+ if (image != NULL)
+ imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ float contentHeight = 0;
+
+ TextMeshGenerator2 *tmgen = GetGenerator (Rectf (0,0,width, 1000), content);
+ if (tmgen)
+ textSize = tmgen->GetSize ();
+ switch (m_ImagePosition) {
+ case kImageLeft: {
+ contentHeight = max (textSize.y, imageSize.y);
+ break;
+ }
+ case kImageAbove: {
+ contentHeight = textSize.y + imageSize.y;
+ break;
+ }
+ case kImageOnly:
+ contentHeight = imageSize.y;
+ break;
+ case kTextOnly: {
+ contentHeight = textSize.y;
+ break;
+ }
+ }
+
+ return contentHeight + m_Padding.top + m_Padding.bottom;
+}
+
+Vector2f GUIStyle::CalcSize (GUIContent &content) const {
+ Texture *image = content.m_Image;
+
+ if (m_FixedHeight != 0.0f && m_FixedWidth != 0.0f)
+ return Vector2f (m_FixedWidth, m_FixedHeight);
+
+ Vector2f textSize (0,0), imageSize (0,0);
+ if (content.m_Text.length != 0 && m_ImagePosition != kImageOnly)
+ textSize = GetGenerator(Rectf (0,0,0,0), content)->GetSize ();
+ if (image != NULL && m_ImagePosition != kTextOnly)
+ imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ Vector2f size (0,0);
+ switch (m_ImagePosition) {
+ case kImageLeft:
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX != 0) && (s_GUIStyleIconSizeY != 0))
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+ size = Vector2f (textSize.x + imageSize.x, max (textSize.y, imageSize.y));
+ break;
+ case kImageAbove:
+ size = Vector2f (max (textSize.x, imageSize.x), textSize.y + imageSize.y);
+ break;
+ case kImageOnly:
+ size = imageSize;
+ break;
+ case kTextOnly:
+ size = textSize;
+ break;
+ }
+
+ // If we
+ if (content.m_Text.length == 0 && image == NULL && m_ImagePosition != kImageOnly)
+ {
+ size.y = GetCurrentFont().GetLineSpacing(m_FontSize);
+ }
+ size+= m_Padding.Size ();
+ if (m_FixedWidth != 0.0f)
+ size.x = m_FixedWidth;
+ if (m_FixedHeight != 0.0f)
+ size.y = m_FixedHeight;
+ return size;
+}
+
+const GUIStyleState *GUIStyle::GetGUIStyleState (GUIState &state, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const
+{
+ const GUIStyleState *t = NULL;
+ if (!on) {
+ // If the GUI is disabled, return the normal style
+ if (isHover)
+ t = m_Hover.background ? &m_Hover : t;
+ if (hasKeyboardFocus)
+ t = m_Focused.background ? &m_Focused : (m_Hover.background ? &m_Hover : t);
+ if (isActive && isHover)
+ t = m_Active.background ? &m_Active : t;
+ if (!state.m_OnGUIState.m_Enabled)
+ t = &m_Normal;
+ } else {
+ // If the GUI is disabled, return the normal style
+ if (isHover)
+ t = m_OnHover.background ? &m_OnHover : t;
+ if (hasKeyboardFocus)
+ t = m_OnFocused.background ? &m_OnFocused : (m_OnHover.background ? &m_OnHover : t);
+ if (isActive && isHover)
+ t = m_OnActive.background ? &m_OnActive : t;
+ if (!state.m_OnGUIState.m_Enabled)
+ t = &m_Normal;
+
+ if (t == NULL || !t->background || !state.m_OnGUIState.m_Enabled)
+ t = &m_OnNormal;
+ }
+
+ if (t == NULL || !t->background)
+ t = &m_Normal;
+ return t;
+}
+
+static void DrawClippedTexture (const Rectf &screenRect, Texture *image, float leftBorder, float rightBorder, float topBorder, float bottomBorder, const ColorRGBAf &color)
+{
+ DrawGUITexture (screenRect, image, int(leftBorder), int(rightBorder), int(topBorder), int(bottomBorder), color, GetGUIBlendMaterial());
+}
+
+
+void GUIStyle::DrawBackground (GUIState &state, const Rectf &screenRect, const GUIStyleState *gss) const {
+#if ENABLE_RETAINEDGUI
+ if (state.m_OnGUIState.m_CaptureBlock)
+ {
+ if (gss->background)
+ {
+ GUIVertexData* data = new GUIVertexData (&GUIVertexDataFormat::vtxPosColorUV0UV1);
+ GUIUtils::BuildImage (m_Overflow.Add (screenRect), m_Border, gss->background, Vector4f(1.0f, 1.0f, 0.0f, 0.0f), *data);
+ GUIClipRegion temp (state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect());
+ GUIUtils::BuildClipCoords (&temp, *data);
+
+ ColorRGBAf backgroundColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_BackgroundColor;
+ if (!state.m_OnGUIState.m_Enabled)
+ backgroundColor.a *= .5f;
+ GUIUtils::BuildColor(backgroundColor, *data);
+
+ state.m_OnGUIState.m_CaptureBlock->push_back (GUIGraphicsCacheBlock (data, GetGUIBlendMaterial()));
+ }
+ return;
+ }
+#endif
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+ SetGUIClipRect(visibleRect);
+
+ if (gss->background) {
+ ColorRGBAf backgroundColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_BackgroundColor;
+ if (!state.m_OnGUIState.m_Enabled)
+ backgroundColor *= ColorRGBAf (1,1,1,.5f);
+
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueBackground (m_Overflow.Add (screenRect), gss->background, m_Border, backgroundColor, state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect());
+ return;
+ }
+#endif
+
+ DrawClippedTexture (m_Overflow.Add (screenRect), gss->background, m_Border.left, m_Border.right, m_Border.top, m_Border.bottom, backgroundColor);
+ }
+}
+
+void GUIStyle::RenderText (const Rectf &screenRect, TextMeshGenerator2 &tmgen, ColorRGBAf color) const {
+ // Configure the shaders
+ Font& currentFont = GetCurrentFont();
+
+ Material *material = GetGUITextMaterial ();
+
+ // now we set color using TextMeshGenerator vertices, rather then using the material, so set material color to white.
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ color = ColorRGBA32(0xffffffff);
+
+ static SHADERPROP (Color);
+ static SHADERPROP (MainTex);
+ ShaderLab::PropertySheet& properties = material->GetWritableProperties();
+
+ properties.SetVector(kSLPropColor, color.GetPtr());
+
+ Texture* fontTexture = currentFont.GetTexture();
+
+ // In the case when font is a custum font, GetTexture() will return NULL, so in this case take the main texture from font's material
+ if (fontTexture == NULL && currentFont.GetMaterial())
+ {
+ fontTexture = currentFont.GetMaterial()->GetTexture(kSLPropMainTex);
+ }
+
+ properties.SetTexture(kSLPropMainTex, fontTexture);
+
+ GfxDevice &device = GetGfxDevice ();
+
+ float matWorld[16], matView[16];
+
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+ Matrix4x4f textMatrix;
+
+ Vector2f offset = tmgen.GetTextOffset (screenRect);
+
+ textMatrix.SetTranslate (Vector3f (offset.x, offset.y, 0.0f));
+
+ device.SetViewMatrix (textMatrix.GetPtr());
+
+ int passCount = material->GetPassCount ();
+ for (int i=0;i < passCount ;i++)
+ {
+ const ChannelAssigns* channels = material->SetPass (i);
+ tmgen.RenderRaw (*channels);
+ }
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+}
+
+TextMeshGenerator2* GUIStyle::GetGenerator (const Rectf &screenRect, GUIContent &content, ColorRGBA32 color) const
+{
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ color = 0xffffffff;
+ return IMGUI::GetGenerator (m_Padding.Remove (screenRect), content, GetCurrentFont(), (TextAnchor)m_Alignment, m_WordWrap, m_RichText, color, m_FontSize, m_FontStyle, (ImagePosition)m_ImagePosition);
+}
+
+TextMeshGenerator2* GUIStyle::GetGenerator (const Rectf &screenRect, GUIContent &content) const
+{
+ ColorRGBA32 color = 0xffffffff;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ {
+ GUIState &state = GetGUIState();
+ ColorRGBAf imageColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_ContentColor;
+ ColorRGBAf textColor = m_Normal.textColor * imageColor;
+ if (!state.m_OnGUIState.m_Enabled) {
+ textColor.a *= .5f;
+ }
+ color = textColor;
+ }
+ return IMGUI::GetGenerator (m_Padding.Remove (screenRect), content, GetCurrentFont(), (TextAnchor)m_Alignment, m_WordWrap, m_RichText, color, m_FontSize, m_FontStyle, (ImagePosition)m_ImagePosition);
+}
+
+
+void GUIStyle::CalcContentRects (const Rectf &contentRect, Vector2f imageSize, Vector2f textSize, Rectf &imageRect, Rectf &textRect, float &width, float &height, int imagePosition, int alignment, Vector2f contentOffset)
+{
+ // Sum them up depending on contents layout.
+ width = 0;
+ height = 0;
+ switch (imagePosition) {
+ case kImageLeft:
+ // Scale the image down if we need the room
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX == 0) || (s_GUIStyleIconSizeY == 0))
+ {
+ float imageScale = clamp (min ((contentRect.Width() - textSize.x) / imageSize.x, contentRect.Height() / imageSize.y), 0.0f, 1.0f);
+
+ imageSize.x = Roundf (imageSize.x * imageScale);
+ imageSize.y = Roundf (imageSize.y * imageScale);
+ }
+ else
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+
+ width = imageSize.x + textSize.x;
+ height = max (imageSize.y, textSize.y);
+ break;
+ case kImageAbove:
+ // Scale the image down if we need the room
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX == 0) || (s_GUIStyleIconSizeY == 0))
+ {
+ float imageScale = clamp (min ((contentRect.Height() - textSize.y) / imageSize.y, contentRect.Width() / imageSize.x), 0.0f, 1.0f);
+ imageSize.x = Roundf (imageSize.x * imageScale);
+ imageSize.y = Roundf (imageSize.y * imageScale);
+ }
+ else
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+
+ width = max (imageSize.x, textSize.x);
+ height = imageSize.y + textSize.y;
+ break;
+ case kImageOnly:
+ // Scale the image down if we need the room
+ if (imageSize.x > 0)
+ {
+ if ((s_GUIStyleIconSizeX == 0) || (s_GUIStyleIconSizeY == 0))
+ {
+ float imageScale = min (min (contentRect.Width() / imageSize.x, contentRect.Height() / imageSize.y), 1.0f);
+ imageSize.x = Roundf (imageSize.x * imageScale);
+ imageSize.y = Roundf (imageSize.y * imageScale);
+ }
+ else
+ {
+ imageSize.x = s_GUIStyleIconSizeX;
+ imageSize.y = s_GUIStyleIconSizeY;
+ }
+ }
+
+ width = imageSize.x;
+ height = imageSize.y;
+ break;
+ case kTextOnly:
+ width = textSize.x;
+ height = textSize.y;
+ break;
+ }
+
+ // We now have the combined sizes - need to know where to put them.
+ float xAlign = 0, yAlign = 0;
+ switch (alignment) {
+ case kUpperLeft:
+ xAlign = 0; yAlign = 0; break;
+ case kUpperCenter:
+ xAlign = .5f; yAlign = 0; break;
+ case kUpperRight:
+ xAlign = 1; yAlign = 0; break;
+ case kMiddleLeft:
+ xAlign = 0; yAlign = .5f; break;
+ case kMiddleCenter:
+ xAlign = .5f; yAlign = .5f; break;
+ case kMiddleRight:
+ xAlign = 1; yAlign = .5f; break;
+ case kLowerLeft:
+ xAlign = 0; yAlign = 1; break;
+ case kLowerCenter:
+ xAlign = .5f; yAlign = 1; break;
+ case kLowerRight:
+ xAlign = 1; yAlign = 1; break;
+ }
+ // We need to round as centering the text can position it on half a pixel
+ float x = Roundf (contentRect.x + (contentRect.width - width) * xAlign + contentOffset.x);
+ float y = Roundf (contentRect.y + (contentRect.height - height) * yAlign + contentOffset.y);
+
+ switch (imagePosition) {
+ case kImageLeft:
+ if (imageSize.x > 0.0f)
+ imageRect = Rectf (x, y + (height - imageSize.y) * .5f, imageSize.x, imageSize.y);
+ if (textSize.x > 0.0f && imageSize.x > 0.0f)
+ textRect = Rectf (x + imageSize.x + 1, y + (height - textSize.y) * .5f, textSize.x, textSize.y);
+ else if (textSize.x > 0.0f)
+ textRect = Rectf (x, y + (height - textSize.y) * .5f, textSize.x, textSize.y);
+ break;
+ case kImageAbove:
+ if (imageSize.x > 0.0f)
+ imageRect = Rectf (Roundf (x + (width - imageSize.x) * .5f), y, imageSize.x, imageSize.y);
+ if (textSize.x > 0.0f)
+ textRect = Rectf (Roundf (x + (width - textSize.x) * .5f), y + imageSize.y, textSize.x, textSize.y);
+
+ break;
+ case kImageOnly:
+ if (imageSize.x > 0.0f)
+ imageRect = Rectf (Roundf (x + (width - imageSize.x) * .5f), y, imageSize.x, imageSize.y);
+ break;
+ case kTextOnly:
+ if (textSize.x > 0.0f)
+ textRect = Rectf (x, y, textSize.x, textSize.y);
+ break;
+ }
+}
+
+void GUIStyle::DrawContent (GUIState &state, const Rectf &screenRect, GUIContent &content, const GUIStyleState *gss) const
+{
+ Vector2f textSize (0,0), imageSize (0,0);
+ TextMeshGenerator2 *tmgen = NULL;
+
+ // we now have the location of the contents in the rects. Now to draw it.
+ // Setup the color
+ ColorRGBAf imageColor = state.m_OnGUIState.m_Color * state.m_OnGUIState.m_ContentColor;
+ ColorRGBAf textColor = gss->textColor * imageColor;
+ if (!state.m_OnGUIState.m_Enabled) {
+ textColor.a *= .5f;
+ imageColor.a *= .5f;
+ }
+
+ if (m_ImagePosition != kImageOnly && content.m_Text.length != 0)
+ {
+ tmgen = GetGenerator (screenRect, content, textColor);
+ textSize = tmgen->GetSize();
+ }
+
+ // TODO: If we use word wrapping and imageLeft we should somehow subtract the width...
+ Texture *image = content.m_Image;
+ if (image != NULL && m_ImagePosition != kTextOnly)
+ imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ Rectf imageRect (0,0,0,0), textRect (0,0,0,0);
+ float width, height;
+ Rectf contentRect = m_Padding.Remove (screenRect);
+ CalcContentRects (contentRect, imageSize, textSize, imageRect, textRect, width, height, m_ImagePosition, m_Alignment, m_ContentOffset);
+
+#if ENABLE_RETAINEDGUI
+ if (state.m_OnGUIState.m_CaptureBlock)
+ {
+ GUIVertexData* data = new GUIVertexData (&GUIVertexDataFormat::vtxPosColorUV0UV1);
+ GUIUtils::BuildText(textRect, *tmgen, *data);
+
+ GUIClipRegion temp (state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect());
+ GUIUtils::BuildClipCoords (&temp, *data);
+
+ GUIUtils::BuildColor(textColor, *data);
+
+ state.m_OnGUIState.m_CaptureBlock->push_back (GUIGraphicsCacheBlock (data, GetGUITextMaterial()));
+ return;
+ }
+#endif
+ // We know where the various components go, now we draw them (with lots of clipping shit)
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ Rectf newClipRect, clipRect = visibleRect;
+ if (m_Clipping != kOverflow && (width > contentRect.width || height > contentRect.height))
+ {
+ newClipRect = contentRect;
+ newClipRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ newClipRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+ newClipRect.Clamp (clipRect);
+ } else {
+ newClipRect = clipRect;
+ }
+
+ // Queue the text for later processing
+ if (textRect.width != 0.0f)
+ {
+ block->QueueText (textRect, tmgen, newClipRect);
+ }
+ // Render image
+ if (imageRect.width != 0.0f)
+ block->QueueTexture (imageRect, image, imageColor, clipRect);
+ return;
+ }
+#endif
+
+ // Do clipping of content
+ bool needClipping = false;
+ Rectf newClipRect;
+ if (m_Clipping != kOverflow && (width > contentRect.width || height > contentRect.height)) {
+ needClipping = true;
+ newClipRect = contentRect;
+ newClipRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ newClipRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+ newClipRect.Clamp (visibleRect);
+ if (newClipRect.width == 0.0f || newClipRect.height == 0.0f)
+ return;
+ SetGUIClipRect (newClipRect);
+ } else {
+ SetGUIClipRect(visibleRect);
+ }
+
+ // Render text
+ if (textRect.width != 0.0f && tmgen != NULL)
+ {
+ RenderText (textRect, *tmgen, textColor);
+ }
+ // Render image
+ if (imageRect.width != 0.0f)
+ DrawClippedTexture (imageRect, image, 0,0,0,0,imageColor);
+
+ // If we used custom clipping, restore it
+ if (needClipping)
+ SetGUIClipRect (visibleRect);
+
+}
+
+Rectf GUIStyle::ClampRect (const Rectf &screenRect) const
+{
+ return Rectf (screenRect.x, screenRect.y, m_FixedWidth ? m_FixedWidth : screenRect.Width(), m_FixedHeight ? m_FixedHeight : screenRect.Height());
+}
+
+void GUIStyle::DrawCursor(GUIState &state, const Rectf &screenRect, GUIContent &content, int position, const ColorRGBAf &cursorColor) const
+{
+ if (!state.m_OnGUIState.m_Enabled)
+ return;
+
+ Texture *tex = builtintex::GetWhiteTexture();
+ Font& currentFont = GetCurrentFont();
+
+ float lineHeight = currentFont.GetLineSpacing (m_FontSize);
+ Material *material = GetGUIBlendMaterial();
+
+ ColorRGBA32 textureColor = cursorColor * state.m_OnGUIState.m_Color;
+ Vector2f cursorPos = GetCursorPixelPosition (screenRect, content, position) - m_ClipOffset;
+ DrawGUITexture(Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,material);
+}
+
+void GUIStyle::DrawTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const {
+ if (!state.m_OnGUIState.m_Enabled)
+ return;
+ Texture *tex = builtintex::GetWhiteTexture();
+
+ Font& currentFont = GetCurrentFont();
+
+ float lineHeight = currentFont.GetLineSpacing (m_FontSize);
+ Material *material = GetGUIBlendMaterial();
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+ SetGUIClipRect(visibleRect);
+
+ // If the style uses clipping, just apply it - there's only ever one of these anyways
+ Rectf innerRect = m_Padding.Remove (screenRect);
+ Rectf clipRect;
+ if (m_Clipping) {
+ clipRect = visibleRect;
+ innerRect.Clamp (visibleRect);
+ innerRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ innerRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+
+ SetGUIClipRect (innerRect);
+ }
+
+ // We don't have a selection, only a cursor position
+ if (first == last) {
+ ColorRGBA32 textureColor = cursorColor * state.m_OnGUIState.m_Color;
+ Vector2f cursorPos = GetCursorPixelPosition (screenRect, content, first) - m_ClipOffset;
+
+ // if we auto-size the GUIStyle to the text, and the cursor is after the last character, it always gets clipped.
+ // Move it one pixel to the left, so it does not clip
+ if (first == content.m_Text.length && cursorPos.x >= screenRect.x + screenRect.width)
+ cursorPos.x--;
+
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,visibleRect);
+ } else {
+ DrawGUITexture(Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,material);
+ }
+#else
+ DrawGUITexture(Rectf (cursorPos.x, cursorPos.y, 1, lineHeight),tex,textureColor,material);
+#endif
+ } else {
+ ColorRGBA32 textureColor = selectionColor * state.m_OnGUIState.m_Color;
+
+ int min = first < last ? first : last;
+ int max = first > last ? first : last;
+
+ Vector2f minPos = GetCursorPixelPosition (screenRect, content, min) - m_ClipOffset;
+ Vector2f maxPos = GetCursorPixelPosition (screenRect, content, max) - m_ClipOffset;
+
+ if (minPos.y == maxPos.y)
+ {
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (minPos.x, minPos.y, maxPos.x - minPos.x + 1, lineHeight), tex, textureColor, visibleRect);
+ } else {
+#endif
+ DrawGUITexture (Rectf (minPos.x, minPos.y, maxPos.x - minPos.x + 1, lineHeight), tex, textureColor, material);
+#if UNITY_EDITOR
+ }
+#endif
+ } else {
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ // draw the first line - including end part
+ block->QueueTexture (Rectf (minPos.x, minPos.y, innerRect.GetRight() - minPos.x, lineHeight), tex, textureColor, visibleRect);
+ // Draw the middle part
+ block->QueueTexture (Rectf (innerRect.x, minPos.y + lineHeight, innerRect.Width(), maxPos.y - minPos.y - lineHeight), tex, textureColor, visibleRect);
+ // Draw the bottom line - up to selection
+ if (maxPos.x != innerRect.x) // Don't draw silly 1px line at the left when a newline is selected
+ block->QueueTexture (Rectf (innerRect.x, maxPos.y, maxPos.x - innerRect.x + 1, lineHeight), tex, textureColor, visibleRect);
+ } else {
+#endif
+ // draw the first line - including end part
+ DrawGUITexture (Rectf (minPos.x, minPos.y, innerRect.GetRight() - minPos.x, lineHeight), tex, textureColor, material);
+ // Draw the middle part
+ DrawGUITexture (Rectf (innerRect.x, minPos.y + lineHeight, innerRect.Width(), maxPos.y - minPos.y - lineHeight), tex, textureColor, material);
+ // Draw the bottom line - up to selection
+ if (maxPos.x != innerRect.x) // Don't draw silly 1px line at the left when a newline is selected
+ DrawGUITexture (Rectf (innerRect.x, maxPos.y, maxPos.x - innerRect.x + 1, lineHeight), tex, textureColor, material);
+#if UNITY_EDITOR
+ }
+#endif
+ }
+ }
+ if (m_Clipping)
+ SetGUIClipRect (clipRect);
+
+}
+
+void GUIStyle::DrawTextUnderline (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const GUIStyleState *gss) const
+{
+ if (!state.m_OnGUIState.m_Enabled)
+ return;
+
+ Rectf visibleRect = state.m_CanvasGUIState.m_GUIClipState.GetVisibleRect();
+
+ SetGUIClipRect(visibleRect);
+
+ Texture *tex = builtintex::GetWhiteTexture();
+ Font& currentFont = GetCurrentFont();
+
+ float lineHeight = currentFont.GetLineSpacing (m_FontSize);
+ Material *material = GetGUIBlendMaterial();
+
+ // If the style uses clipping, just apply it - there's only ever one of these anyways
+ Rectf innerRect = m_Padding.Remove (screenRect);
+ Rectf clipRect;
+
+ if (m_Clipping) {
+ clipRect = visibleRect;
+ innerRect.Clamp (visibleRect);
+ innerRect.x += (m_ContentOffset.x + m_ClipOffset.x);
+ innerRect.y += (m_ContentOffset.y + m_ClipOffset.y);
+
+ SetGUIClipRect (innerRect);
+ }
+
+ {
+ ColorRGBA32 textColor = gss->textColor * state.m_OnGUIState.m_Color * state.m_OnGUIState.m_ContentColor;
+
+ int min = first < last ? first : last;
+ int max = first > last ? first : last;
+
+ Vector2f pos = GetCursorPixelPosition (screenRect, content, min) - m_ClipOffset;
+ Vector2f maxPos = GetCursorPixelPosition (screenRect, content, max) - m_ClipOffset;
+
+ float underlineSize = std::max(1.0f, lineHeight*0.03f);
+ float underlineOffset = lineHeight*0.95f-underlineSize;
+
+ while(pos.y < maxPos.y - 0.01)
+ {
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (pos.x, pos.y+underlineOffset, innerRect.GetRight() - pos.x + 1, underlineSize), tex, textColor, visibleRect);
+ } else
+#endif
+ DrawGUITexture (Rectf (pos.x, pos.y+underlineOffset, innerRect.GetRight() - pos.x + 1, underlineSize), tex, textColor, material);
+ pos.y += ceilf(lineHeight);
+ pos.x = innerRect.x;
+ }
+#if UNITY_EDITOR
+ OptimizedGUIBlock *block = GetCaptureGUIBlock ();
+ if (block)
+ {
+ block->QueueTexture (Rectf (pos.x, pos.y+underlineOffset, maxPos.x - pos.x + 1, underlineSize), tex, textColor, visibleRect);
+ } else
+#endif
+ DrawGUITexture (Rectf (pos.x, pos.y+underlineOffset, maxPos.x - pos.x + 1, underlineSize), tex, textColor, material);
+ }
+ if (m_Clipping)
+ SetGUIClipRect (clipRect);
+}
+
+void GUIStyle::SetStyleState (int stateIndex, ColorRGBAf textColor, Texture2D *background) {
+ GUIStyleState *gss = &m_Normal;
+ gss += stateIndex;
+ gss->background = background;
+ gss->textColor = textColor;
+}
+
+void GUIStyle::SetGUIClipRect (const Rectf &screenRect)
+{
+ s_GUIClipRect = screenRect;
+ Matrix4x4f clipMatrix;
+ clipMatrix.SetIdentity();
+ // In a divide-by-zero case, set these to infinity, so we don't render anything.
+ if (screenRect.width > 0.0f)
+ clipMatrix.Get (0,0) = (kGUIClipTextureSize-2.0f)/kGUIClipTextureSize / screenRect.width;
+ else
+ clipMatrix.Get (0,0) = numeric_limits<float>::infinity ();
+ if (screenRect.height > 0.0f)
+ clipMatrix.Get (1,1) = (kGUIClipTextureSize-2.0f)/kGUIClipTextureSize / screenRect.height;
+ else
+ clipMatrix.Get (1,1) = numeric_limits<float>::infinity ();
+ clipMatrix.Get (0,3) = -screenRect.x * clipMatrix.Get (0,0) + 1.0f/kGUIClipTextureSize;
+ clipMatrix.Get (1,3) = -screenRect.y * clipMatrix.Get (1,1) + 1.0f/kGUIClipTextureSize;
+ clipMatrix.Get (2,2) = clipMatrix.Get (3,3) = 0; // Kill all perspective/depth: Just put 1 in ZW texcoords
+ clipMatrix.Get (2,3) = clipMatrix.Get (3,3) = 1; // Kill all perspective/depth: Just put 1 in ZW texcoords
+ GetGfxDevice().GetBuiltinParamValues().SetMatrixParam(kShaderMatGUIClip, clipMatrix);
+}
+
+Rectf GUIStyle::GetGUIClipRect ()
+{
+ return s_GUIClipRect;
+}
+
+MonoBehaviour* GetBuiltinSkin (int skin)
+{
+ // 0 == Game, 1 == Editor Light, 2 = Editor Dark
+ static PPtr<MonoBehaviour> skinObject[3] = { NULL, NULL, NULL };
+
+#if UNITY_EDITOR
+ // Load skins (editor version)
+ if (!skinObject[0] || !skinObject[1] || !skinObject[2])
+ {
+ // If this is a devel buildwe want to try and load the skins from the opened project
+ // (super useful when skinning the app).
+ if (IsDeveloperBuild ())
+ {
+ // Try to load the skins from the current project
+ skinObject[0] = dynamic_pptr_cast<MonoBehaviour*> (GetMainAsset ("Assets/DefaultResources/GameSkin/GameSkin.GUISkin"));
+ skinObject[1] = dynamic_pptr_cast<MonoBehaviour*> (GetMainAsset (AppendPathName ("Assets/Editor Default Resources/", EditorResources::kLightSkinPath)));
+ skinObject[2] = dynamic_pptr_cast<MonoBehaviour*> (GetMainAsset (AppendPathName ("Assets/Editor Default Resources/", EditorResources::kDarkSkinPath)));
+ }
+
+ // Load the game skin.
+ // We can not mark this object as dont save, because that will make it not be unloaded when unloading the player.
+ // When that happens in the player the serialized state will be lost. So instead we let it be unloaded and on next
+ // load it will be reloaded from disk.
+ if (!skinObject[0])
+ {
+ Object *obj = GetBuiltinResourceManager ().GetResource (ClassID(MonoBehaviour), "GameSkin/GameSkin.guiskin");
+ skinObject[0] = static_cast<MonoBehaviour*> (obj);
+ }
+
+ // Load the light inspector skin.
+ if (!skinObject[1])
+ skinObject[1] = GetEditorAssetBundle()->Get<MonoBehaviour>(EditorResources::kLightSkinPath);
+
+ // Load the dark inspector skin.
+ if (!skinObject[2])
+ skinObject[2] = GetEditorAssetBundle()->Get<MonoBehaviour>(EditorResources::kDarkSkinPath);
+ }
+#else
+ // Players are much easier: we just load the game skin.
+ if (!skinObject[0])
+ {
+ Object *obj = GetBuiltinResourceManager ().GetResource (ClassID(MonoBehaviour), "GameSkin/GameSkin.guiskin");
+ skinObject[0] = static_cast<MonoBehaviour*> (obj);
+ }
+#endif
+
+ return skinObject[skin];
+}
+
+MonoBehaviour* GetDefaultSkin (int skinMode)
+{
+#if UNITY_EDITOR
+ if (skinMode == 0)
+ return GetBuiltinSkin (0);
+ return GetBuiltinSkin (GetEditorResources().GetSkinIdx () + 1);
+#else
+ return GetBuiltinSkin (0);
+#endif
+}
diff --git a/Runtime/IMGUI/GUIStyle.h b/Runtime/IMGUI/GUIStyle.h
new file mode 100644
index 0000000..d1af3ac
--- /dev/null
+++ b/Runtime/IMGUI/GUIStyle.h
@@ -0,0 +1,297 @@
+#ifndef GUISTYLE_H
+#define GUISTYLE_H
+
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/IMGUI/TextUtil.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+
+class Font;
+struct MonoString;
+struct GUIState;
+struct GUIContent;
+class TextMeshGenerator2;
+struct GUIGraphicsCacheBlock;
+class GUIClipRegion;
+
+struct GUIStyleState {
+ DECLARE_SERIALIZE (GUIStyleState)
+
+ /// Background image used by this style
+ PPtr<Texture2D> background;
+ /// The color of the text
+ ColorRGBAf textColor;
+
+ GUIStyleState () : textColor (0,0,0,1) {}
+ GUIStyleState (const GUIStyleState &other) : background (other.background), textColor (other.textColor) {}
+};
+
+template<class TransferFunc>
+void GUIStyleState::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer (background, "m_Background");
+ transfer.Transfer (textColor, "m_TextColor");
+}
+
+/// Positioning of the image and the text withing a GUIStyle
+enum ImagePosition {
+ /// Image is to the left of the text.
+ kImageLeft = 0,
+ /// Image is above the text.
+ kImageAbove = 1,
+ /// Only the image is displayed.
+ kImageOnly = 2,
+ /// Only the text is displayed.
+ kTextOnly = 3
+};
+
+
+
+struct RectOffset {
+ DECLARE_SERIALIZE (RectOffset)
+
+ int left, right, top, bottom;
+
+ RectOffset () { left = right = top = bottom = 0; }
+ RectOffset (const RectOffset &other)
+ {
+ left = other.left;
+ right = other.right;
+ top = other.top;
+ bottom = other.bottom;
+ }
+
+ int GetHorizontal () { return left + right; }
+ int GetVertical () { return top + bottom; }
+
+ Rectf Add (const Rectf &r) const
+ {
+ return MinMaxRect (r.x - left, r.y - top, r.GetRight() + right, r.GetBottom() + bottom);
+ }
+
+ Vector2f Size () const {
+ return Vector2f (left + right, top + bottom);
+ }
+ Rectf Remove (const Rectf &r) const
+ {
+ return MinMaxRect (r.x + left, r.y + top, r.GetRight() - right, r.GetBottom() - bottom);
+ }
+};
+
+template<class TransferFunc>
+void RectOffset::Transfer (TransferFunc& transfer)
+{
+ transfer.Transfer (left, "m_Left");
+ transfer.Transfer (right, "m_Right");
+ transfer.Transfer (top, "m_Top");
+ transfer.Transfer (bottom, "m_Bottom");
+}
+
+class GUIStyle {
+ public:
+ DECLARE_SERIALIZE (GUIStyle);
+ GUIStyle ();
+ GUIStyle (const GUIStyle &other);
+ UnityStr m_Name;
+
+ GUIStyleState m_Normal;
+ GUIStyleState m_Hover;
+ GUIStyleState m_Active;
+ GUIStyleState m_Focused;
+ GUIStyleState m_OnNormal;
+ GUIStyleState m_OnHover;
+ GUIStyleState m_OnActive;
+ GUIStyleState m_OnFocused;
+
+ /// Border of the background images
+ RectOffset m_Border;
+
+ /// Spacing between this element and ones next to it
+ RectOffset m_Margin;
+
+ /// Distance from outer edge to contents
+ RectOffset m_Padding;
+
+ /// Extra size to use for the background images.
+ RectOffset m_Overflow;
+
+ /// The font to use. If not set, the font is read from the main GUISkin
+ PPtr<Font> m_Font;
+
+ /// Text alignment.
+ int m_Alignment; ///< enum { Upper Left = 0, Upper Center = 1, Upper Right = 2, Middle Left = 3, Middle Center = 4, Middle Right = 5, Lower Left = 6, Lower Center = 7, Lower Right = 8 } How is the content placed inside the control.
+
+ /// Word wrap the text?
+ bool m_WordWrap;
+
+ /// Use HTML-style markup
+ bool m_RichText;
+
+ /// Clipping mode to use.
+ int m_Clipping; ///< enum { Overflow = 0, Clip = 1 } What happens with content that goes outside the control
+
+ /// How image and text is combined.
+ int m_ImagePosition; ///< enum { Image Left = 0, Image Above = 1, Image Only = 2, Text Only = 3 } How text and image is placed in relation to each other.
+
+ /// Pixel offset to apply to the content of this GUIstyle.
+ Vector2f m_ContentOffset;
+
+ /// Clip offset
+ Vector2f m_ClipOffset;
+
+ float m_FixedWidth; ///< If non-0, that axis is always draw at the specified size.
+ float m_FixedHeight; ///< If non-0, that axis is always draw at the specified size.
+
+ /// The font size to use. Set to 0 to use default font size. Only applicable for dynamic fonts.
+ int m_FontSize;
+
+ /// The font style to use. Only applicable for dynamic fonts.
+ int m_FontStyle; ///< enum { Normal = 0, Bold = 1, Italic = 2, Bold and Italic = 3 } Only applicable for dynamic fonts.
+
+ bool m_StretchWidth, m_StretchHeight;
+
+ /// Draw this GUI style
+ /// screenRect: Rectangle for the border
+ /// content: The text/image to stuff inside
+ /// isHover: Is the mouse over the element
+ /// isActive: Does the element have keyboard focus
+ /// on: Is the element on (as in a togglebutton)
+ void Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const ;
+
+ /// Draw this GUI style
+ /// screenRect: Rectangle for the border
+ /// content: The text/image to stuff inside
+ /// id: The controlID mouse of the element
+ /// isActive: Does the element have keyboard focus
+ /// on: Is the element on (as in a togglebutton)
+ void Draw (GUIState &state, const Rectf &screenRect, GUIContent &content, int controlID, bool on) const ;
+
+
+ /// screenRect: Rectangle for the border
+ /// content: The text/image to stuff inside
+ /// isHover: Is the mouse over the element
+ /// isActive: Does the element have keyboard focus
+ /// on: Is the element on (as in a togglebutton)
+ /// cursorFirst, last Where is the text selection
+ void DrawWithTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, bool isHover, bool isActive, bool on, bool hasKeyboardFocus, bool drawSelectionAsComposition, int cursorFirst, int cursorLast, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const;
+
+ /// screenRect: Rectangle for the border
+ /// text/image: The text/image to stuff inside
+ /// position Where is the text selection
+ void DrawCursor(GUIState &state, const Rectf &screenRect, GUIContent &content, int position, const ColorRGBAf &cursorColor) const;
+
+ /// Calculate the min & max widths of this element to correctly render the content
+ void CalcMinMaxWidth (GUIContent &content, float *minWidth, float *maxWidth) const;
+ /// Calculate the height of a component given a specific width
+ float CalcHeight (GUIContent &content, float width) const;
+ /// Calculate the size
+ Vector2f CalcSize (GUIContent &content) const;
+ /// Get the position of a specific character (used for finding out where to draw the cursor)
+ Vector2f GetCursorPixelPosition (const Rectf &screenRect, GUIContent &content, int cursorStringIndex) const;
+ /// Get the index of a given pixel position (used for finding out where in the string of a textfield the user clicked)
+ int GetCursorStringIndex (const Rectf &screenRect, GUIContent &content, const Vector2f &cursorPixelPosition) const;
+
+ /// Get the height of one line, in pixels...
+ float GetLineHeight () const;
+
+ /// returns number of characters that can fit within width, returns -1 if fails to shorten string
+ int GetNumCharactersThatFitWithinWidth (const UTF16String &text, float width) const;
+
+ /// Set the default font (used by GUISkin)
+ static void SetDefaultFont (Font *font);
+ static Font*GetDefaultFont ();
+
+ static Texture2D* GetClipTexture ();
+
+ Font& GetCurrentFont () const;
+ static Font &GetBuiltinFont ();
+
+ /// Set a specific style state. Used when changing style states from mono
+ void SetStyleState (int stateIndex, ColorRGBAf textColor, Texture2D *background);
+
+ // Calculate where text and image go inside screenRect in order to fit imageSize and textSize
+ static void CalcContentRects (const Rectf &contentRect, Vector2f imageSize, Vector2f textSize, Rectf &imageRect, Rectf &textRect, float &width, float &height, int imagePosition, int alignment, Vector2f contentOffset);
+
+ // Used by OptimizedGUIBlock
+ static Rectf GetGUIClipRect ();
+ static void SetGUIClipRect (const Rectf& rect);
+
+ static void SetMouseTooltip (GUIState& state, const UTF16String& tooltip, const Rectf& screenRect);
+ private:
+
+ /// Figure out which GUIStyle to use
+ const GUIStyleState *GetGUIStyleState (GUIState &state, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) const;
+ /// Draw the background GUIStyle without any contents
+ void DrawBackground (GUIState &state, const Rectf &screenRect, const GUIStyleState *gss) const;
+
+
+ /// Draw the contents
+ void DrawContent (GUIState &state, const Rectf &screenRect, GUIContent &content, const GUIStyleState *gss) const;
+ /// Draw the text selection highlight.
+ void DrawTextSelection (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const ColorRGBAf &cursorColor, const ColorRGBAf &selectionColor) const;
+ void DrawTextUnderline (GUIState &state, const Rectf &screenRect, GUIContent &content, int first, int last, const GUIStyleState *gss) const;
+
+ // set up all the static text-rendering vars from this settings.
+ void RenderText (const Rectf &screenRect, TextMeshGenerator2 &tmgen, ColorRGBAf color) const;
+
+ TextMeshGenerator2 *GetGenerator (const Rectf &screenRect, GUIContent &content, ColorRGBA32 color) const;
+ TextMeshGenerator2 *GetGenerator (const Rectf &screenRect, GUIContent &content) const;
+
+ // Clamp a screenRect to be set to fixedWidth & height
+ Rectf ClampRect (const Rectf &screenRect) const;
+
+};
+template<class TransferFunc>
+void GUIStyle::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (m_Name);
+ transfer.Align ();
+ TRANSFER (m_Normal);
+ TRANSFER (m_Hover);
+ TRANSFER (m_Active);
+ TRANSFER (m_Focused);
+ TRANSFER (m_OnNormal);
+ TRANSFER (m_OnHover);
+ TRANSFER (m_OnActive);
+ TRANSFER (m_OnFocused);
+
+ TRANSFER (m_Border);
+ TRANSFER (m_Margin);
+ TRANSFER (m_Padding);
+ TRANSFER (m_Overflow);
+ TRANSFER (m_Font);
+ TRANSFER (m_FontSize);
+ TRANSFER (m_FontStyle);
+ TRANSFER (m_Alignment);
+ TRANSFER (m_WordWrap);
+ TRANSFER (m_RichText);
+ transfer.Align();
+ transfer.Transfer (m_Clipping, "m_TextClipping");
+ TRANSFER (m_ImagePosition);
+ TRANSFER (m_ContentOffset);
+ TRANSFER (m_FixedWidth);
+ TRANSFER (m_FixedHeight);
+ TRANSFER (m_StretchWidth);
+ TRANSFER (m_StretchHeight);
+ transfer.Align();
+};
+
+Material* GetGUIBlitMaterial ();
+Material* GetGUIBlendMaterial ();
+
+void InitializeGUIClipTexture();
+
+// skin:
+// Game = 0
+// Light Skin = 1
+// Dark Skin = 2
+MonoBehaviour* GetBuiltinSkin (int skin);
+// skinMode:
+// Game = 0
+// Editor = 1
+MonoBehaviour* GetDefaultSkin (int skinMode);
+
+#endif
diff --git a/Runtime/IMGUI/GUITest.cpp b/Runtime/IMGUI/GUITest.cpp
new file mode 100644
index 0000000..2214097
--- /dev/null
+++ b/Runtime/IMGUI/GUITest.cpp
@@ -0,0 +1,209 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+
+// Disabled GUITests for lack of acceptable framework for native tests hitting scripting invocations (GetControlID). Rene is on the case!
+#if 0
+//#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Misc/InputEvent.h"
+#include "Runtime/IMGUI/IDList.h"
+
+static ObjectGUIState* gObjectGUIState;
+
+// Set up a clean GUIState for testing purposes. Delete it with DeleteTestGUIState
+static GUIState &MakeTestGUIState ()
+{
+ GUIState *state = new GUIState ();
+ state->m_EternalGUIState = new EternalGUIState ();
+ state->m_CurrentEvent = new InputEvent();
+ state->m_CurrentEvent->Init();
+ gObjectGUIState = new ObjectGUIState();
+ return *state;
+}
+
+static void DeleteTestGUIState (GUIState &state)
+{
+ delete gObjectGUIState;
+ delete &state;
+}
+
+static void BeginOnGUI (GUIState &state, InputEvent evt)
+{
+ *state.m_CurrentEvent = evt;
+ state.BeginOnGUI (*gObjectGUIState);
+}
+
+static void EndOnGUI (GUIState &state)
+{
+ state.EndOnGUI ();
+}
+
+static InputEvent MakeLayoutEvent ()
+{
+ InputEvent e;
+ e.type = InputEvent::kLayout;
+ return e;
+}
+
+static InputEvent MakeRepaintEvent ()
+{
+ InputEvent e;
+ e.type = InputEvent::kRepaint;
+ return e;
+}
+
+static InputEvent MakeKeyDownEvent (char c)
+{
+ InputEvent e;
+ e.type = InputEvent::kKeyDown;
+ e.character = c;
+ return e;
+}
+
+SUITE ( GUITests )
+{
+TEST (GUITests_IDListGeneration)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+ BeginOnGUI (state, MakeLayoutEvent ());
+ int v1 = state.GetControlID (1, kPassive);
+ int v2 = state.GetControlID (1, kPassive);
+ int v3 = state.GetControlID (2, kPassive);
+ EndOnGUI (state);
+
+ // Check we get the same IDs next event
+ BeginOnGUI (state, MakeRepaintEvent ());
+ CHECK_EQUAL (v1, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (v2, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (v3, state.GetControlID (2, kPassive));
+ EndOnGUI (state);
+
+ //check we correctly handle something going away.
+ BeginOnGUI (state, MakeRepaintEvent ());
+ CHECK_EQUAL (v1, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (v3, state.GetControlID (2, kPassive));
+ EndOnGUI (state);
+
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+
+TEST (GUITests_IDListTabFinding)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+
+ // Init & set up keycontrol
+ BeginOnGUI (state, MakeLayoutEvent ());
+ int v1 = state.GetControlID (1, kKeyboard);
+ state.GetControlID (1, kPassive);
+ int v2 = state.GetControlID (1, kKeyboard);
+ int v3 = state.GetControlID (2, kKeyboard);
+ EndOnGUI (state);
+ state.m_MultiFrameGUIState.m_KeyboardControl = v2;
+
+ // Run again to make sure we have the values - they are only recorded on keydown events
+ BeginOnGUI (state, MakeKeyDownEvent ('\t'));
+ state.GetControlID (1, kKeyboard);
+ state.GetControlID (1, kPassive);
+ state.GetControlID (1, kKeyboard);
+ state.GetControlID (2, kKeyboard);
+
+ CHECK (state.m_ObjectGUIState->m_IDList.HasKeyboardControl());
+ CHECK_EQUAL (v1, state.m_ObjectGUIState->m_IDList.GetPreviousKeyboardControlID());
+ CHECK_EQUAL (v3, state.m_ObjectGUIState->m_IDList.GetNextKeyboardControlID());
+ CHECK_EQUAL (v1, state.m_ObjectGUIState->m_IDList.GetFirstKeyboardControlID());
+ CHECK_EQUAL (v3, state.m_ObjectGUIState->m_IDList.GetLastKeyboardControlID());
+
+ EndOnGUI (state);
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+
+TEST (GUITests_IDListNamedKeyControls)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+
+ BeginOnGUI (state, MakeLayoutEvent ());
+ state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v1");
+ int v1 = state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v2");
+ state.GetControlID (1, kPassive);
+ int v2 = state.GetControlID (1, kKeyboard);
+ state.GetControlID (2, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v3fake");
+ EndOnGUI (state);
+
+ // Run event chain so we can move to past events
+ BeginOnGUI (state, MakeRepaintEvent ());
+ state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v1");
+ state.GetControlID (1, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v2");
+ state.GetControlID (1, kPassive);
+ state.GetControlID (1, kKeyboard);
+ state.GetControlID (2, kKeyboard);
+ state.SetNameOfNextKeyboardControl ("v3fake");
+
+ CHECK_EQUAL (v1, state.GetIDOfNamedControl ("v1"));
+ CHECK_EQUAL (v2, state.GetIDOfNamedControl ("v2"));
+ EndOnGUI (state);
+
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+
+TEST (GUITests_IDListNativeGetsKBControl)
+{
+ GUIState &state = MakeTestGUIState ();
+ state.BeginFrame ();
+
+ // First pass: Layout... get all the control ID's
+ BeginOnGUI (state, MakeLayoutEvent ());
+
+ // padding control at start
+ int id1 = state.GetControlID (1, kNative);
+
+ // first named control
+ state.SetNameOfNextKeyboardControl ("named1");
+ int id2 = state.GetControlID (1, kNative);
+
+ // second named control... passive should be ignored!
+ state.SetNameOfNextKeyboardControl ("named2");
+ int id3 = state.GetControlID (1, kPassive);
+ int id4 = state.GetControlID (1, kNative);
+
+ // extra trailing controls
+ int id5 = state.GetControlID (1, kNative);
+
+ // check the named ID's
+ CHECK_EQUAL (id2, state.GetIDOfNamedControl ("named1"));
+ CHECK_EQUAL (id4, state.GetIDOfNamedControl ("named2"));
+ EndOnGUI (state);
+
+ // Now simulate a repaint... the control ID's should be the same as before!
+ BeginOnGUI (state, MakeRepaintEvent ());
+ CHECK_EQUAL (id1, state.GetControlID (1, kNative));
+ state.SetNameOfNextKeyboardControl ("v1");
+ CHECK_EQUAL (id2, state.GetControlID (1, kNative));
+ state.SetNameOfNextKeyboardControl ("v2");
+ CHECK_EQUAL (id3, state.GetControlID (1, kPassive));
+ CHECK_EQUAL (id4, state.GetControlID (1, kNative));
+ CHECK_EQUAL (id5, state.GetControlID (1, kNative));
+
+ // also check that the named events STILL have the same ID's
+ CHECK_EQUAL (id2, state.GetIDOfNamedControl ("named1"));
+ CHECK_EQUAL (id4, state.GetIDOfNamedControl ("named2"));
+ EndOnGUI (state);
+
+ state.EndFrame ();
+ DeleteTestGUIState(state);
+}
+}
+#endif
diff --git a/Runtime/IMGUI/GUIToggle.cpp b/Runtime/IMGUI/GUIToggle.cpp
new file mode 100644
index 0000000..a8fde5a
--- /dev/null
+++ b/Runtime/IMGUI/GUIToggle.cpp
@@ -0,0 +1,76 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/GUIToggle.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+namespace IMGUI
+{
+
+static const int kGUIToggleHash = -1784436876;
+
+bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style, int id)
+{
+ InputEvent &evt (*state.m_CurrentEvent);
+ switch (GetEventTypeForControl (state, evt, id))
+ {
+ case InputEvent::kMouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ GrabMouseControl (state, id);
+ evt.Use ();
+ }
+ break;
+ case InputEvent::kKeyDown:
+ if (evt.character == 32 && state.m_MultiFrameGUIState.m_KeyboardControl == id)
+ {
+ evt.Use ();
+ state.m_OnGUIState.m_Changed = true;
+ return !value;
+ }
+ break;
+ case InputEvent::kMouseUp:
+ if (HasMouseControl (state, id))
+ {
+ ReleaseMouseControl (state);
+
+ // If we got the mousedown, the mouseup is ours as well
+ // (no matter if the click was in the button or not)
+ evt.Use ();
+
+ // toggle the passed-in value if the mouse was over the button & return true
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ state.m_OnGUIState.m_Changed = true;
+ return !value;
+ }
+ }
+ break;
+ case InputEvent::kMouseDrag:
+ if (HasMouseControl (state, id))
+ evt.Use ();
+ break;
+
+ case InputEvent::kRepaint:
+ style.Draw (state, position, content, id, value);
+ break;
+ }
+ return value;
+}
+
+bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style)
+{
+ int id = GetControlID (state, kGUIToggleHash, kNative, position);
+ return GUIToggle (state, position, value, content, style, id);
+}
+
+} \ No newline at end of file
diff --git a/Runtime/IMGUI/GUIToggle.h b/Runtime/IMGUI/GUIToggle.h
new file mode 100644
index 0000000..3f9d619
--- /dev/null
+++ b/Runtime/IMGUI/GUIToggle.h
@@ -0,0 +1,16 @@
+#ifndef GUITOGGLE_H
+#define GUITOGGLE_H
+
+#include "Runtime/Math/Rect.h"
+
+struct GUIState;
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style, int id);
+ bool GUIToggle (GUIState &state, const Rectf &position, bool value, GUIContent &content, GUIStyle &style);
+}
+
+#endif
diff --git a/Runtime/IMGUI/GUIWindows.cpp b/Runtime/IMGUI/GUIWindows.cpp
new file mode 100644
index 0000000..30a3f9b
--- /dev/null
+++ b/Runtime/IMGUI/GUIWindows.cpp
@@ -0,0 +1,748 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#if ENABLE_UNITYGUI
+#include "Runtime/IMGUI/GUIWindows.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingObjectWithIntPtrField.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+
+#include <algorithm> // for std::sort
+namespace IMGUI
+{
+ static Vector2f s_DragStartPos (0,0); // Start of the drag (mousePosition)
+ static Vector2f s_DragStartSize (0,0); // Value at start of drag.
+
+ GUIWindow::GUIWindow ()
+ {
+ m_Delegate = m_Skin = 0;
+ m_Moved = m_ForceRect = false;
+ m_ID = 0;
+ m_Style = 0;
+ }
+
+ GUIWindow::~GUIWindow ()
+ {
+ ReleaseScriptingObjects ();
+ }
+
+
+ void GUIWindow::ReleaseScriptingObjects ()
+ {
+ if (m_Delegate)
+ {
+ scripting_gchandle_free (m_Delegate);
+ m_Delegate = 0;
+ }
+ if (m_Skin)
+ {
+ scripting_gchandle_free (m_Skin);
+ m_Skin = 0;
+ }
+ if (m_Style)
+ {
+ scripting_gchandle_free (m_Style);
+ m_Style = 0;
+ }
+ }
+
+ void GUIWindow::OnGUI (GUIState& state)
+ {
+ InputEvent& evt (*state.m_CurrentEvent);
+ // Set up the state that was recorded for this window
+ state.m_OnGUIState.m_Color = m_Color;
+ state.m_OnGUIState.m_BackgroundColor = m_BackgroundColor;
+ state.m_OnGUIState.m_ContentColor = m_ContentColor;
+ state.m_OnGUIState.m_Enabled = m_Enabled;
+ state.m_CanvasGUIState.m_GUIClipState.SetMatrix (evt, m_Matrix);
+ state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow = this;
+
+ // Block OnHover calls into the scene if the window contains the mouse
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (evt.type == InputEvent::kRepaint && m_Position.Contains (evt.touch.pos))
+#else
+ if (evt.type == InputEvent::kRepaint && m_Position.Contains (evt.mousePosition))
+#endif
+ state.m_CanvasGUIState.m_IsMouseUsed = true;
+
+ // Disable drawing keyboard focus if window doesn't have focus.
+ int hadShowKeyboardControl = state.m_OnGUIState.m_ShowKeyboardControl;
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ state.m_OnGUIState.m_ShowKeyboardControl &= winState->m_FocusedWindow == m_ID;
+
+ // If it's a repaint event, draw the background
+ ScriptingObjectPtr style = scripting_gchandle_get_target (m_Style);
+ if (style && evt.type == InputEvent::kRepaint)
+ {
+ GUIStyle* _style = ScriptingObjectWithIntPtrField<GUIStyle> (style).GetPtr();
+#if ENABLE_NEW_EVENT_SYSTEM
+ _style->Draw (state, m_Position, m_Title, m_Position.Contains (evt.touch.pos), false, state.m_MultiFrameGUIState.m_Windows->m_FocusedWindow == m_ID, false);
+#else
+ _style->Draw (state, m_Position, m_Title, m_Position.Contains (evt.mousePosition), false, state.m_MultiFrameGUIState.m_Windows->m_FocusedWindow == m_ID, false);
+#endif
+ }
+
+ state.m_CanvasGUIState.m_GUIClipState.Push (*state.m_CurrentEvent, m_Position, Vector2f::zero, Vector2f::zero, false);
+ ObjectGUIState* old = state.m_ObjectGUIState;
+ state.BeginOnGUI (m_ObjectGUIState);
+
+ // No exception handling here on purpose.
+ ScriptingInvocation invocation(MONO_COMMON.callGUIWindowDelegate);
+ invocation.AddObject(scripting_gchandle_get_target (m_Delegate));
+ invocation.AddInt(m_ID);
+ invocation.AddObject(scripting_gchandle_get_target (m_Skin));
+ invocation.AddInt((int)m_ForceRect);
+ invocation.AddFloat(m_Position.width);
+ invocation.AddFloat(m_Position.height);
+ invocation.AddObject(style);
+
+ state.m_OnGUIState.m_ShowKeyboardControl = winState->m_FocusedWindow == m_ID;
+
+ // we need to catch a log our own exceptions to properly handle ExitGUIException
+ ScriptingExceptionPtr exception = NULL;
+ invocation.logException = false;
+
+ invocation.Invoke (&exception);
+
+ if (exception)
+ {
+ // TODO: Kill GUI all the way down to the MonoBehaviour
+#if ENABLE_MONO
+ void* excparams[] = {exception};
+ MonoObject* res = CallStaticMonoMethod("GUIUtility", "EndGUIFromException", excparams);
+ if (!MonoObjectToBool(res))
+ ::Scripting::LogException(exception, 0);
+#endif
+ }
+
+ state.EndOnGUI ();
+ state.m_ObjectGUIState = old;
+ state.m_CanvasGUIState.m_GUIClipState.Pop (evt);
+ state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow = NULL;
+
+ // make sure that the rest of the script shows keyboard focus
+ state.m_OnGUIState.m_ShowKeyboardControl = hadShowKeyboardControl;
+ }
+
+ Rectf DoWindow (GUIState& state, int id, const Rectf &clientRect, ScriptingObjectPtr delegate, GUIContent& title, ScriptingObjectPtr style, ScriptingObjectPtr guiSkin, bool forceRectOnLayout, bool isModal)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (winState == NULL)
+ state.m_MultiFrameGUIState.m_Windows = winState = new GUIWindowState ();
+
+ GUIWindow* win = winState->GetWindow (id);
+ if (!win)
+ {
+ if(isModal && winState->m_ModalWindow != NULL)
+ {
+ DebugStringToFile ("You cannot show two modal windows at once", 0, __FILE__, __LINE__, kError);
+ return clientRect;
+ }
+ win = new GUIWindow();
+ win->m_ID = id;
+ win->m_Depth = -1;
+
+ if(isModal)
+ {
+ winState->m_ModalWindow = win;
+ }
+ else
+ {
+ winState->m_WindowList.push_back(win);
+ winState->m_LayersChanged = true;
+ }
+ }
+
+ if(isModal)
+ {
+ if(winState->m_ModalWindow == NULL)
+ {
+ winState->m_ModalWindow = win;
+
+ // If window is in the window list, remove it.
+ GUIWindowState::WindowList::iterator i = std::find(winState->m_WindowList.begin(),
+ winState->m_WindowList.end(),
+ win);
+ if(i != winState->m_WindowList.end())
+ {
+ winState->m_WindowList.erase(i);
+ winState->m_LayersChanged = true;
+ }
+ }
+ else if(winState->m_ModalWindow != win)
+ {
+ // This can happen if you already have a modal window open, and attempt
+ // to show an already-created window as a modal window.
+ DebugStringToFile ("Attempting to show modal windows at once; the newer windows will not be modal", 0, __FILE__, __LINE__, kError);
+ }
+ }
+
+ if (!win->m_Moved)
+ win->m_Position = clientRect;
+ else
+ win->m_Moved = false;
+
+ win->m_Title = title;
+
+ win->ReleaseScriptingObjects ();
+ win->m_Style = scripting_gchandle_new (style);
+ win->m_Delegate = scripting_gchandle_new (delegate);
+ win->m_Skin = scripting_gchandle_new (guiSkin);
+
+ win->m_Used = true;
+ win->m_Enabled = state.m_OnGUIState.m_Enabled;
+ win->m_Color = state.m_OnGUIState.m_Color;
+ win->m_BackgroundColor = state.m_OnGUIState.m_BackgroundColor;
+ win->m_ContentColor = state.m_OnGUIState.m_ContentColor;
+ win->m_Matrix = state.m_CanvasGUIState.m_GUIClipState.GetMatrix();
+ win->m_ForceRect = forceRectOnLayout;
+
+ #if !GAMERELEASE
+ if (state.m_MultiFrameGUIState.m_Windows->m_CurrentWindow)
+ ErrorString("GUI Error: You called GUI.Window inside a another window's function. Ensure to call it in a OnGUI code path.");
+ #endif
+
+ return win->m_Position;
+
+ }
+
+ void DragWindow (GUIState &state, const Rectf &position)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ GUIWindow* win = winState ? winState->m_CurrentWindow : NULL;
+ if (win == NULL)
+ {
+ ErrorString ("Dragwindow can only be called within a window callback");
+ return;
+ }
+
+ int id = IMGUI::GetControlID (state, 0, kPassive);
+
+ InputEvent& evt (*state.m_CurrentEvent);
+
+ switch (GetEventTypeForControl (state, evt, id)) {
+ case InputEvent::kMouseDown:
+ // If the mouse is inside the button, we say that we're the hot control
+#if ENABLE_NEW_EVENT_SYSTEM
+ if (position.Contains (evt.touch.pos))
+#else
+ if (position.Contains (evt.mousePosition))
+#endif
+ {
+ GrabMouseControl (state, id);
+ evt.Use ();
+ //Matrix4x4f mat = win->m_Matrix;
+ Vector2f mouseAbs = state.m_CanvasGUIState.m_GUIClipState.GetAbsoluteMousePosition();
+ Vector3f windowAbs = win->m_Matrix.MultiplyPoint3 (Vector3f (win->m_Position.x, win->m_Position.y, 0));
+ s_DragStartPos = mouseAbs - Vector2f (windowAbs.x, windowAbs.y);
+ s_DragStartSize = Vector2f (win->m_Position.width, win->m_Position.height);
+ }
+ break;
+ case InputEvent::kMouseUp:
+ if (GetHotControl (state) == id)
+ {
+ ReleaseMouseControl (state);
+ evt.Use ();
+ }
+ break;
+ case InputEvent::kMouseDrag:
+ if (GetHotControl (state) == id)
+ {
+ Matrix4x4f mat;
+ Matrix4x4f::Invert_Full (win->m_Matrix, mat);
+ Vector2f mouseAbs = state.m_CanvasGUIState.m_GUIClipState.GetAbsoluteMousePosition();
+
+ Vector3f deltaPos (mouseAbs.x - s_DragStartPos.x, mouseAbs.y - s_DragStartPos.y, 0);
+ deltaPos = mat.MultiplyPoint3 (deltaPos);
+ win->m_Position = Rectf (deltaPos.x, deltaPos.y, s_DragStartSize.x, s_DragStartSize.y);
+
+ win->m_Moved = true;
+ evt.Use ();
+ }
+ break;
+ }
+
+ }
+
+ struct GUIStatePropertiesCache
+ {
+ Matrix4x4f mat;
+ ColorRGBAf color;
+ ColorRGBAf contentColor;
+ ColorRGBAf backgroundColor;
+ bool enabled;
+ };
+
+ void CacheGUIStateProperties (GUIState &state, GUIStatePropertiesCache &cache)
+ {
+ // Cache some GUIState properties to restore after we're done doing windows
+ cache.mat = state.m_CanvasGUIState.m_GUIClipState.GetMatrix();
+ cache.color = state.m_OnGUIState.m_Color;
+ cache.contentColor = state.m_OnGUIState.m_ContentColor;
+ cache.backgroundColor = state.m_OnGUIState.m_BackgroundColor;
+ cache.enabled = state.m_OnGUIState.m_Enabled;
+ }
+
+ void RestoreGUIStateProperties (GUIState &state, InputEvent &evt, GUIStatePropertiesCache &cache)
+ {
+ // Restore previous GUIState properties
+ state.m_CanvasGUIState.m_GUIClipState.SetMatrix (evt, cache.mat);
+ state.m_OnGUIState.m_Color = cache.color;
+ state.m_OnGUIState.m_ContentColor = cache.contentColor;
+ state.m_OnGUIState.m_BackgroundColor = cache.backgroundColor;
+ state.m_OnGUIState.m_Enabled = cache.enabled;
+ }
+
+ void BeginWindows (GUIState &state, bool setupClipping, bool ignoreModalWindow)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ InputEvent& evt (*state.m_CurrentEvent);
+ if (winState == NULL)
+ return;
+
+ GUIStatePropertiesCache oldProperties;
+ CacheGUIStateProperties (state, oldProperties);
+
+ if (setupClipping)
+ state.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (evt);
+
+ if (winState->m_LayersChanged)
+ winState->SortWindows();
+
+ // The window we want to pass the event on to (mouse & key mainly)
+ GUIWindow* win = NULL;
+
+ switch (evt.type) {
+ case InputEvent::kLayout:
+ // Before we process any events... Mark all windows as unused (we mark them as used when doing the layout event for all scripts)
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ (*i)->m_Used = false;
+
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ winState->m_ModalWindow->m_Used = false;
+ break;
+
+ // Dragging events go to the window UNDER the mouse
+ case InputEvent::kDragUpdated:
+ case InputEvent::kDragPerform:
+ case InputEvent::kDragExited:
+ if (!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->FindWindowUnderMouse (state);
+ break;
+
+ // If we have a hot control, we send mouseUp/mouseDrag event to the active window.
+ // If not, we send it to window under mouse.
+ case InputEvent::kMouseUp:
+ case InputEvent::kMouseDrag:
+ case InputEvent::kMouseMove:
+ if (!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else if (GetHotControl (state) == 0)
+ win = winState->FindWindowUnderMouse (state);
+ else
+ win = winState->GetWindow (winState->m_FocusedWindow);
+ break;
+
+ // Scroll wheel goes to window under mouse
+ // TODO: Maybe not the same for windows
+ case InputEvent::kScrollWheel:
+ if (!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->FindWindowUnderMouse (state);
+ break;
+ // mouse events should pick a specific window & bring that forwards...
+ case InputEvent::kMouseDown:
+ winState->m_FocusedWindow = -1;
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->FindWindowUnderMouse (state);
+
+ // If somebody got moved to front - we need to go over all windows and reset the window depth
+ if (win) {
+ win->m_Depth = -1;
+ winState->m_FocusedWindow = win->m_ID;
+ winState->SortWindows ();
+ }
+ break;
+ case InputEvent::kRepaint:
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_1_a1))
+ state.m_EternalGUIState->m_AllowHover = ((ignoreModalWindow || winState->m_ModalWindow == NULL) && winState->FindWindowUnderMouse (state) == NULL);
+ // We handle all repainting in EndWindows (so we can draw in reverse order on top of other stuff)
+ return;
+ default:
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ win = winState->m_ModalWindow;
+ else
+ win = winState->GetWindow (winState->m_FocusedWindow);
+ break;
+ }
+
+ // Pass the event on to the window
+ if (win != NULL && win->m_Delegate != 0)
+ {
+ win->OnGUI (state);
+
+ // Some events should not be passed down to the GUI or windows underneath the handled window
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ {
+ // If this is a scrollwheel or mousedown event, OR
+ // If this is a mouse move/drag or mouseup event AND we have no active control
+ // Ignore the event.
+ if(evt.type == InputEvent::kScrollWheel || evt.type == InputEvent::kMouseDown)
+ {
+ evt.type = InputEvent::kIgnore;
+ }
+ else if( (evt.type == InputEvent::kMouseMove
+ || evt.type == InputEvent::kMouseDrag
+ || evt.type == InputEvent::kMouseUp)
+ && IMGUI::GetHotControl (state) == 0)
+ {
+ evt.type = InputEvent::kIgnore;
+ }
+ }
+ }
+
+ RestoreGUIStateProperties (state, evt, oldProperties);
+
+ if (setupClipping)
+ state.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*state.m_CurrentEvent);
+ }
+
+ void EndWindows (GUIState &state, bool ignoreModalWindow)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (winState == NULL)
+ return;
+
+ GUIStatePropertiesCache oldProperties;
+ CacheGUIStateProperties (state, oldProperties);
+
+ InputEvent& evt (*state.m_CurrentEvent);
+ switch (evt.type) {
+ case InputEvent::kLayout:
+ {
+ // Remove unused windows (they have to be marked as Used during the Layout event.
+ bool clearFocus = true;
+ for (int i = winState->m_WindowList.size(); i--;)
+ {
+ GUIWindow* win = winState->m_WindowList[i];
+ if (!win->m_Used)
+ {
+ delete win;
+ winState->m_WindowList.erase(winState->m_WindowList.begin() + i);
+ winState->m_LayersChanged = true;
+ } else {
+ if (win->m_ID == winState->m_FocusedWindow)
+ clearFocus = false;
+ }
+ }
+
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL && !winState->m_ModalWindow->m_Used)
+ {
+ delete winState->m_ModalWindow;
+ winState->m_ModalWindow = NULL;
+ }
+
+ if (clearFocus)
+ winState->m_FocusedWindow = -1;
+
+ if (winState->m_LayersChanged)
+ winState->SortWindows ();
+
+ // Always run modal windows first
+ if(!ignoreModalWindow && winState->m_ModalWindow != NULL)
+ winState->m_ModalWindow->OnGUI(state);
+
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ {
+ // Send the layout event to the user's input code (also does the layouting from the C# delegate wrapper)
+ (*i)->OnGUI (state);
+ }
+ break;
+ }
+ case InputEvent::kRepaint:
+ GUIWindow* windowUnderMouse;
+
+ if(winState->m_ModalWindow != NULL)
+ windowUnderMouse = winState->m_ModalWindow;
+ else
+ windowUnderMouse = winState->FindWindowUnderMouse (state);
+
+ for (int i = winState->m_WindowList.size(); i--;)
+ {
+ GUIWindow* win = winState->m_WindowList[i];
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_1_a1))
+ state.m_EternalGUIState->m_AllowHover = (win == windowUnderMouse && winState->m_ModalWindow == NULL);
+ win->OnGUI (state);
+ }
+
+ if(ignoreModalWindow || winState->m_ModalWindow == NULL)
+ {
+ // Re-enable hovering for always-on-top normal GUIs when there's no modal GUI
+ state.m_EternalGUIState->m_AllowHover = true;
+ }
+ else
+ {
+ // Disable hovering for the non-modal GUI when we have one.
+ // Repainting happens in RepaintModalWindow.
+ state.m_EternalGUIState->m_AllowHover = false;
+ }
+
+ break;
+ }
+
+ RestoreGUIStateProperties (state, evt, oldProperties);
+
+ // Release objects if we don't have a modal window. If we do, this will be done later.
+ if (evt.type != InputEvent::kLayout && (ignoreModalWindow || winState->m_ModalWindow == NULL))
+ winState->ReleaseScriptingObjects();
+ }
+
+ void RepaintModalWindow(GUIState& state)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (winState == NULL)
+ return;
+
+ GUIStatePropertiesCache oldProperties;
+ CacheGUIStateProperties (state, oldProperties);
+
+ InputEvent& evt (*state.m_CurrentEvent);
+ if(evt.type == InputEvent::kRepaint)
+ {
+ state.m_EternalGUIState->m_AllowHover = true;
+
+ // Always run modal windows last so they paint on top.
+ if(winState->m_ModalWindow != NULL)
+ winState->m_ModalWindow->OnGUI(state);
+ }
+
+ if(evt.type != InputEvent::kLayout)
+ {
+ winState->ReleaseScriptingObjects();
+ }
+ }
+
+ GUIWindow *GetFocusedWindow (GUIState &state)
+ {
+ if (state.m_MultiFrameGUIState.m_Windows)
+ return state.m_MultiFrameGUIState.m_Windows->GetWindow (state.m_MultiFrameGUIState.m_Windows->m_FocusedWindow);
+ return NULL;
+ }
+
+
+ GUIWindowState::GUIWindowState ()
+ {
+ m_FocusedWindow = -1;
+ m_LayersChanged = false;
+ m_CurrentWindow = NULL;
+ m_ModalWindow = NULL;
+ }
+
+
+ GUIWindowState::~GUIWindowState ()
+ {
+ for (GUIWindowState::WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ delete *i;
+
+ if(m_ModalWindow != NULL)
+ {
+ delete m_ModalWindow;
+ m_ModalWindow = NULL;
+ }
+ }
+
+ GUIWindow* GUIWindowState::GetWindow (int windowId)
+ {
+ for (GUIWindowState::WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ {
+ if ((*i)->m_ID == windowId)
+ return *i;
+ }
+
+ if(m_ModalWindow != NULL && m_ModalWindow->m_ID == windowId)
+ {
+ return m_ModalWindow;
+ }
+
+ return NULL;
+ }
+
+ static bool SortTwoWindows(const GUIWindow* a, const GUIWindow* b)
+ {
+ return a->m_Depth < b->m_Depth;
+ }
+
+ void GUIWindowState::SortWindows ()
+ {
+ std::sort (m_WindowList.begin(), m_WindowList.end(), SortTwoWindows);
+ for (int i = 0; i < m_WindowList.size(); i++)
+ m_WindowList[i]->m_Depth = i;
+ }
+
+
+ GUIWindow* GUIWindowState::FindWindowUnderMouse (GUIState &state)
+ {
+ InputEvent evt (*state.m_CurrentEvent);
+
+#if ENABLE_NEW_EVENT_SYSTEM
+ if(m_ModalWindow != NULL && m_ModalWindow->m_Position.Contains(evt.touch.pos))
+#else
+ if(m_ModalWindow != NULL && m_ModalWindow->m_Position.Contains(evt.mousePosition))
+#endif
+ {
+ return m_ModalWindow;
+ }
+
+ for (GUIWindowState::WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ {
+ state.m_CanvasGUIState.m_GUIClipState.SetMatrix (evt, (*i)->m_Matrix);
+#if ENABLE_NEW_EVENT_SYSTEM
+ if ((*i)->m_Position.Contains (evt.touch.pos))
+#else
+ if ((*i)->m_Position.Contains (evt.mousePosition))
+#endif
+ return *i;
+ }
+ return NULL;
+ }
+
+ void GUIWindowState::ReleaseScriptingObjects ()
+ {
+ for (WindowList::iterator i = m_WindowList.begin(); i != m_WindowList.end(); i++)
+ (*i)->ReleaseScriptingObjects ();
+
+ if(m_ModalWindow != NULL)
+ {
+ m_ModalWindow->ReleaseScriptingObjects();
+ }
+ }
+
+ void MoveWindowFromLayout (GUIState &state, int windowID, const Rectf &rect)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ AssertIf (!winState);
+
+ GUIWindow* win = winState->GetWindow(windowID);
+ if (win && win->m_Position != rect)
+ {
+ win->m_Position = rect;
+ win->m_Moved = true;
+ }
+ }
+
+ Rectf GetWindowRect (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ AssertIf (!winState);
+
+ GUIWindow* win = winState->GetWindow (windowID);
+ if (win)
+ return win->m_Position;
+ return Rectf (0,0,0,0);
+ }
+
+ Rectf GetWindowsBounds (GUIState &state)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return Rectf(0,0,0,0);
+
+ GUIWindowState::WindowList &windows = winState->m_WindowList;
+
+ Rectf bounds (std::numeric_limits<float>::max (), std::numeric_limits<float>::max (), -std::numeric_limits<float>::max (), -std::numeric_limits<float>::max ());
+ for (GUIWindowState::WindowList::const_iterator i = windows.begin (); i != windows.end (); i++)
+ {
+ Rectf& windowRect = (*i)->m_Position;
+
+ bounds.SetLeft (std::min (bounds.x, windowRect.x));
+ bounds.SetTop (std::min (bounds.y, windowRect.y));
+ bounds.SetRight (std::max (bounds.GetXMax (), windowRect.GetXMax ()));
+ bounds.SetBottom (std::max (bounds.GetYMax (), windowRect.GetYMax ()));
+ }
+
+ if(winState->m_ModalWindow != NULL)
+ {
+ Rectf& windowRect = winState->m_ModalWindow->m_Position;
+ bounds.SetLeft (std::min (bounds.x, windowRect.x));
+ bounds.SetTop (std::min (bounds.y, windowRect.y));
+ bounds.SetRight (std::max (bounds.GetXMax (), windowRect.GetXMax ()));
+ bounds.SetBottom (std::max (bounds.GetYMax (), windowRect.GetYMax ()));
+ }
+
+ return bounds;
+ }
+
+ void BringWindowToFront (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return;
+
+ // Modal windows are on top by definition.
+ if(winState->m_ModalWindow != NULL && winState->m_ModalWindow->m_ID == windowID)
+ return;
+
+ GUIWindow* win = winState->GetWindow (windowID);
+ if (win)
+ {
+ int minDepth = 0;
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ {
+ if ((*i)->m_Depth < minDepth)
+ minDepth = (*i)->m_Depth;
+ }
+ win->m_Depth = minDepth - 1;
+ winState->m_LayersChanged = true;
+ }
+ }
+
+ void BringWindowToBack (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return;
+
+ // Modal windows are on top by definition.
+ if(winState->m_ModalWindow != NULL && winState->m_ModalWindow->m_ID == windowID)
+ return;
+
+ GUIWindow* win = winState->GetWindow (windowID);
+ if (win)
+ {
+ int maxDepth = 0;
+ for (GUIWindowState::WindowList::iterator i = winState->m_WindowList.begin(); i != winState->m_WindowList.end(); i++)
+ {
+ if ((*i)->m_Depth > maxDepth)
+ maxDepth = (*i)->m_Depth;
+ }
+ win->m_Depth = maxDepth + 1;
+ winState->m_LayersChanged = true;
+ }
+ }
+
+ void FocusWindow (GUIState &state, int windowID)
+ {
+ GUIWindowState* winState = state.m_MultiFrameGUIState.m_Windows;
+ if (!winState)
+ return;
+
+ // Modal windows must be focused if they exist.
+ if(winState->m_ModalWindow != NULL && winState->m_ModalWindow->m_ID != windowID)
+ return;
+
+ winState->m_FocusedWindow = windowID;
+ }
+}
+#endif
diff --git a/Runtime/IMGUI/GUIWindows.h b/Runtime/IMGUI/GUIWindows.h
new file mode 100644
index 0000000..1519d43
--- /dev/null
+++ b/Runtime/IMGUI/GUIWindows.h
@@ -0,0 +1,95 @@
+#ifndef GUIWINDOWS_H
+#define GUIWINDOWS_H
+
+#include "Runtime/Math/Rect.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+
+struct GUIContent;
+class GUIStyle;
+
+namespace IMGUI
+{
+ struct GUIWindow
+ {
+ int m_ID;
+ // The ID list used by this window
+ ObjectGUIState m_ObjectGUIState;
+ Rectf m_Position;
+ // Sorting depth
+ int m_Depth;
+ // What's the title?
+ GUIContent m_Title;
+ // Was this window referenced this frame? (used to clean up unused windows at end-of-frame)
+ bool m_Used;
+ // Was it moved with a DragWindow? If so, we need to use our internal rect instead of the one passed in to us
+ bool m_Moved;
+ bool m_ForceRect;
+
+ // Mono Object handles
+ int m_Delegate;
+ int m_Skin;
+ int m_Style;
+
+ // GUIState GUI.window time:
+ ColorRGBAf m_Color, m_BackgroundColor, m_ContentColor;
+ Matrix4x4f m_Matrix;
+ bool m_Enabled;
+
+ void LoadFromGUIState (GUIState &state);
+ void SetupGUIValues (GUIState &state);
+ void OnGUI (GUIState &state);
+ void ReleaseScriptingObjects ();
+
+ GUIWindow ();
+ ~GUIWindow ();
+ };
+
+ Rectf DoWindow (GUIState &state, int windowId, const Rectf &clientRect, ScriptingObjectPtr delegate, GUIContent& title, ScriptingObjectPtr style, ScriptingObjectPtr guiSkin, bool forceRectOnLayout, bool isModal = false);
+ void DragWindow (GUIState &state, const Rectf &rect);
+
+ void BeginWindows (GUIState &state, bool setupClipping, bool ignoreModalWindow = true);
+ void EndWindows (GUIState &state, bool ignoreModalWindow = true);
+ void RepaintModalWindow(GUIState &state);
+
+ void MoveWindowFromLayout (GUIState &state, int windowID, const Rectf &rect);
+ Rectf GetWindowRect (GUIState &state, int windowID);
+
+ Rectf GetWindowsBounds (GUIState &state);
+
+ void BringWindowToFront (GUIState &state, int windowID);
+ void BringWindowToBack (GUIState &state, int windowID);
+ void FocusWindow (GUIState &state, int windowID);
+
+ // Get the window that has focus, or NULL
+ GUIWindow *GetFocusedWindow (GUIState &state);
+
+ struct GUIWindowState
+ {
+ GUIWindowState ();
+ ~GUIWindowState ();
+ typedef std::vector<GUIWindow*> WindowList;
+ WindowList m_WindowList;
+ int m_FocusedWindow;
+ bool m_LayersChanged;
+
+ // The window we're currently calling OnGUI on, or NULL
+ GUIWindow* m_CurrentWindow;
+
+ // The current modal window being displayed, or NULL if there are no modal windows this frame
+ GUIWindow* m_ModalWindow;
+
+ GUIWindow* GetWindow (int windowId);
+ void SortWindows ();
+ GUIWindow* FindWindowUnderMouse (GUIState &state);
+
+ // Release all GC handles. We call this at the end of every frame in order to make sure we don't leak anything
+ // (they only need to get remembered WITHIN one layout/event cycle)
+ void ReleaseScriptingObjects ();
+ };
+}
+
+
+
+
+#endif
diff --git a/Runtime/IMGUI/IDList.cpp b/Runtime/IMGUI/IDList.cpp
new file mode 100644
index 0000000..896c498
--- /dev/null
+++ b/Runtime/IMGUI/IDList.cpp
@@ -0,0 +1,164 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/IDList.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/Misc/InputEvent.h"
+
+IDList::IDList ()
+{
+ m_Idx = 0;
+}
+
+int IDList::CalculateNextFromHintList (GUIState &state, int hint, bool isKeyboard)
+{
+ int retval = 0;
+ // Ok - we're searching: start at idx and search to end.
+ for (int searchIdx = m_Idx; searchIdx < m_IDs.size(); searchIdx++)
+ {
+ if (m_IDs[searchIdx].hint == hint)
+ {
+ m_Idx = searchIdx + 1;
+ retval = m_IDs[searchIdx].value;
+ break;
+ }
+ }
+
+ // We still couldn't find it, so we just add to end...
+ if (retval == 0)
+ {
+ retval = state.m_EternalGUIState->GetNextUniqueID();
+ m_IDs.push_back (ID (hint, retval, isKeyboard));
+ m_Idx = m_IDs.size();
+ }
+
+ return retval;
+}
+
+void IDList::BeginOnGUI ()
+{
+ m_Idx = 0;
+ m_FirstKeyControl = -1;
+ m_LastKeyControl = -1;
+ m_PreviousKeyControl = -1;
+ m_NextKeyControl = -1;
+ m_HasKeyboardControl = false;
+ m_TabControlSearchStatus = kLookingForPrevious;
+}
+
+int IDList::GetNext (GUIState &state, int hint, FocusType focusType, const Rectf &rect)
+{
+ int retval = GetNext (state, hint, focusType);
+ if (state.m_CurrentEvent->type != InputEvent::kLayout && state.m_CurrentEvent->type != InputEvent::kUsed && ShouldBeKeyboardControl(focusType))
+ {
+ Assert (m_Idx > 0);
+ m_IDs[m_Idx-1].rect = rect;
+ }
+ return retval;
+}
+
+int IDList::GetNext (GUIState &state, int hint, FocusType focusType)
+{
+ InputEvent::Type type = state.m_CurrentEvent->type;
+
+ bool isKeyboard = ShouldBeKeyboardControl(focusType);
+ int retval = 0;
+ if (type != InputEvent::kUsed)
+ retval = CalculateNextFromHintList(state, hint, isKeyboard);
+ else
+ {
+ return -1;
+ }
+
+ if (type != InputEvent::kLayout)
+ {
+ if (type == InputEvent::kKeyDown && state.m_OnGUIState.m_Enabled == (int)true)
+ {
+ if (isKeyboard)
+ {
+ switch (m_TabControlSearchStatus)
+ {
+ case kNotActive:
+ break;
+ case kLookingForPrevious:
+ if (m_FirstKeyControl == -1)
+ m_FirstKeyControl = retval;
+ if (retval != state.m_MultiFrameGUIState.m_KeyboardControl)
+ m_PreviousKeyControl = retval;
+ else
+ {
+ m_TabControlSearchStatus = kLookingForNext;
+ m_HasKeyboardControl = true;
+ }
+ break;
+ case kLookingForNext:
+ m_NextKeyControl = retval;
+ m_TabControlSearchStatus = kFound;
+ break;
+ default:
+ break;
+ }
+ m_LastKeyControl = retval;
+ }
+ }
+ }
+ return retval;
+}
+
+bool IDList::GetRectOfControl (int id, Rectf &out) const
+{
+ for (dynamic_array<ID>::const_iterator i = m_IDs.begin(); i != m_IDs.end(); i++)
+ {
+ if (i->value == id && i->rect.width != -1.0f)
+ {
+ out = i->rect;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IDList::CanHaveKeyboardFocus (int id) const
+{
+ for (dynamic_array<ID>::const_iterator i = m_IDs.begin(); i != m_IDs.end(); i++)
+ {
+ if (i->value == id)
+ {
+ return i->isKeyboard;
+ }
+ }
+ return false;
+}
+
+bool IDList::ShouldBeKeyboardControl (FocusType focus) {
+ switch (focus) {
+ case kPassive:
+ return false;
+ case kKeyboard:
+ return true;
+ case kNative:
+ return false;
+ // TODO: Move this back in during 2.x when we accept keyboard input on the various GUI controls.
+ /* PlatformSelection platform = GUI.skin.settings.keyboardFocus;
+ if (platform == PlatformSelection.Native) {
+ if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsWebPlayer || Application.platform == RuntimePlatform.WindowsEditor)
+ platform = PlatformSelection.Windows;
+ else
+ platform = PlatformSelection.Mac;
+ }
+ return platform == PlatformSelection.Windows;
+ */ }
+ return true;
+}
+
+void IDList::SetSearchIndex (int index)
+{
+ if (index >= 0 && index < m_IDs.size())
+ m_Idx = index;
+ else
+ AssertString (Format("Invalid index %d (size is %zd)", index, m_IDs.size()));
+}
+
+int IDList::GetSearchIndex () const
+{
+ return m_Idx;
+}
+
diff --git a/Runtime/IMGUI/IDList.h b/Runtime/IMGUI/IDList.h
new file mode 100644
index 0000000..40c1a3d
--- /dev/null
+++ b/Runtime/IMGUI/IDList.h
@@ -0,0 +1,69 @@
+#ifndef IDLIST_H
+#define IDLIST_H
+
+struct GUIState;
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+/// Used by GUIUtility.GetcontrolID to inform the UnityGUI system if a given control can get keyboard focus.
+/// MUST MATCH FocusType in GUIUtility.txt
+enum FocusType {
+ /// This control can get keyboard focus on Windows, but not on Mac. Used for buttons, checkboxes and other "pressable" things.
+ kNative = 0,
+ /// This is a proper keyboard control. It can have input focus on all platforms. Used for TextField and TextArea controls
+ kKeyboard = 1,
+ /// This control can never recieve keyboard focus.
+ kPassive = 2,
+};
+
+/// Manages the list of IDs that are returned from GUIUtility.GetControlID ();
+class IDList
+{
+public:
+ IDList ();
+ int GetNext (GUIState &state, int hint, FocusType focusType);
+ int GetNext (GUIState &state, int hint, FocusType focusType, const Rectf &rect);
+ void BeginOnGUI ();
+ bool HasKeyboardControl () { return m_HasKeyboardControl; }
+
+ // Get the ID of the keyboard control BEFORE the one that has keyboard focus (used to implement shift-tab) -1 if not found
+ int GetPreviousKeyboardControlID () { return m_PreviousKeyControl; }
+ // Get the ID of the keyboard control AFTER the one that has keyboard focus (used to implement tab) -1 if not found
+ int GetNextKeyboardControlID () { return m_NextKeyControl; }
+ // Get the ID of the first keyboard control - used when tabbing into this script -1 if not found
+ int GetFirstKeyboardControlID () { return m_FirstKeyControl; }
+ // Get the ID of the last keyboard control - used when tabbing out of this script -1 if not found
+ int GetLastKeyboardControlID () { return m_LastKeyControl; }
+
+ bool GetRectOfControl (int id, Rectf &out) const;
+ bool CanHaveKeyboardFocus (int id) const;
+ void SetSearchIndex (int index);
+ int GetSearchIndex () const;
+private:
+ // Enum and vars for handling searching for next and previous fields when tabbing through controls.
+ enum TabControlSearchStatus
+ {
+ kNotActive = 0, kLookingForPrevious = 1, kLookingForNext = 2, kFound = 3
+ };
+ TabControlSearchStatus m_TabControlSearchStatus;
+ int m_FirstKeyControl, m_LastKeyControl, m_PreviousKeyControl, m_NextKeyControl;
+ bool m_HasKeyboardControl;
+ /// Determine if a given focusType should result in keyboard control
+ static bool ShouldBeKeyboardControl (FocusType focus);
+
+ int CalculateNextFromHintList (GUIState &state, int hint, bool isKeyboard);
+ struct ID
+ {
+ int hint, value;
+ bool isKeyboard;
+ Rectf rect;
+ ID (int _hint, int _value, bool _isKeyboard) : hint (_hint), value (_value), isKeyboard (_isKeyboard), rect (-1.0f, -1.0f, -1.0f, -1.0f) {}
+ ID (int _hint, int _value, bool _isKeyboard, Rectf _rect) : hint (_hint), value (_value), isKeyboard (_isKeyboard), rect (_rect) {}
+ };
+
+ dynamic_array<ID> m_IDs;
+ int m_Idx;
+};
+
+
+#endif
diff --git a/Runtime/IMGUI/IMGUIUtils.cpp b/Runtime/IMGUI/IMGUIUtils.cpp
new file mode 100644
index 0000000..2ce0596
--- /dev/null
+++ b/Runtime/IMGUI/IMGUIUtils.cpp
@@ -0,0 +1,101 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/IMGUIUtils.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+
+const float s_TabWidth = 16;
+extern float s_GUIStyleIconSizeX;
+
+namespace IMGUI
+{
+ InputEvent::Type GetEventType (const GUIState &state, const InputEvent &event)
+ {
+ InputEvent::Type type = event.type;
+ if (!state.m_OnGUIState.m_Enabled)
+ {
+ if (type == InputEvent::kRepaint || type == InputEvent::kLayout || type == InputEvent::kUsed)
+ return type;
+ return InputEvent::kIgnore;
+ }
+
+ if (state.m_CanvasGUIState.m_GUIClipState.GetEnabled())
+ return type;
+
+ if (type == InputEvent::kMouseDown || type == InputEvent::kMouseUp || type == InputEvent::kDragPerform || type == InputEvent::kDragUpdated)
+ return InputEvent::kIgnore;
+
+ return type;
+ }
+
+
+ InputEvent::Type GetEventTypeForControl (const GUIState &state, const InputEvent &event, int controlID)
+ {
+ InputEvent::Type m_Type = event.type;
+
+ // if we have no hot control, just return the usual
+ if (GetHotControl(state) == 0)
+ return GetEventType (state, event);
+ switch (m_Type)
+ {
+ // Mouse events follow GUIUtility.hotControl
+ case InputEvent::kMouseDown:
+ case InputEvent::kMouseUp:
+ case InputEvent::kMouseMove:
+ case InputEvent::kMouseDrag:
+ if (!GetEnabled(state))
+ return InputEvent::kIgnore;
+ if (GetGUIClipEnabled(state) || HasMouseControl (state, controlID))
+ return m_Type;
+ return InputEvent::kIgnore;
+
+ // Key events follow keyboard control
+ case InputEvent::kKeyDown:
+ case InputEvent::kKeyUp:
+ case InputEvent::kScrollWheel: // Not sure about scrollwheel. For now we map it to behave like keyboard events.
+
+ if (!GetEnabled(state))
+ return InputEvent::kIgnore;
+ if (GetGUIClipEnabled(state) || HasMouseControl (state, controlID) || GetKeyboardControl(state) == controlID)
+ return m_Type;
+ return InputEvent::kIgnore;
+
+ // Repaint, Layout, Used, DragUpdated, DragPerform, Ignore
+ default:
+ return m_Type;
+ }
+ }
+
+
+ TextMeshGenerator2* GetGenerator (const Rectf &contentRect, const GUIContent &content, Font& font, TextAnchor alignment, bool wordWrap, bool richText, ColorRGBA32 color, int fontSize, int fontStyle, ImagePosition imagePosition)
+ {
+ if (!wordWrap)
+ return &TextMeshGenerator2::Get (content.m_Text, &font, alignment, kAuto, 0.0f, s_TabWidth, 1.0f, richText, true, color, fontSize, fontStyle);
+
+ Texture *image = content.m_Image;
+ float textWidth = contentRect.Width();
+ switch (imagePosition) {
+ case kImageLeft:
+ // Todo: Subtract width of the icon
+ if (image != NULL)
+ {
+ Vector2f imageSize = Vector2f (image->GetDataWidth(), image->GetDataHeight());
+
+ //float imageScale = clamp (min (contentRect.Width() / imageSize.x, contentRect.Height() / imageSize.y), 0.0f, 1.0f);
+ //contentRect.width -= Roundf (imageSize.x * imageScale);
+
+ if (s_GUIStyleIconSizeX == 0)
+ textWidth -= Roundf (imageSize.x * clamp (std::min (contentRect.Width() / imageSize.x, contentRect.Height() / imageSize.y), 0.0f, 1.0f));
+ else
+ textWidth -= s_GUIStyleIconSizeX;
+ }
+ break;
+ case kImageOnly:
+ return NULL;
+ case kImageAbove:
+ case kTextOnly:
+ // These two require no special handling
+ break;
+ }
+
+ return &TextMeshGenerator2::Get (content.m_Text, &font, alignment, kAuto, textWidth, s_TabWidth, 1.0f, richText, true, color, fontSize, fontStyle);
+ }
+} // namespace IMGUI
diff --git a/Runtime/IMGUI/IMGUIUtils.h b/Runtime/IMGUI/IMGUIUtils.h
new file mode 100644
index 0000000..486b2fe
--- /dev/null
+++ b/Runtime/IMGUI/IMGUIUtils.h
@@ -0,0 +1,64 @@
+#ifndef IMGUIUtils_H
+#define IMGUIUtils_H
+
+#include "Runtime/Math/Color.h"
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIContent.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+
+struct InputEvent;
+struct GUIState;
+class TextMeshGenerator2;
+namespace IMGUI
+{
+ inline ColorRGBAf GetColor (const GUIState &state) { return state.m_OnGUIState.m_Color; }
+ inline void SetColor (GUIState &state, const ColorRGBAf &color) { state.m_OnGUIState.m_Color = color; }
+
+ inline ColorRGBAf GetBackgroundColor (const GUIState &state) { return state.m_OnGUIState.m_BackgroundColor; }
+ inline void SetBackgroundColor (GUIState &state, const ColorRGBAf &color) { state.m_OnGUIState.m_BackgroundColor = color; }
+
+ inline ColorRGBAf GetContentColor (const GUIState &state) { return state.m_OnGUIState.m_ContentColor; }
+ inline void SetContentColor (GUIState &state, const ColorRGBAf &color) { state.m_OnGUIState.m_ContentColor = color; }
+
+ inline bool GetEnabled (const GUIState &state) { return state.m_OnGUIState.m_Enabled; }
+ inline void SetEnabled (GUIState &state, bool enab) { state.m_OnGUIState.m_Enabled = enab; }
+
+ inline bool GetChanged (const GUIState &state) { return state.m_OnGUIState.m_Changed; }
+ inline void SetChanged (GUIState &state, bool changed) { state.m_OnGUIState.m_Changed = changed; }
+
+ inline int GetDepth (const GUIState &state) { return state.m_OnGUIState.m_Depth; }
+ inline void SetDepth (GUIState &state, int depth) { state.m_OnGUIState.m_Depth = depth; }
+
+ inline int GetHotControl (const GUIState &state) { return state.m_EternalGUIState->m_HotControl; }
+ inline void SetHotControl (GUIState &state, int hotControl) { state.m_EternalGUIState->m_HotControl = hotControl; }
+
+ inline int GetKeyboardControl (const GUIState &state) { return state.m_MultiFrameGUIState.m_KeyboardControl; }
+ inline void SetKeyboardControl (GUIState &state, int keyControl) { state.m_MultiFrameGUIState.m_KeyboardControl = keyControl; }
+
+ inline InputEvent &GetCurrentEvent (GUIState &state) { return *state.m_CurrentEvent; }
+
+ inline bool GetGUIClipEnabled (const GUIState &state) { return state.m_CanvasGUIState.m_GUIClipState.GetEnabled();}
+
+ /// Get the type of the current event taking clipping and enabled flag into account from GUIState.
+ InputEvent::Type GetEventType (const GUIState &state, const InputEvent &event);
+
+ /// Get the type of the current event considering that a specific control is asking.
+ /// This is more agressive than the old C# one as it will also cull mouse events, assuming that hotControl is being used.
+ InputEvent::Type GetEventTypeForControl (const GUIState &state, const InputEvent &event, int controlID);
+
+ inline int GetControlID (GUIState &state, int hash, FocusType focusType)
+ { return state.GetControlID (hash, focusType); }
+
+ inline int GetControlID (GUIState &state, int hash, FocusType focusType, const Rectf& rect)
+ { return state.GetControlID (hash, focusType, rect); }
+
+
+ inline void GrabMouseControl (GUIState &state, int controlID) { state.m_EternalGUIState->m_HotControl = controlID; }
+ inline void ReleaseMouseControl (GUIState &state) { state.m_EternalGUIState->m_HotControl = 0; }
+ inline bool HasMouseControl (const GUIState &state, int controlID) { return state.m_EternalGUIState->m_HotControl == controlID; }
+
+ inline bool AreWeInOnGUI (const GUIState &state) { return state.m_OnGUIDepth > 0; }
+
+ TextMeshGenerator2* GetGenerator (const Rectf &contentRect, const GUIContent &content, Font& font, TextAnchor alignment, bool wordWrap, bool richText, ColorRGBA32 color, int fontSize, int fontStyle, ImagePosition imagePosition);
+}
+#endif
diff --git a/Runtime/IMGUI/NamedKeyControlList.cpp b/Runtime/IMGUI/NamedKeyControlList.cpp
new file mode 100644
index 0000000..6574a13
--- /dev/null
+++ b/Runtime/IMGUI/NamedKeyControlList.cpp
@@ -0,0 +1,27 @@
+#include "UnityPrefix.h"
+#include "Runtime/IMGUI/NamedKeyControlList.h"
+namespace IMGUI
+{
+
+void NamedKeyControlList::AddNamedControl (const std::string &name, int id, int windowID)
+{
+ m_NamedControls[name] = NamedControl (id, windowID);
+}
+
+std::string NamedKeyControlList::GetNameOfControl (int id)
+{
+ for (std::map<std::string, NamedControl>::const_iterator i = m_NamedControls.begin(); i != m_NamedControls.end(); i++)
+ if (i->second.ID == id)
+ return i->first;
+ return std::string ("");
+}
+
+NamedControl* NamedKeyControlList::GetControlNamed (const std::string &name)
+{
+ std::map<std::string, NamedControl>::iterator i = m_NamedControls.find (name);
+ if (i != m_NamedControls.end ())
+ return &i->second;
+ return NULL;
+}
+
+} // namespace \ No newline at end of file
diff --git a/Runtime/IMGUI/NamedKeyControlList.h b/Runtime/IMGUI/NamedKeyControlList.h
new file mode 100644
index 0000000..dc4dff7
--- /dev/null
+++ b/Runtime/IMGUI/NamedKeyControlList.h
@@ -0,0 +1,40 @@
+#ifndef NAMEDKEYCONTROLLIST_H
+#define NAMEDKEYCONTROLLIST_H
+
+namespace IMGUI
+{
+ struct NamedControl
+ {
+ int ID;
+ int windowID;
+ NamedControl ()
+ {
+ ID = 0;
+ windowID = -1;
+ }
+ NamedControl (int _ID, int _windowID)
+ {
+ ID = _ID;
+ windowID = _windowID;
+ }
+ };
+
+ class NamedKeyControlList
+ {
+ public:
+ void AddNamedControl (const std::string &str, int id, int windowID);
+
+ // Return ptr name of a given control, NULL if none.
+ std::string GetNameOfControl (int id);
+
+ // Return the ID of a named control.
+ // Used in GUIState::FocusControl
+ NamedControl* GetControlNamed (const std::string &name);
+
+ void Clear () { m_NamedControls.clear (); }
+ private:
+ std::map<std::string, NamedControl> m_NamedControls;
+ };
+};
+
+#endif \ No newline at end of file
diff --git a/Runtime/IMGUI/TextFormatting.cpp b/Runtime/IMGUI/TextFormatting.cpp
new file mode 100644
index 0000000..7495326
--- /dev/null
+++ b/Runtime/IMGUI/TextFormatting.cpp
@@ -0,0 +1,337 @@
+#include "UnityPrefix.h"
+#include "TextFormatting.h"
+
+enum FormattingTag {
+ kTagBold,
+ kTagItalic,
+ kTagColor,
+ kTagSize,
+ kTagMaterial,
+ kTagImage,
+ kTagX,
+ kTagY,
+ kTagWidth,
+ kTagHeight,
+ kNumTags,
+};
+
+const char* kFormattingTagStrings[] =
+{
+ "b",
+ "i",
+ "color",
+ "size",
+ "material",
+ "quad",
+ "x",
+ "y",
+ "width",
+ "height",
+};
+
+
+const char* kFormattingHTMLColorStrings[] =
+{
+ "red",
+ "cyan",
+ "blue",
+ "darkblue",
+ "lightblue",
+ "purple",
+ "yellow",
+ "lime",
+ "fuchsia",
+ "white",
+ "silver",
+ "grey",
+ "black",
+ "orange",
+ "brown",
+ "maroon",
+ "green",
+ "olive",
+ "navy",
+ "teal",
+ "aqua",
+ "magenta",
+};
+
+#define kNumHTMLColors (sizeof(kFormattingHTMLColorStrings)/sizeof(kFormattingHTMLColorStrings[0]))
+
+const UInt8 kFormattingHTMLColorValues[kNumHTMLColors*4] =
+{
+ 0xff,0x00,0x00,0xff,
+ 0x00,0xff,0xff,0xff,
+ 0x00,0x00,0xff,0xff,
+ 0x00,0x00,0xa0,0xff,
+ 0xad,0xd8,0xe6,0xff,
+ 0x80,0x00,0x80,0xff,
+ 0xff,0xff,0x00,0xff,
+ 0x00,0xff,0x00,0xff,
+ 0xff,0x00,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xc0,0xc0,0xc0,0xff,
+ 0x80,0x80,0x80,0xff,
+ 0x00,0x00,0x00,0xff,
+ 0xff,0xa5,0x00,0xff,
+ 0xa5,0x2a,0x2a,0xff,
+ 0x80,0x00,0x00,0xff,
+ 0x00,0x80,0x00,0xff,
+ 0x80,0x80,0x00,0xff,
+ 0x00,0x00,0x80,0xff,
+ 0x00,0x80,0x80,0xff,
+ 0x00,0xff,0xff,0xff,
+ 0xff,0x00,0xff,0xff,
+};
+
+FormattingTag GetTag(UTF16String& input, int &pos, bool &closing)
+{
+ if (input[pos] == '<')
+ {
+ int outpos = pos + 1;
+ if (outpos == input.length)
+ return (FormattingTag)-1;
+
+ closing = input[outpos] == '/';
+ if (closing)
+ outpos++;
+
+ for (int i=0;i<kNumTags;i++)
+ {
+ bool match = true;
+ for (int j=0;kFormattingTagStrings[i][j] != '\0'; j++)
+ {
+ if (outpos+j == input.length || ToLower((char)input[outpos+j]) != kFormattingTagStrings[i][j])
+ {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ {
+ int paramPos = outpos + strlen(kFormattingTagStrings[i]);
+ if ((!closing && input[paramPos] == '=') || (input[paramPos] == ' ' && i == kTagImage))
+ {
+ while (input[paramPos] != '>' && paramPos < input.length)
+ paramPos++;
+ }
+ else if (input[paramPos] != '>')
+ continue;
+ outpos += strlen(kFormattingTagStrings[i]);
+ pos = outpos;
+ return (FormattingTag)i;
+ }
+ }
+ }
+ return (FormattingTag)-1;
+}
+
+FormattingTag GetImageTag(UTF16String& input, int &pos)
+{
+ for (int i=0;i<kNumTags;i++)
+ {
+ bool match = true;
+ for (int j=0;kFormattingTagStrings[i][j] != '\0'; j++)
+ {
+ if (pos+j == input.length || ToLower((char)input[pos+j]) != kFormattingTagStrings[i][j])
+ {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ {
+ int paramPos = pos + strlen(kFormattingTagStrings[i]);
+ if (input[paramPos] == '=')
+ {
+ pos = paramPos;
+ return (FormattingTag)i;
+ }
+ else
+ continue;
+ }
+ }
+ return (FormattingTag)-1;
+}
+
+std::string GetParameter(UTF16String& input, int &pos, bool allowMultipleParamaters = false)
+{
+ std::string parameter;
+ if (input[pos] == '=')
+ {
+ pos++;
+ while (input[pos] != '>' && (input[pos] != ' ' || !allowMultipleParamaters) && pos < input.length)
+ parameter.push_back(input[pos++]);
+ }
+ if (parameter.size() > 2 && parameter[0] == parameter[parameter.size()-1])
+ {
+ if (parameter[0] == '\'' || parameter[0] == '"')
+ parameter = parameter.substr(1,parameter.size()-2);
+ }
+ return parameter;
+}
+
+ColorRGBA32 ParseHTMLColor(std::string colorstring)
+{
+ ColorRGBA32 color = ColorRGBA32(0xffffffff);
+ if (colorstring[0] == '#')
+ {
+ if (colorstring.size() == 4 || colorstring.size() == 5)
+ {
+ std::string longcolorstring = "#";
+ for (int i=1;i<colorstring.size();i++)
+ {
+ longcolorstring += colorstring[i];
+ longcolorstring += colorstring[i];
+ }
+ colorstring = longcolorstring;
+ }
+ if (colorstring.size() == 7 || colorstring.size() == 9)
+ HexStringToBytes (&colorstring[1], colorstring.size()/2, &color);
+ }
+ else for (int i=0; i<kNumHTMLColors; i++)
+ {
+ if (StrICmp(colorstring, kFormattingHTMLColorStrings[i]) == 0)
+ return ColorRGBA32 (*(UInt32*)(kFormattingHTMLColorValues+i*4));
+ }
+ return color;
+}
+
+bool ValidateFormat (std::vector<TextFormatChange> &format)
+{
+ std::vector<int> stack;
+ for (std::vector<TextFormatChange>::iterator i = format.begin(); i != format.end(); i++)
+ {
+ int flags = i->flags;
+ if (flags & kFormatPop)
+ {
+ if (stack.empty())
+ // closing tag without opening tag.
+ return false;
+ flags &= ~kFormatPop;
+ if (stack.back() != flags)
+ // closing does not match opening tag.
+ return false;
+ stack.pop_back();
+ }
+ else
+ stack.push_back(flags);
+ }
+ if (!stack.empty())
+ // unclosed tags.
+ return false;
+ return true;
+}
+
+void ParseImageParameters (UTF16String& input, int &pos, TextFormatChange &change)
+{
+ while (pos < input.length && input[pos] != '>')
+ {
+ FormattingTag tag = GetImageTag(input, pos);
+ if (tag != -1)
+ {
+ switch (tag)
+ {
+ case kTagMaterial:
+ change.flags |= kFormatMaterial;
+ change.format.material = StringToInt(GetParameter(input, pos, true));
+ break;
+ case kTagSize:
+ change.flags |= kFormatSize;
+ change.format.size = StringToInt(GetParameter(input, pos, true));
+ break;
+ case kTagColor:
+ change.flags |= kFormatColor;
+ change.format.color = ParseHTMLColor(GetParameter(input, pos, true));
+ break;
+ case kTagX:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.x);
+ break;
+ case kTagY:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.y);
+ break;
+ case kTagWidth:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.width);
+ break;
+ case kTagHeight:
+ sscanf(GetParameter(input, pos, true).c_str(), "%f", &change.format.imageRect.height);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ pos++;
+ }
+}
+
+void GetFormatString (UTF16String& input, std::vector<TextFormatChange> &format)
+{
+ int pos = 0;
+ while (pos < input.length)
+ {
+ int oldpos = pos;
+ bool closing;
+ FormattingTag tag = GetTag(input, pos, closing);
+ if (tag != -1)
+ {
+ TextFormatChange change;
+ change.flags = kFormatPop;
+ switch (tag)
+ {
+ case kTagBold:
+ change.flags = kFormatBold;
+ break;
+ case kTagItalic:
+ change.flags = kFormatItalic;
+ break;
+ case kTagSize:
+ change.flags = kFormatSize;
+ break;
+ case kTagColor:
+ change.flags = kFormatColor;
+ break;
+ case kTagMaterial:
+ change.flags = kFormatMaterial;
+ break;
+ case kTagImage:
+ change.flags = kFormatImage;
+ break;
+ default:
+ break;
+ }
+ if (closing)
+ change.flags |= kFormatPop;
+ else switch (tag)
+ {
+ case kTagSize:
+ change.format.size = StringToInt(GetParameter(input, pos));
+ break;
+ case kTagColor:
+ change.format.color = ParseHTMLColor(GetParameter(input, pos));
+ break;
+ case kTagMaterial:
+ change.format.material = StringToInt(GetParameter(input, pos));
+ break;
+ case kTagImage:
+ ParseImageParameters(input, pos, change);
+ break;
+ default:
+ break;
+ }
+ change.skipCharacters = pos + 1 - oldpos;
+ change.startPosition = oldpos;
+ format.push_back(change);
+ if (tag == kTagImage)
+ {
+ change.flags |= kFormatPop;
+ change.skipCharacters = 0;
+ format.push_back(change);
+ }
+ }
+ pos ++;
+ }
+ if (!ValidateFormat(format))
+ // Fail silently here rather then spamming the console with errors while typing unfinished markup.
+ format.clear();
+} \ No newline at end of file
diff --git a/Runtime/IMGUI/TextFormatting.h b/Runtime/IMGUI/TextFormatting.h
new file mode 100644
index 0000000..3646692
--- /dev/null
+++ b/Runtime/IMGUI/TextFormatting.h
@@ -0,0 +1,85 @@
+#ifndef TEXTFORMATTING_H
+#define TEXTFORMATTING_H
+
+#include "TextUtil.h"
+#include "Runtime/Math/Rect.h"
+
+enum {
+ kStyleDefault = 0,
+ kStyleFlagBold = 1 << 0,
+ kStyleFlagItalic = 1 << 1,
+};
+
+struct TextFormat
+{
+ int style;
+ ColorRGBA32 color;
+ int size;
+ int material;
+ Rectf imageRect;
+
+ TextFormat () :
+ style(0),
+ color(0xff,0xff,0xff,0xff),
+ size(0),
+ material(0),
+ imageRect(0,0,1,1)
+ {
+ }
+};
+
+enum FormatFlags {
+ kFormatBold = 1 << 0,
+ kFormatItalic = 1 << 1,
+ kFormatColor = 1 << 2,
+ kFormatSize = 1 << 3,
+ kFormatMaterial = 1 << 4,
+ kFormatImage = 1 << 5,
+ kFormatPop = 1 << 15,
+};
+
+struct TextFormatChange
+{
+ int startPosition;
+ int skipCharacters;
+ TextFormat format;
+ int flags;
+};
+
+class FormatStack : std::vector<TextFormat>
+{
+public:
+ FormatStack (ColorRGBA32 _color, int _size, int _style)
+ {
+ push_back (TextFormat());
+ back().color = _color;
+ back().size = _size;
+ back().style = _style;
+ }
+
+ void PushFormat (TextFormatChange &change)
+ {
+ if (change.flags & kFormatPop)
+ pop_back();
+ else
+ {
+ push_back(back());
+ if (change.flags & kFormatBold)
+ back().style |= kStyleFlagBold;
+ if (change.flags & kFormatItalic)
+ back().style |= kStyleFlagItalic;
+ if (change.flags & kFormatColor)
+ back().color = change.format.color;
+ if (change.flags & kFormatSize)
+ back().size = change.format.size;
+ if (change.flags & kFormatMaterial)
+ back().material = change.format.material;
+ }
+ }
+
+ TextFormat& Current() { return back(); }
+};
+
+void GetFormatString (UTF16String& input, std::vector<TextFormatChange> &format);
+
+#endif \ No newline at end of file
diff --git a/Runtime/IMGUI/TextMeshGenerator2.cpp b/Runtime/IMGUI/TextMeshGenerator2.cpp
new file mode 100644
index 0000000..c5fafb0
--- /dev/null
+++ b/Runtime/IMGUI/TextMeshGenerator2.cpp
@@ -0,0 +1,871 @@
+#include "UnityPrefix.h"
+#include "TextMeshGenerator2.h"
+#include "TextFormatting.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/DrawUtil.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include <limits>
+
+#define TEXTDEBUG 0
+
+#define kMaxMaterials 8
+
+using std::pair;
+typedef std::vector<TextMeshGenerator2*/*, main_thread_allocator<TextMeshGenerator2*> */> Generators;
+static Generators s_Generators;
+static Font *gDefaultFont = NULL;
+const int kKillFrames = 5;
+
+static const TextAlignment kTextAnchorToAlignment[] = {
+ kLeft, kCenter, kRight, kLeft, kCenter, kRight, kLeft, kCenter, kRight
+};
+
+// The global getter function
+TextMeshGenerator2 &TextMeshGenerator2::Get (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelCorrect, ColorRGBA32 color, int fontSize, int fontStyle) {
+ if (font == NULL) {
+ if (!gDefaultFont)
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ font = gDefaultFont;
+ }
+
+ // maybe not the best way around, but we want to report error if changing size/style for non-dynamic fonts
+ // but in the same time don't want to spam console because we'll continue to work just fine
+ bool reportNonDynamicFontError = false;
+
+ bool fontDynamic = font->GetConvertCase() == Font::kDynamicFont;
+ if( !fontDynamic )
+ {
+ if( fontSize != 0 || fontStyle != kStyleDefault )
+ reportNonDynamicFontError = true;
+
+ fontSize = 0;
+ fontStyle = kStyleDefault;
+ }
+
+ if (alignment == kAuto)
+ alignment = kTextAnchorToAlignment[anchor];
+ // TODO: Use some sort of hash to quickly find candidate meshes
+ // in practice a lot of the parameters will be the same, with only the string really differing
+ for (Generators::const_iterator i = s_Generators.begin(); i != s_Generators.end(); i++) {
+ TextMeshGenerator2 *gen = *i;
+ if ( gen->m_Font.GetInstanceID() == font->GetInstanceID() &&
+ (anchor == kDontCare || (gen->m_Anchor == anchor && gen->m_Alignment == alignment)) &&
+ gen->m_WordWrapWidth == wordWrapWidth &&
+ gen->m_TabSize == tabSize &&
+ gen->m_LineSpacing == lineSpacing &&
+ gen->m_UTF16Text == text &&
+ gen->m_FontSize == fontSize &&
+ gen->m_FontStyle == fontStyle &&
+ gen->m_RichText == richText &&
+ gen->m_PixelCorrect == pixelCorrect &&
+ gen->m_Color == color
+ ) {
+ // we mark it as dirty the moment we look at it.
+ // Doing it during rendering only makes us regenerate strings only for wordwrap & scrollview size negotiations.
+ gen->m_LastUsedFrame = GetTimeManager().GetRenderFrameCount();
+ return *gen;
+ }
+ }
+
+ // report error only now - it will fire up the single time we create text mesh
+ if(reportNonDynamicFontError)
+ {
+ // TODO: font name?
+ WarningString("Font size and style overrides are only supported for dynamic fonts.");
+ }
+
+ // We don't have one already so we need to generate it.
+ // Sanitize anchor if we don't care. We just pick one for the generation.
+ // If it gets rendered with another, this mesh will get garbage collected later and the real one used instead.
+ if (anchor == kDontCare)
+ anchor = kUpperLeft;
+
+ TextMeshGenerator2 *gen = new TextMeshGenerator2 (text, font, anchor, alignment, wordWrapWidth, tabSize, lineSpacing, richText, pixelCorrect, color, fontSize, fontStyle);
+ gen->Generate ();
+ s_Generators.push_back (gen);
+ return *gen;
+}
+
+float TextMeshGenerator2::Roundf (float x)
+{
+ if (m_PixelCorrect)
+ return ::Roundf(x);
+ else
+ return x;
+}
+/// Get the cursor position
+Vector2f TextMeshGenerator2::GetCursorPosition (const Rectf &screenRect, int textPosition) {
+ // clamp the text position to valid ranges
+ if (textPosition < 0)
+ textPosition = 0;
+ else if (textPosition > m_UTF16Text.length)
+ textPosition = m_UTF16Text.length;
+
+ // make sure we don't access out of bounds position when the string is too large to be processed completely.
+ if(textPosition * 4 + 4 > std::numeric_limits<UInt16>::max())
+ textPosition = (std::numeric_limits<UInt16>::max() / 4) - 1;
+
+ return m_CursorPos[textPosition] + GetTextOffset (screenRect);
+}
+
+
+// Compare 2 vertex positions.
+// this function correctly handles rounding so you can click on the first half of a letter and be taken to the front...
+inline int ComparePositions2 (Vector2f *arr, int maxCount, int idx1, const Vector2f &p2, float lineHeight) {
+ // Find out if we're line(s) above or below
+ Vector2f p = arr[idx1];
+ if (p.y <= p2.y - lineHeight)
+ return -1;
+ if (p.y > p2.y)
+ return 1;
+ // ok, we're on the same line here, so compare x positions...
+
+ // We need to get the middle of letters
+ Vector2f curr = arr[idx1];
+ Vector2f nextPoint = arr[idx1 != maxCount ? idx1+ 1 : maxCount];
+ // If the next letter is on the line after, we cheat by moving the point way to the right, so hit detection will work...
+ float next;
+ if (nextPoint.y == curr.y)
+ next = nextPoint.x;
+ else
+ next = 10000;
+
+
+ float rightBorder = (next + curr.x) * .5f;
+ if (rightBorder < p2.x)
+ return -1;
+
+ Vector2f prevPoint = arr[idx1 != 0 ? idx1 - 1 : 0];
+ float prev;
+ // If the previous letter is on the line before, we cheat by moving the point way to the left, so hit detection will work...
+ if (prevPoint.y == curr.y)
+ prev = prevPoint.x;
+ else
+ prev = -10000;
+ float leftBorder = (prev + curr.x) * .5f;
+ if (leftBorder > p2.x)
+ return 1;
+ return 0;
+}
+
+int TextMeshGenerator2::GetCursorIndexAtPosition (const Rectf &screenRect, const Vector2f &pos) {
+ int start = 0;
+ int maxCount = m_CursorPos.size () - 1;
+ int end = maxCount;
+ Vector2f localPos = pos - GetTextOffset (screenRect);
+ Vector2f *arr = &m_CursorPos[0];
+
+ Font *font = GetFont();
+ float lineSize = Roundf (font->GetLineSpacing(m_FontSize));
+
+ // Binary search to find out where we clicked
+ while (start <= end) {
+ int mid = ((start + end) >> 1);
+ switch (ComparePositions2 (arr, maxCount, mid, localPos, lineSize)) {
+ case 0:
+ return mid;
+ case -1:
+ start = mid + 1;
+ break;
+ case 1:
+ end = mid - 1;
+ break;
+ }
+ }
+ // We didn't find anything, this is the closest match
+ if (end < 0)
+ end = 0; // Sanity.
+ return end;
+}
+
+PROFILER_INFORMATION(gTextMeshGenerator, "TextRendering.Cleanup", kProfilerRender)
+
+ // clear the cache for anything that wasn't rendered in the last frame.
+ void TextMeshGenerator2::GarbageCollect ()
+{
+ int currentFrame = GetTimeManager().GetRenderFrameCount();
+
+#if TEXTDEBUG
+ printf_console ("TextMesh GarbageCollect\n");
+#endif
+
+ // We're removing from the vector, so we need to iterate backwards through it
+ for (int i = s_Generators.size(); i--;)
+ {
+ PROFILER_AUTO(gTextMeshGenerator, NULL)
+ TextMeshGenerator2 *gen = s_Generators[i];
+ if (currentFrame - gen->m_LastUsedFrame > kKillFrames)
+ {
+#if TEXTDEBUG
+ printf_console ("\tTextMesh killed - %d chars\n", gen->m_CursorPos.size() - 1);
+#endif
+ delete gen;
+ s_Generators.erase(s_Generators.begin() + i);
+ }
+ }
+}
+
+void TextMeshGenerator2::Flush ()
+{
+ for (int i = s_Generators.size(); i--;)
+ {
+ TextMeshGenerator2 *gen = s_Generators[i];
+ delete gen;
+ }
+ s_Generators.clear ();
+}
+
+
+TextMeshGenerator2::TextMeshGenerator2 (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelCorrect, ColorRGBA32 color, int fontSize, int fontStyle) :
+m_UTF16Text (text)
+{
+ m_Font = font;
+ m_FontSize = fontSize;
+ m_FontStyle = fontStyle;
+
+ m_Anchor = anchor;
+ m_Alignment = alignment;
+ m_WordWrapWidth = wordWrapWidth;
+ m_TabSize = tabSize;
+ m_LastUsedFrame = 0;
+ m_LineSpacing = lineSpacing;
+ m_Mesh = NULL;
+ m_RichText = richText;
+ m_PixelCorrect = pixelCorrect;
+ m_Color = color;
+
+#if TEXTDEBUG
+ printf_console ("Creating textMesh\n");
+#endif
+}
+
+
+TextMeshGenerator2::~TextMeshGenerator2 ()
+{
+ if (m_Mesh)
+ DestroySingleObject (&*m_Mesh);
+}
+
+Vector2f TextMeshGenerator2::GetTextOffset (const Rectf &rect)
+{
+ Vector2f offset;
+ switch (m_Anchor) {
+ case kUpperLeft:
+ offset.x = Roundf (rect.x);
+ offset.y = Roundf (rect.y);
+ break;
+ case kUpperCenter:
+ offset.x = Roundf (rect.x + rect.width *.5f);
+ offset.y = Roundf (rect.y);
+ break;
+ case kUpperRight:
+ offset.x = Roundf (rect.GetRight());
+ offset.y = Roundf (rect.y);
+ break;
+ case kMiddleLeft:
+ offset.x = Roundf (rect.x);
+ offset.y = Roundf ((rect.GetBottom() + rect.y - m_Rect.Height ()) * .5f);
+ break;
+ case kMiddleCenter:
+ offset.x = Roundf (rect.x + rect.width *.5f);
+ offset.y = Roundf ((rect.GetBottom() + rect.y - m_Rect.Height ()) * .5f);
+ break;
+ case kMiddleRight:
+ offset.x = Roundf (rect.GetRight());
+ offset.y = Roundf ((rect.GetBottom() + rect.y - m_Rect.Height ()) * .5f);
+ break;
+ case kLowerLeft:
+ offset.x = Roundf (rect.x);
+ offset.y = Roundf (rect.GetBottom() - m_Rect.Height ());
+ break;
+ case kLowerCenter:
+ offset.x = Roundf ((rect.x + rect.GetRight()) *.5f);
+ offset.y = Roundf (rect.GetBottom() - m_Rect.Height ());
+ break;
+ case kLowerRight:
+ offset.x = Roundf (rect.GetRight());
+ offset.y = Roundf (rect.GetBottom() - m_Rect.Height ());
+ break;
+ default:
+ offset.x = offset.y = 0.0F;
+ break;
+ }
+ return offset;
+}
+
+// Draw the text mesh
+// TODO: clip
+void TextMeshGenerator2::Render (const Rectf &rect, const ChannelAssigns& channels)
+{
+ GfxDevice &device = GetGfxDevice();
+
+ float matWorld[16], matView[16];
+
+ CopyMatrix(device.GetViewMatrix(), matView);
+ CopyMatrix(device.GetWorldMatrix(), matWorld);
+
+ Matrix4x4f textMatrix;
+
+ Vector2f offset = GetTextOffset (rect);
+ textMatrix.SetTranslate (Vector3f (offset.x, offset.y, 0.0f));
+
+ device.SetViewMatrix (textMatrix.GetPtr());
+
+ DrawUtil::DrawMeshRaw (channels, *m_Mesh, 0);
+
+ device.SetViewMatrix(matView);
+ device.SetWorldMatrix(matWorld);
+
+ // YES, we have actually used this TextMesh
+ m_LastUsedFrame = GetTimeManager().GetRenderFrameCount();
+}
+
+void TextMeshGenerator2::RenderRaw (const ChannelAssigns& channels)
+{
+ DrawUtil::DrawMeshRaw (channels, *m_Mesh, 0);
+ m_LastUsedFrame = GetTimeManager().GetRenderFrameCount();
+}
+
+// Will offset count*4 vertices horizontally - used for doing center & rightaligned
+void OffsetCharacters (Vector2f offset, TextMeshGenerator2::Vertex *firstIndex, Vector2f *cursor, int count) {
+ int vertexCount = count * 4;
+ while (vertexCount--) {
+ firstIndex->vert.x += offset.x;
+ firstIndex->vert.y += offset.y;
+ firstIndex++;
+ }
+ while (count--) {
+ cursor->x += offset.x;
+ cursor->y += offset.y;
+ cursor++;
+ }
+}
+
+void TextMeshGenerator2::FixLineOffset (float lineWidth, TextMeshGenerator2::Vertex *firstChar, Vector2f *firstCursor, int count) {
+ // If we're centre or right-aligned, handle that.
+ // Right now charOffset.x contains the width of the line.
+ switch (m_Alignment) {
+ // if we're right-aligned, move everything backl
+ case kRight:
+ OffsetCharacters (Vector2f (-lineWidth, 0), firstChar, firstCursor, count);
+ break;
+ case kCenter:
+ OffsetCharacters (Vector2f (Roundf (-lineWidth * .5f), 0), firstChar, firstCursor, count);
+ break;
+ default:
+ break;
+ }
+}
+
+class TextMeshGenerationData
+{
+ TextMeshGenerator2 &tmgen;
+ std::vector<TextFormatChange> format;
+ TextMeshGenerator2::Vertex *vertex, *vertexBegin;
+ dynamic_array<UInt16> materialTriangles[kMaxMaterials];
+ Font *font;
+ int formatChange;
+ int characterPos;
+ int lastChar;
+ int startOfWord, startOfLine;
+ int stringlen;
+ int materialCount;
+ float lineSize;
+ float lineLength, wordLength;
+ float endOfLastWord, startOfWordPos;
+ float spacesLength;
+ float maxLineLength;
+ bool wordWrap;
+ Vector3f charOffset;
+ float startY;
+ FormatStack formatStack;
+
+ inline float Roundf (float x)
+ {
+ return tmgen.Roundf(x);
+ }
+
+public:
+ int GetStringLength () const { return stringlen; }
+ dynamic_array<UInt16> *GetTriangles () { return materialTriangles; }
+
+ TextMeshGenerationData(TextMeshGenerator2 &_tmgen)
+ : tmgen (_tmgen),
+ formatStack(tmgen.m_Color, tmgen.m_FontSize, tmgen.m_FontStyle),
+ formatChange (0),
+ characterPos (0),
+ startOfWord (0), // Character index at start of current word (used for wordwrap)
+ startOfLine (0), // character index at start of current line (used for right/center-aligning text).
+ lineLength (0), // Length of current line without trailing spaces (in pixels)
+ wordLength (0), // Length of current word (in pixels)
+ endOfLastWord (0), // position of the last completed word's right bounds
+ startOfWordPos (0), // Pixel position of the current word start.
+ spacesLength (0), // Size of spaces (used to not make trailing spaces affect alignment)
+ maxLineLength (0), // The max line length - used for calculating the rect at the end.
+ lineSize (0),
+ startY (0),
+ lastChar (-1)
+ {
+ font = tmgen.GetFont();
+ wordWrap = tmgen.m_WordWrapWidth != 0.0f;
+ }
+
+ void ParseFormatAndCacheFont ()
+ {
+ if (tmgen.m_RichText && IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ GetFormatString (tmgen.m_UTF16Text, format);
+ int maxFontSize = tmgen.m_FontSize ? tmgen.m_FontSize : font->GetFontSize();
+
+ materialCount = 1;
+ for (std::vector<TextFormatChange>::iterator i = format.begin(); i != format.end(); i++)
+ {
+ if (i->flags & kFormatSize)
+ {
+ float size = i->format.size ? i->format.size : font->GetFontSize();
+ if (size > maxFontSize)
+ maxFontSize = (int)size;
+ }
+ if (i->flags & (kFormatMaterial | kFormatImage))
+ {
+ if (i->format.material >= kMaxMaterials || i->format.material < 0)
+ {
+ WarningStringMsg ("Only %d materials are allowed per TextMesh.", kMaxMaterials);
+ i->format.material = 0;
+ }
+ if (i->format.material+1 > materialCount)
+ materialCount = i->format.material+1;
+ }
+ }
+
+ AssertIf (font == NULL);
+ font->CacheFontForText (tmgen.m_UTF16Text.text, tmgen.m_UTF16Text.length, tmgen.m_FontSize, tmgen.m_FontStyle, format);
+
+ if (font->GetFontSize() != 0 && IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) || font->GetConvertCase() == Font::kDynamicFont)
+ startY = Roundf(font->GetAscent() * ((float)maxFontSize/font->GetFontSize() - 1.0f));
+
+ lineSize = Roundf (font->GetLineSpacing(maxFontSize) * tmgen.m_LineSpacing);
+
+ charOffset = Vector3f (0, startY, 0);
+ }
+
+ void SetupBuffers ()
+ {
+ stringlen = tmgen.m_UTF16Text.length;
+
+ if(stringlen * 4 + 4 > std::numeric_limits<UInt16>::max())
+ {
+ stringlen = (std::numeric_limits<UInt16>::max() / 4) - 1;
+ Assert("String too long for TextMeshGenerator. Cutting off characters.");
+ }
+
+ Mesh *mesh = tmgen.m_Mesh;
+ if (!mesh)
+ {
+ tmgen.m_Mesh = NEW_OBJECT_MAIN_THREAD (Mesh);
+ mesh = tmgen.m_Mesh;
+ mesh->Reset();
+ mesh->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ mesh->SetHideFlags (Object::kHideAndDontSave);
+ mesh->SetHideFromRuntimeStats(true);
+#if UNITY_EDITOR
+ mesh->SetName("TextMesh");
+#endif
+ }
+ else
+ mesh->Clear (true);
+
+ size_t vCount = stringlen * 4 + 4;
+ mesh->ResizeVertices (vCount, TextMeshGenerator2::Vertex::kFormat);
+ mesh->SetVertexColorsSwizzled (gGraphicsCaps.needsToSwizzleVertexColors);
+
+ vertexBegin = (TextMeshGenerator2::Vertex*)mesh->GetVertexDataPointer ();
+ vertex = vertexBegin;
+
+ // Set up the array & pointer for cursor positions.
+ // This is 1 longer than the string, as asking for a cursor position AFTER the last character is a valid request
+ tmgen.m_CursorPos.resize (stringlen + 1);
+ }
+
+ void InsertSpace ()
+ {
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+ float w = font->GetCharacterWidth (' ', formatStack.Current().size, formatStack.Current().style);
+ // If this is the first space following a word, we record the last word position for correct wrapping
+ if (spacesLength == 0)
+ endOfLastWord = charOffset.x;
+
+ spacesLength += w;
+
+ // all the vars that makes this a word delimiter
+ startOfWord = characterPos + 1;
+ wordLength = 0;
+ startOfWordPos = charOffset.x + w;
+ // we never wordwrap on a space (no matter how many spaces you have, if it doesn't fit the cursor just stops at the rightmost position)
+
+ charOffset.x += w;
+ }
+
+ void InsertLineBreak ()
+ {
+ // Move the line down
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+
+ // if we're right or center aligned, move all character in this line to the right place.
+ tmgen.FixLineOffset (lineLength, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], characterPos - startOfLine + 1);
+
+ charOffset.x = 0;
+ charOffset.y += lineSize;
+ maxLineLength = std::max(maxLineLength, lineLength);
+ lineLength = 0;
+ spacesLength = 0;
+ startOfLine = startOfWord = characterPos + 1;
+ }
+
+ void InsertTab ()
+ {
+ if (spacesLength == 0)
+ endOfLastWord = charOffset.x;
+ spacesLength = 0;
+
+ int tab = FloorfToInt (charOffset.x / tmgen.m_TabSize) + 1;
+ if (wordWrap && tab * tmgen.m_TabSize > tmgen.m_WordWrapWidth) {
+ // if we're right or center aligned, move all character in this line to the right place.
+ tmgen.FixLineOffset (lineLength, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], characterPos - startOfLine);
+ charOffset.y += lineSize;
+ charOffset.x = lineLength = tmgen.m_TabSize;
+ startOfLine = startOfWord = characterPos + 1;
+ maxLineLength = std::max(maxLineLength, lineLength);
+ } else {
+ // float newPos = tab * m_TabSize;
+
+ if (wordWrap && tab * tmgen.m_TabSize > tmgen.m_WordWrapWidth) {
+ // if we're right or center aligned, move all character in this line to the right place.
+ tmgen.FixLineOffset (lineLength, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], characterPos - startOfLine);
+ charOffset.y -= lineSize;
+ charOffset.x = lineLength = tmgen.m_TabSize;
+ startOfLine = startOfWord = characterPos + 1;
+ maxLineLength = std::max(maxLineLength, lineLength);
+ } else {
+ float newPos = tab * tmgen.m_TabSize;
+
+ lineLength = charOffset.x = newPos;
+ }
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+
+ // all the vars that makes this a word delimiter
+ startOfWord = characterPos + 1;
+ wordLength = 0;
+ startOfWordPos = charOffset.x;
+ }
+ }
+
+ void InsertCharacter (int c)
+ {
+ Rectf vert, charUv; // rendering info
+ bool flipped;
+ float w; // character width
+
+ font->GetCharacterRenderInfo( c, formatStack.Current().size, formatStack.Current().style, vert, charUv, flipped );
+ w = Roundf (font->GetCharacterWidth (c, formatStack.Current().size, formatStack.Current().style));
+
+ // TODO: can these be non-floats? If they can't shouldn't we be doing this at load rather than when getting each character?
+ // Possibly during serialization as well to conserve size (assume 100 characters * 4 floats = 1600 bytes/font)
+ vert.y = Roundf (vert.y);
+ vert.x = Roundf (vert.x);
+ vert.width = Roundf (vert.width);
+ vert.height = Roundf (vert.height);
+
+ // Add kerning information if present.
+ if( !font->GetKerningValues().empty() && lastChar != -1 )
+ {
+ pair<UnicodeChar, UnicodeChar> kerningPair = pair<UnicodeChar, UnicodeChar>(lastChar, c);
+ Font::KerningValues::iterator foundKerning = font->GetKerningValues().find(kerningPair);
+
+ if (foundKerning != font->GetKerningValues().end())
+ {
+ float kerning = foundKerning->second;
+ if (tmgen.m_FontSize != 0)
+ kerning = Roundf(kerning * tmgen.m_FontSize/(float)font->GetFontSize());
+ charOffset.x += kerning;
+ }
+ }
+
+ // Add quad vertices and UVs
+ vertex[0].vert = charOffset + Vector3f(vert.x, -vert.y, 0);
+ vertex[flipped?2:0].uv = Vector2f (charUv.x, charUv.GetBottom());
+ vertex[1].vert = charOffset + Vector3f(vert.GetRight(), -vert.y, 0);
+ vertex[1].uv = Vector2f (charUv.GetRight(), charUv.GetBottom());
+ vertex[2].vert = charOffset + Vector3f(vert.GetRight(), -vert.GetBottom(), 0);
+ vertex[flipped?0:2].uv = Vector2f (charUv.GetRight(), charUv.y);
+ vertex[3].vert = charOffset + Vector3f(vert.x, -vert.GetBottom(), 0);
+ vertex[3].uv = Vector2f (charUv.x, charUv.y);
+ ColorRGBA32 deviceColor = GfxDevice::ConvertToDeviceVertexColor(formatStack.Current().color);
+ vertex[0].color = vertex[1].color = vertex[2].color = vertex[3].color = deviceColor;
+ vertex += 4;
+
+ // Add two triangles to indices
+ int vertexIndex = characterPos * 4; // since we always emit vertices we know the triangle index from the character index
+ int material = formatStack.Current().material;
+ materialTriangles[material].push_back(vertexIndex + 1);
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex);
+
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex + 3);
+ materialTriangles[material].push_back(vertexIndex);
+
+ // word-wrapping: If we're too large with this letter, we wrap the word to the next line by moving the verts
+ if (wordWrap && charOffset.x + w > tmgen.m_WordWrapWidth /* && wordLength != 0.0f*/) {
+
+ // Special case: if the word we're wrapping is longer than one line alone, we split the word at the offending
+ // character.
+ if (startOfWord == startOfLine) {
+ startOfWord = characterPos;
+ wordLength = 0;
+ startOfWordPos = charOffset.x;
+ endOfLastWord = charOffset.x;
+ }
+
+ // Horizontally align everything up to the last word (the part before the inserted newline)
+ tmgen.FixLineOffset (endOfLastWord, &vertexBegin[startOfLine * 4], &tmgen.m_CursorPos[startOfLine], startOfWord - startOfLine);
+
+ // Move all letters after the inserted newline one line down and to 0 in Xoffset.
+ OffsetCharacters (Vector2f (-startOfWordPos, +lineSize), &vertexBegin[startOfWord * 4], &tmgen.m_CursorPos[startOfWord], characterPos - startOfWord + 1);
+ maxLineLength = std::max(maxLineLength, lineLength);
+
+ // Move the cursor
+ charOffset.x -= startOfWordPos;
+ // The length of the current line is the length of the word we just wrapped down
+ lineLength = wordLength;
+
+ charOffset.y += lineSize;
+
+ // The new line starts at this word
+ startOfLine = startOfWord;
+ startOfWordPos = 0;
+
+ // If we had any accumulated spaces, they don't apply now (as we just word-wrapped)
+ spacesLength = 0;
+ }
+ wordLength += w;
+ // advance the cursor
+ charOffset.x += w;
+ lineLength += w + spacesLength; // Add this letter + any accumulated spaces to the line length
+ spacesLength = 0; // We have now used the accumulated spaces
+ lastChar = c;
+ }
+
+ void ProcessFormatForPosition ()
+ {
+ while (formatChange < format.size() && characterPos >= format[formatChange].startPosition)
+ {
+ characterPos += format[formatChange].skipCharacters;
+
+ // Make sure we don't overflow if the string is capped.
+ if (characterPos > stringlen)
+ {
+ characterPos = stringlen;
+ return;
+ }
+
+ for (int j=0; j<format[formatChange].skipCharacters; j++)
+ {
+ vertex[0].vert = vertex[1].vert = vertex[2].vert = vertex[3].vert = charOffset;
+ vertex += 4;
+ }
+
+ formatStack.PushFormat(format[formatChange]);
+ if ((format[formatChange].flags & (kFormatImage | kFormatPop)) == kFormatImage)
+ {
+ vertex -= 4;
+ float size = formatStack.Current().size;
+ if (font->GetFontSize() != 0 && IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) || font->GetConvertCase() == Font::kDynamicFont)
+ {
+ if (size == 0)
+ size = font->GetFontSize();
+ size = size / font->GetFontSize() * font->GetAscent();
+ }
+ else
+ size = font->GetAscent();
+ Vector3f imageBase = charOffset + Vector3f(0, font->GetAscent(), 0);
+ Rectf& r = format[formatChange].format.imageRect;
+ float aspect;
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ aspect = r.width / r.height;
+ else
+ aspect = 1.0f;
+ vertex[0].vert = imageBase + Vector3f(0, -size, 0);
+ vertex[0].uv = Vector2f (r.x, r.GetYMax());
+ vertex[1].vert = imageBase + Vector3f(size * aspect, -size, 0);
+ vertex[1].uv = Vector2f (r.GetXMax(), r.GetYMax());
+ vertex[2].vert = imageBase + Vector3f(size * aspect, 0, 0);
+ vertex[2].uv = Vector2f (r.GetXMax(), r.y);
+ vertex[3].vert = imageBase + Vector3f(0, 0, 0);
+ vertex[3].uv = Vector2f (r.x, r.y);
+ ColorRGBA32 deviceColor = GfxDevice::ConvertToDeviceVertexColor(formatStack.Current().color);
+ vertex[0].color = vertex[1].color = vertex[2].color = vertex[3].color = deviceColor;
+ charOffset += Vector3f(Roundf(size * aspect), 0, 0);
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ lineLength = charOffset.x;
+
+ vertex += 4;
+
+ int vertexIndex = (characterPos-1) * 4; // since we always emit vertices we know the triangle index from the character index
+ int material = formatStack.Current().material;
+ materialTriangles[material].push_back(vertexIndex + 1);
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex);
+
+ materialTriangles[material].push_back(vertexIndex + 2);
+ materialTriangles[material].push_back(vertexIndex + 3);
+ materialTriangles[material].push_back(vertexIndex);
+ }
+ formatChange++;
+ }
+ }
+
+ void ProcessString ()
+ {
+ for (characterPos=0; characterPos <= stringlen; characterPos++)
+ {
+ ProcessFormatForPosition ();
+
+ // all the code becomes a lot simpler if we pretend we have a newline at the end.
+ // If not, the last line becomes a special-case requiring lots of code duplication.
+ int c;
+ if (characterPos < stringlen)
+ c = tmgen.m_UTF16Text[characterPos];
+ else
+ c = '\n';
+
+#if TEXTDEBUG
+ printf_console ("%c", c);
+#endif
+
+ // store the character offset into the cursor position array
+ tmgen.m_CursorPos[characterPos] = Vector2f (charOffset.x, charOffset.y - startY);
+
+ switch (c) {
+ case '\n':
+ InsertLineBreak();
+ break;
+ case ' ':
+ InsertSpace();
+ break;
+ case '\t':
+ InsertTab();
+ break;
+ default:
+ InsertCharacter(c);
+ break;
+ }
+ }
+#if TEXTDEBUG
+ printf_console ("\n");
+#endif
+ }
+
+ Rectf GetBounds ()
+ {
+ // Get the bounding volume.
+ // Y coordinates are 0 - charOffset.y (which has been moved down one line due to the fake \n we inserted.
+ Rectf r;
+ r.y = 0;
+ r.SetBottom(charOffset.y-startY);
+ switch (tmgen.m_Alignment) {
+ case kLeft:
+ r.x = 0;
+ r.width = Roundf (maxLineLength);
+ break;
+ case kRight:
+ r.x = -Roundf (maxLineLength);
+ r.width = Abs(r.x);
+ break;
+ case kCenter:
+ r.x = -Roundf (maxLineLength * .5f);
+ r.width = Roundf (maxLineLength);
+ break;
+ default:
+ break;
+ }
+ return r;
+ }
+
+ void SetMeshData ()
+ {
+ Mesh* mesh = tmgen.m_Mesh;
+ mesh->SetSubMeshCount(materialCount);
+ for (int i=0; i<materialCount; i++)
+ {
+ if (materialTriangles[i].size())
+ mesh->SetIndicesComplex(&materialTriangles[i][0], materialTriangles[i].size(), i, kPrimitiveTriangles, Mesh::k16BitIndices);
+ }
+
+ AABB bounds;
+ bounds.SetCenterAndExtent(Vector3f(tmgen.m_Rect.x, tmgen.m_Rect.y, 0.0f), Vector3f::zero);
+ bounds.Encapsulate (Vector3f(tmgen.m_Rect.GetXMax(), tmgen.m_Rect.GetYMax(), 0.0f));
+ mesh->SetLocalAABB (bounds);
+
+ mesh->SetChannelsDirty (mesh->GetAvailableChannels (), false );
+ }
+};
+
+void TextMeshGenerator2::Generate ()
+{
+ TextMeshGenerationData data (*this);
+ data.ParseFormatAndCacheFont ();
+ data.SetupBuffers ();
+
+ // Allocate temp memory for the first material (which contains all triangles by default),
+ // to avoid unnecessary allocations.
+ UInt16 *triangles = NULL;
+ int bufferSize = data.GetStringLength() * 6;
+ ALLOC_TEMP(triangles, UInt16, bufferSize);
+ data.GetTriangles()[0].assign_external (triangles, triangles + bufferSize);
+ data.GetTriangles()[0].resize_uninitialized(0);
+
+ data.ProcessString ();
+
+ m_Rect = data.GetBounds ();
+ data.SetMeshData ();
+}
+
+Font *TextMeshGenerator2::GetFont () const {
+ Font *f = m_Font;
+ if (!f) {
+ if (!gDefaultFont)
+ gDefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ return gDefaultFont;
+ }
+ else {
+ return f;
+ }
+}
+
+#if UNITY_EDITOR
+void TextMeshGenerator2::CleanCache (const UTF16String &text)
+{
+ for (Generators::iterator i = s_Generators.begin(); i != s_Generators.end();)
+ {
+ TextMeshGenerator2 *gen = *i;
+ if (gen->m_UTF16Text == text)
+ {
+ delete gen;
+ i = s_Generators.erase(i);
+ }
+ else
+ i++;
+ }
+}
+#endif
diff --git a/Runtime/IMGUI/TextMeshGenerator2.h b/Runtime/IMGUI/TextMeshGenerator2.h
new file mode 100644
index 0000000..71b5cc6
--- /dev/null
+++ b/Runtime/IMGUI/TextMeshGenerator2.h
@@ -0,0 +1,96 @@
+#ifndef TEXTMESH
+#define TEXTMESH
+
+#include "TextUtil.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Math/Rect.h"
+
+class ChannelAssigns;
+
+#if UNITY_LINUX
+class Font;
+#endif
+
+//#include "DLAllocator.h"
+
+// Pixel-correct textmesh generator
+class TextMeshGenerator2 {
+ public:
+// DECLARE_FORWARD_MAINTHREAD_ALLOCATOR_MEMBER_NEW_DELETE
+
+ struct Vertex {
+ enum { kFormat = VERTEX_FORMAT3(Vertex, Color, TexCoord0) };
+ Vector3f vert;
+ ColorRGBA32 color;
+ Vector2f uv;
+ };
+
+ // The global getter function
+ static TextMeshGenerator2 &Get (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelcorrect, ColorRGBA32 color, int fontSize = 0, int fontStyle = 0);
+
+ /// Global update function - this is called once per frame & cleans up any meshes not used since then.
+ static void GarbageCollect ();
+
+ /// Delete all text meshes
+ static void Flush ();
+
+ static void CleanCache (const UTF16String &text);
+ // Individual textmesh objects here on down:
+
+ /// Render this text thing inside Rect.
+ // See this code for the correct stup instructions for rendering text with RenderRaw
+ void Render (const Rectf &rect, const ChannelAssigns& channels);
+
+ /// Just call the rendering on the GPU - don't set up a matrix to position the text or any such crap. Just submit the mesh.
+ void RenderRaw (const ChannelAssigns& channels);
+
+ /// Get the modelview offset to position this object's text within rect.
+ Vector2f GetTextOffset (const Rectf &rect);
+ // Get the font to use - this gets the default font if none set
+ Font *GetFont () const;
+ // Get the g
+ Mesh *GetMesh () const { return m_Mesh; }
+
+ /// Get the cursor position
+ Vector2f GetCursorPosition (const Rectf &screenRect, int cursor);
+
+ int GetCursorIndexAtPosition (const Rectf &screenRect, const Vector2f &pos);
+
+ /// Get the size.
+ Vector2f GetSize () const { return Vector2f (m_Rect.width, m_Rect.height); }
+
+ private:
+
+ TextMeshGenerator2 (const UTF16String &text, Font *font, TextAnchor anchor, TextAlignment alignment, float wordWrapWidth, float tabSize, float lineSpacing, bool richText, bool pixelcorrect, ColorRGBA32 color, int fontSize, int fontStyle);
+ ~TextMeshGenerator2 ();
+ void FixLineOffset (float lineWidth, Vertex *firstChar, Vector2f *firstCursor, int count);
+
+ // Generate the mesh stuff from the stored values
+ void Generate ();
+
+ float Roundf (float x);
+
+ // Width to word-wrap against. If 0, no word-wrapping is applied.
+ float m_WordWrapWidth;
+
+ TextAnchor m_Anchor; //< The text anchor.
+ TextAlignment m_Alignment;
+ float m_LineSpacing;
+ float m_TabSize; //< The pixel tab size
+ bool m_TabUsed; //< Is the tab used in the string?
+ bool m_RichText;
+ PPtr<Font> m_Font; //< font to use
+ int m_FontSize;
+ int m_FontStyle;
+ ColorRGBA32 m_Color;
+ bool m_PixelCorrect;
+ Rectf m_Rect;
+ UTF16String m_UTF16Text;
+
+ Mesh* m_Mesh; //< The mesh that contains the generated characters
+ std::vector<Vector2f/*, main_thread_allocator<Vector2f>*/ > m_CursorPos; //< Cursor positions for each character
+ int m_LastUsedFrame; //< Has this textmeshgenerator been used this frame
+
+ friend class TextMeshGenerationData;
+};
+#endif
diff --git a/Runtime/IMGUI/TextUtil.cpp b/Runtime/IMGUI/TextUtil.cpp
new file mode 100644
index 0000000..4d8289a
--- /dev/null
+++ b/Runtime/IMGUI/TextUtil.cpp
@@ -0,0 +1,277 @@
+#include "UnityPrefix.h"
+#include "TextUtil.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/VBO.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+using namespace std;
+
+class Font;
+
+namespace TextUtil_Static
+{
+static Font *s_DefaultFont = NULL;
+} // namespace TextUtil_Static
+static PPtr <Font> s_CurrentFont = NULL;
+static PPtr<Material> s_CurrentMaterial = NULL;
+
+void SetTextFont (Font *font) { s_CurrentFont = font; }
+void SetDrawTextMaterial (Material *m) { s_CurrentMaterial = m; }
+Material *GetDrawTextMaterial () { return s_CurrentMaterial; }
+
+extern "C" UInt16* AllocateUTF16Memory(int bytes)
+{
+ return (UInt16*)UNITY_MALLOC(kMemUTF16String,bytes);
+}
+
+#if ENABLE_SCRIPTING
+
+#if ENABLE_MONO
+UTF16String::UTF16String (MonoString *sourceString) {
+ if (sourceString != SCRIPTING_NULL && mono_string_length (sourceString) != 0)
+ {
+ text = mono_string_chars (sourceString);
+ length = mono_string_length (sourceString);
+ owns = false;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#elif UNITY_WINRT
+UTF16String::UTF16String (ScriptingStringPtr sourceString)
+{
+ if (sourceString != SCRIPTING_NULL)
+ {
+ Platform::String^ utf16String = safe_cast<Platform::String^>(sourceString);
+ length = utf16String->Length();
+ int size = length * sizeof(UInt16);
+ text = (UInt16*)UNITY_MALLOC(kMemUTF16String, size);
+ memcpy(text, utf16String->Data(), size);
+ owns = true;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#else
+UTF16String::UTF16String (ScriptingStringPtr sourceString)
+{
+ if (sourceString != SCRIPTING_NULL)
+ {
+ std::string str = scripting_cpp_string_for(sourceString);
+ text = AllocateUTF16Memory(str.size()*sizeof(UInt16));
+ ConvertUTF8toUTF16 (str.c_str(), str.size(), text, length);
+ owns = true;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#endif
+
+#endif
+
+extern "C" void UTF16String_TakeOverPreAllocatedUTF16Bytes(UTF16String* utfString, UInt16* bytes, int length)
+{
+ utfString->TakeOverPreAllocatedUTF16Bytes(bytes,length);
+}
+
+void UTF16String::TakeOverPreAllocatedUTF16Bytes(UInt16* bytes, int size)
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+ text = bytes;
+ length = size;
+ owns = true;
+}
+
+void UTF16String::InitFromCharPointer (const char* str)
+{
+ int slen = strlen (str);
+ if (slen != 0) {
+ text = AllocateUTF16Memory(slen*sizeof(UInt16));
+ ConvertUTF8toUTF16 (str, slen, text, length);
+ owns = true;
+ }
+ else {
+ text = NULL;
+ length = 0;
+ owns = false;
+ }
+}
+
+UTF16String::UTF16String (const char* str) {
+ InitFromCharPointer(str);
+}
+
+UTF16String::~UTF16String () {
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+}
+
+UTF16String::UTF16String (const UTF16String &other) {
+ if (other.length != 0)
+ {
+ length = other.length;
+ text =(UInt16*)UNITY_MALLOC(kMemUTF16String,length*sizeof(UInt16));
+ memcpy(text,other.text,sizeof (UInt16) * length);
+ owns = true;
+ }
+ else
+ {
+ length = 0;
+ text = NULL;
+ owns = false;
+ }
+}
+
+
+void UTF16String::CopyString (const UTF16String &other)
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+ if (other.length != 0)
+ {
+ length = other.length;
+ text =(UInt16*)UNITY_MALLOC(kMemUTF16String,length*sizeof(UInt16));
+ memcpy(text,other.text,sizeof (UInt16) * length);
+ owns = true;
+ }
+ else
+ {
+ length = 0;
+ text = NULL;
+ owns = false;
+ }
+}
+
+#if ENABLE_SCRIPTING
+
+#if ENABLE_MONO
+void UTF16String::BorrowString( ScriptingString *sourceString )
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+
+ if (sourceString != NULL && mono_string_length (sourceString) != 0)
+ {
+ text = mono_string_chars (sourceString);
+ length = mono_string_length (sourceString);
+ owns = false;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#elif UNITY_WINRT
+void UTF16String::BorrowString(ScriptingStringPtr sourceString)
+{
+ if (owns)
+ UNITY_FREE(kMemUTF16String,text);
+
+ if (sourceString != SCRIPTING_NULL)
+ {
+ Platform::String^ utf16String = safe_cast<Platform::String^>(sourceString);
+ length = utf16String->Length();
+ int size = length * sizeof(UInt16);
+ text = (UInt16*)UNITY_MALLOC(kMemUTF16String, size);
+ memcpy(text, utf16String->Data(), size);
+ owns = true;
+ }
+ else
+ {
+ owns = false;
+ text = NULL;
+ length = 0;
+ }
+}
+#endif
+
+void UTF8ToUTF16String (const char* src, UTF16String& dst)
+{
+ UTF16String tmp(src);
+ dst.TakeOverPreAllocatedUTF16Bytes(tmp.text, tmp.length);
+ tmp.owns = false;
+}
+
+ScriptingStringPtr UTF16String::GetScriptingString ()
+{
+ if (!length || !text)
+ return SCRIPTING_NULL;
+
+#if ENABLE_MONO
+ UInt16* buffer = new UInt16[length+1];
+ memcpy(buffer, text, length * 2);
+ buffer[length] = 0;
+ MonoString* retval = MonoStringNewUTF16 ((const wchar_t*) buffer);
+ delete[] buffer;
+ return retval;
+#elif UNITY_FLASH || UNITY_WINRT
+ std::vector<char> tempStr;
+ tempStr.resize(length * 4);
+ int outLength;
+ ConvertUTF16toUTF8 (text, length, &tempStr[0], outLength);
+ tempStr[outLength]=0;
+ return scripting_string_new((char const*)&tempStr[0]);
+#endif
+}
+
+#endif
+
+Font*
+GetTextFont ()
+{
+ using namespace TextUtil_Static;
+ Font* font = s_CurrentFont;
+ if (font == NULL) {
+ // Make sure we have default resource
+ if (s_DefaultFont == NULL) {
+ s_DefaultFont = GetBuiltinResource<Font> (kDefaultFontName);
+ if (!s_DefaultFont || !s_DefaultFont->GetMaterial()) {
+ LogString ("Couldn't load default font or font material!");
+ }
+ }
+ font = s_DefaultFont;
+ }
+ return font;
+}
+
+
+#include "TextMeshGenerator2.h"
+static TextMeshGenerator2 &GetMeshGen (const UTF16String &text, Font *font, TextAnchor align, float width) {
+ const float tabSize = 16;
+ return TextMeshGenerator2::Get (text, font, align, kAuto, width, tabSize, 1.0f, false, true, ColorRGBA32(0xffffffff));
+}
+float CalcTextHeight (const UTF16String &text, float width) {
+ TextMeshGenerator2 &gen = GetMeshGen (text, (Font*)GetTextFont(), kDontCare, width);
+ return gen.GetSize().y;
+}
+Vector2f CalcTextSize (const UTF16String &text) {
+ TextMeshGenerator2 &gen = GetMeshGen (text, (Font*)GetTextFont(), kDontCare, 0);
+ return gen.GetSize ();
+}
+Vector2f GetCursorPosition (const Rectf &screenRect, const UTF16String &text, TextAnchor align, bool wordWrap, int textPosition) {
+ float width = wordWrap ? screenRect.Width() : 0;
+ TextMeshGenerator2 &gen = GetMeshGen (text, (Font*)GetTextFont(), align, width);
+ return gen.GetCursorPosition(screenRect, textPosition);
+}
diff --git a/Runtime/IMGUI/TextUtil.h b/Runtime/IMGUI/TextUtil.h
new file mode 100644
index 0000000..2ddc8be
--- /dev/null
+++ b/Runtime/IMGUI/TextUtil.h
@@ -0,0 +1,110 @@
+#ifndef TEXTUTIL_H
+#define TEXTUTIL_H
+
+namespace Unity { class Material; }
+class Transform;
+class Camera;
+class Vector3f;
+class Matrix4x4f;
+class Object;
+class Texture;
+class Shader;
+class Vector2f;
+class Font;
+
+//#include <string>
+#include <set>
+#include <vector>
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Rect.h"
+
+using namespace Unity;
+
+enum TextAlignment {
+ kLeft,
+ kCenter,
+ kRight,
+ kAuto,
+};
+
+enum TextAnchor {
+ kUpperLeft,
+ kUpperCenter,
+ kUpperRight,
+ kMiddleLeft,
+ kMiddleCenter,
+ kMiddleRight,
+ kLowerLeft,
+ kLowerCenter,
+ kLowerRight,
+ kDontCare ///< Special case for getting text mesh generators: The anchoring used for the text doesn't modify the size of the generated text, so if you just want to query for it you don't care about anchoring
+};
+
+enum TextClipping {
+ /// Text flows freely outside the element.
+ kOverflow = 0,
+ /// Text gets clipped to be inside the element.
+ kClip = 1,
+};
+
+struct MonoString;
+
+// Opaque UTF16 string representation
+struct UTF16String
+{
+#if ENABLE_SCRIPTING
+ explicit UTF16String (ScriptingStringPtr sourceSting);
+#endif
+ UTF16String () {text = NULL; length = 0; owns = false;}
+ explicit UTF16String (const char* str);
+ UTF16String (const UTF16String &other);
+ ~UTF16String ();
+
+ UInt16 operator [] (int index) const { return text[index]; }
+ friend bool operator == (const UTF16String& lhs, const UTF16String& rhs)
+ {
+ using namespace std;
+ if (lhs.length != rhs.length)
+ return false;
+ return (rhs.text == NULL || memcmp(lhs.text, rhs.text,lhs.length * sizeof (UInt16)) == 0);
+ }
+
+ UInt16* text;
+ int length;
+ bool owns;
+
+ ScriptingStringPtr GetScriptingString ();
+ void CopyString (const UTF16String &other);
+#if ENABLE_MONO || UNITY_WINRT
+ void BorrowString (ScriptingStringPtr sourceString);
+#endif
+ void TakeOverPreAllocatedUTF16Bytes(UInt16* bytes, int size);
+private:
+ void InitFromCharPointer(const char* str);
+};
+
+void UTF8ToUTF16String (const char* src, UTF16String& dst);
+
+Vector2f GetCursorPosition (const Rectf &screenRect, const UTF16String &text, TextAnchor align, bool wordWrap, int textPosition);
+
+// Set a custom font to use.
+// If null, it will use the default resource font
+void SetTextFont (Font *font);
+Font *GetTextFont ();
+
+// Calculate size of a piece of text
+Vector2f CalcTextSize (const UTF16String &text);
+
+/// Calculate height of text word-wrapped to width
+float CalcTextHeight (const UTF16String &, float width);
+
+/// Set a custom shader to use for drawing text.
+/// If null, the material set in the font will be used
+void SetDrawTextMaterial (Material *m);
+Material *GetDrawTextMaterial ();
+
+
+
+
+#endif