/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "LyShine_precompiled.h" #include "UiTooltipDisplayComponent.h" #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC MEMBER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayComponent::UiTooltipDisplayComponent() : m_triggerMode(TriggerMode::OnHover) , m_autoPosition(true) , m_autoPositionMode(AutoPositionMode::OffsetFromMouse) , m_offset(0.0f, -10.0f) , m_autoSize(true) , m_delayTime(0.5f) , m_displayTime(5.0f) , m_state(State::Hidden) , m_stateStartTime(-1.0f) , m_curDelayTime(-1.0f) , m_timeSinceLastShown(-1.0f) , m_maxWrapTextWidth(-1.0f) , m_showSequence(nullptr) , m_hideSequence(nullptr) { } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayComponent::~UiTooltipDisplayComponent() { } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayInterface::TriggerMode UiTooltipDisplayComponent::GetTriggerMode() { return m_triggerMode; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetTriggerMode(TriggerMode triggerMode) { m_triggerMode = triggerMode; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool UiTooltipDisplayComponent::GetAutoPosition() { return m_autoPosition; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetAutoPosition(bool autoPosition) { m_autoPosition = autoPosition; } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayInterface::AutoPositionMode UiTooltipDisplayComponent::GetAutoPositionMode() { return m_autoPositionMode; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetAutoPositionMode(AutoPositionMode autoPositionMode) { m_autoPositionMode = autoPositionMode; } //////////////////////////////////////////////////////////////////////////////////////////////////// const AZ::Vector2& UiTooltipDisplayComponent::GetOffset() { return m_offset; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetOffset(const AZ::Vector2& offset) { m_offset = offset; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool UiTooltipDisplayComponent::GetAutoSize() { return m_autoSize; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetAutoSize(bool autoSize) { m_autoSize = autoSize; } //////////////////////////////////////////////////////////////////////////////////////////////////// AZ::EntityId UiTooltipDisplayComponent::GetTextEntity() { return m_textEntity; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetTextEntity(AZ::EntityId textEntity) { m_textEntity = textEntity; } //////////////////////////////////////////////////////////////////////////////////////////////////// float UiTooltipDisplayComponent::GetDelayTime() { return m_delayTime; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetDelayTime(float delayTime) { m_delayTime = delayTime; } //////////////////////////////////////////////////////////////////////////////////////////////////// float UiTooltipDisplayComponent::GetDisplayTime() { return m_displayTime; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetDisplayTime(float displayTime) { m_displayTime = displayTime; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::PrepareToShow(AZ::EntityId tooltipElement) { m_tooltipElement = tooltipElement; // We should be in the hidden state AZ_Assert(((m_state == State::Hiding) || (m_state == State::Hidden)), "State is not hidden when attempting to show tooltip"); if (m_state != State::Hidden) { EndTransitionState(); SetState(State::Hidden); } m_curDelayTime = m_delayTime; SetState(State::DelayBeforeShow); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::Hide() { switch (m_state) { case State::Showing: { // Since sequences can't have keys that represent current values, // only play the hide animation if the show animation has completed. m_timeSinceLastShown = gEnv->pTimer->GetCurrTime(ITimer::ETIMER_UI); EndTransitionState(); SetState(State::Hidden); } break; case State::Shown: { m_timeSinceLastShown = gEnv->pTimer->GetCurrTime(ITimer::ETIMER_UI); // Check if there is a hide animation to play IUiAnimationSystem* animSystem = nullptr; PrepareSequenceForPlay(m_hideSequenceName, m_hideSequence, animSystem); if (m_hideSequence) { SetState(State::Hiding); animSystem->PlaySequence(m_hideSequence, nullptr, false, false); } else { SetState(State::Hidden); } } break; case State::DelayBeforeShow: { SetState(State::Hidden); } break; default: break; } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::Update() { if (m_state == State::DelayBeforeShow) { // Check if it's time to show the tooltip if ((gEnv->pTimer->GetCurrTime(ITimer::ETIMER_UI) - m_stateStartTime) >= m_curDelayTime) { // Make sure nothing has changed with the hover interactable if (m_tooltipElement.IsValid() && UiTooltipDataPopulatorBus::FindFirstHandler(m_tooltipElement)) { Show(); } else { Hide(); } } } else if (m_state == State::Shown) { // Check if it's time to hide the tooltip if (m_displayTime >= 0.0f) { if ((gEnv->pTimer->GetCurrTime(ITimer::ETIMER_UI) - m_stateStartTime) >= m_displayTime) { // Hide tooltip Hide(); } } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::InGamePostActivate() { SetState(State::Hidden); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::OnUiAnimationEvent(EUiAnimationEvent uiAnimationEvent, IUiAnimSequence* animSequence) { if (m_state == State::Showing && animSequence == m_showSequence) { if ((uiAnimationEvent == eUiAnimationEvent_Stopped) || (uiAnimationEvent == eUiAnimationEvent_Aborted)) { SetState(State::Shown); } } else if (m_state == State::Hiding && animSequence == m_hideSequence) { if ((uiAnimationEvent == eUiAnimationEvent_Stopped) || (uiAnimationEvent == eUiAnimationEvent_Aborted)) { SetState(State::Hidden); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayComponent::State UiTooltipDisplayComponent::GetState() { return m_state; } //////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC STATIC MEMBER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() ->Version(2, &VersionConverter) ->Field("TriggerMode", &UiTooltipDisplayComponent::m_triggerMode) ->Field("AutoPosition", &UiTooltipDisplayComponent::m_autoPosition) ->Field("AutoPositionMode", &UiTooltipDisplayComponent::m_autoPositionMode) ->Field("Offset", &UiTooltipDisplayComponent::m_offset) ->Field("AutoSize", &UiTooltipDisplayComponent::m_autoSize) ->Field("Text", &UiTooltipDisplayComponent::m_textEntity) ->Field("DelayTime", &UiTooltipDisplayComponent::m_delayTime) ->Field("DisplayTime", &UiTooltipDisplayComponent::m_displayTime) ->Field("ShowSequence", &UiTooltipDisplayComponent::m_showSequenceName) ->Field("HideSequence", &UiTooltipDisplayComponent::m_hideSequenceName); AZ::EditContext* ec = serializeContext->GetEditContext(); if (ec) { auto editInfo = ec->Class("TooltipDisplay", "A component that handles how the tooltip element is to be displayed."); editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "UI") ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiTooltipDisplay.png") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiTooltipDisplay.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true); editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_triggerMode, "Trigger Mode", "Sets the way the tooltip is triggered to display.") ->EnumAttribute(UiTooltipDisplayInterface::TriggerMode::OnHover, "On Hover") ->EnumAttribute(UiTooltipDisplayInterface::TriggerMode::OnPress, "On Press") ->EnumAttribute(UiTooltipDisplayInterface::TriggerMode::OnClick, "On Click"); editInfo->DataElement(0, &UiTooltipDisplayComponent::m_autoPosition, "Auto position", "Whether the element will automatically be positioned.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c)); editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_autoPositionMode, "Positioning", "Sets the positioning behavior of the element.") ->EnumAttribute(UiTooltipDisplayInterface::AutoPositionMode::OffsetFromMouse, "Offset from mouse") ->EnumAttribute(UiTooltipDisplayInterface::AutoPositionMode::OffsetFromElement, "Offset from element") ->Attribute(AZ::Edit::Attributes::Visibility, &UiTooltipDisplayComponent::m_autoPosition); editInfo->DataElement(0, &UiTooltipDisplayComponent::m_offset, "Offset", "The offset to use when positioning the element.") ->Attribute(AZ::Edit::Attributes::Visibility, &UiTooltipDisplayComponent::m_autoPosition); editInfo->DataElement(0, &UiTooltipDisplayComponent::m_autoSize, "Auto size", "Whether the element will automatically be sized so that the text element's size is the same as the size of the tooltip string.\n" "If auto size is on, the text element's anchors should be apart."); editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_textEntity, "Text", "The UI element to hold the main tooltip text. Also used for auto sizing.") ->Attribute(AZ::Edit::Attributes::EnumValues, &UiTooltipDisplayComponent::PopulateTextEntityList); editInfo->DataElement(0, &UiTooltipDisplayComponent::m_delayTime, "Delay Time", "The amount of time to wait before displaying the element."); editInfo->DataElement(0, &UiTooltipDisplayComponent::m_displayTime, "Display Time", "The amount of time the element is to be displayed."); editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_showSequenceName, "Show Sequence", "The sequence to be played when the element is about to show.") ->Attribute(AZ::Edit::Attributes::StringList, &UiTooltipDisplayComponent::PopulateSequenceList); editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_hideSequenceName, "Hide Sequence", "The sequence to be played when the element is about to hide.") ->Attribute(AZ::Edit::Attributes::StringList, &UiTooltipDisplayComponent::PopulateSequenceList); } } AZ::BehaviorContext* behaviorContext = azrtti_cast(context); if (behaviorContext) { behaviorContext->Enum<(int)UiTooltipDisplayInterface::AutoPositionMode::OffsetFromMouse>("eUiTooltipDisplayAutoPositionMode_OffsetFromMouse") ->Enum<(int)UiTooltipDisplayInterface::AutoPositionMode::OffsetFromElement>("eUiTooltipDisplayAutoPositionMode_OffsetFromElement"); behaviorContext->Enum<(int)UiTooltipDisplayInterface::TriggerMode::OnHover>("eUiTooltipDisplayTriggerMode_OnHover") ->Enum<(int)UiTooltipDisplayInterface::TriggerMode::OnPress>("eUiTooltipDisplayTriggerMode_OnPress") ->Enum<(int)UiTooltipDisplayInterface::TriggerMode::OnPress>("eUiTooltipDisplayTriggerMode_OnClick"); behaviorContext->EBus("UiTooltipDisplayBus") ->Event("GetTriggerMode", &UiTooltipDisplayBus::Events::GetTriggerMode) ->Event("SetTriggerMode", &UiTooltipDisplayBus::Events::SetTriggerMode) ->Event("GetAutoPosition", &UiTooltipDisplayBus::Events::GetAutoPosition) ->Event("SetAutoPosition", &UiTooltipDisplayBus::Events::SetAutoPosition) ->Event("GetAutoPositionMode", &UiTooltipDisplayBus::Events::GetAutoPositionMode) ->Event("SetAutoPositionMode", &UiTooltipDisplayBus::Events::SetAutoPositionMode) ->Event("GetOffset", &UiTooltipDisplayBus::Events::GetOffset) ->Event("SetOffset", &UiTooltipDisplayBus::Events::SetOffset) ->Event("GetAutoSize", &UiTooltipDisplayBus::Events::GetAutoSize) ->Event("SetAutoSize", &UiTooltipDisplayBus::Events::SetAutoSize) ->Event("GetTextEntity", &UiTooltipDisplayBus::Events::GetTextEntity) ->Event("SetTextEntity", &UiTooltipDisplayBus::Events::SetTextEntity) ->Event("GetDelayTime", &UiTooltipDisplayBus::Events::GetDelayTime) ->Event("SetDelayTime", &UiTooltipDisplayBus::Events::SetDelayTime) ->Event("GetDisplayTime", &UiTooltipDisplayBus::Events::GetDisplayTime) ->Event("SetDisplayTime", &UiTooltipDisplayBus::Events::SetDisplayTime) ->VirtualProperty("DelayTime", "GetDelayTime", "SetDelayTime") ->VirtualProperty("DisplayTime", "GetDisplayTime", "SetDisplayTime"); behaviorContext->Class()->RequestBus("UiTooltipDisplayBus"); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // PROTECTED MEMBER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::Activate() { UiTooltipDisplayBus::Handler::BusConnect(GetEntityId()); UiInitializationBus::Handler::BusConnect(GetEntityId()); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::Deactivate() { UiTooltipDisplayBus::Handler::BusDisconnect(GetEntityId()); UiInitializationBus::Handler::BusDisconnect(GetEntityId()); // Stop listening for animation actions. The sequences may have been deleted at this point, // so first check if they still exist. IUiAnimationSystem* animSystem = nullptr; IUiAnimSequence* sequence; if (m_listeningForAnimationEvents) { m_listeningForAnimationEvents = false; sequence = GetSequence(m_showSequenceName, animSystem); if (sequence) { animSystem->RemoveUiAnimationListener(sequence, this); } sequence = GetSequence(m_hideSequenceName, animSystem); if (sequence) { animSystem->RemoveUiAnimationListener(sequence, this); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::SetState(State state) { m_state = state; m_stateStartTime = gEnv->pTimer->GetCurrTime(ITimer::ETIMER_UI); switch (m_state) { case State::Hiding: UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnHiding); break; case State::Hidden: UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnHidden); UiElementBus::Event(GetEntityId(), &UiElementBus::Events::SetIsEnabled, false); break; case State::Showing: case State::Shown: UiElementBus::Event(GetEntityId(), &UiElementBus::Events::SetIsEnabled, true); if (m_state == State::Showing) { UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnShowing); } else { UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnShown); } break; default: break; } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::EndTransitionState() { if (m_state == State::Showing) { if (m_showSequence) { IUiAnimationSystem* animSystem = GetAnimationSystem(); if (animSystem) { animSystem->StopSequence(m_showSequence); } } SetState(State::Shown); } else if (m_state == State::Hiding) { if (m_hideSequence) { IUiAnimationSystem* animSystem = GetAnimationSystem(); if (animSystem) { animSystem->StopSequence(m_hideSequence); } } SetState(State::Hidden); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::Show() { // It is assumed that current state is DelayBeforeShow if (m_autoSize && m_textEntity.IsValid()) { // Initialize max wrap text width if (m_maxWrapTextWidth < 0.0f) { UiTransformInterface::Rect textRect; EBUS_EVENT_ID(m_textEntity, UiTransformBus, GetCanvasSpaceRectNoScaleRotate, textRect); m_maxWrapTextWidth = textRect.GetWidth(); } // If wrapping is on, reset display element to its original width UiTextInterface::WrapTextSetting wrapSetting = UiTextInterface::WrapTextSetting::NoWrap; EBUS_EVENT_ID_RESULT(wrapSetting, m_textEntity, UiTextBus, GetWrapText); if (wrapSetting != UiTextInterface::WrapTextSetting::NoWrap) { UiTransformInterface::Rect textRect; EBUS_EVENT_ID(m_textEntity, UiTransformBus, GetCanvasSpaceRectNoScaleRotate, textRect); AZ::Vector2 delta(m_maxWrapTextWidth - textRect.GetWidth(), 0.0f); ResizeElementByDelta(GetEntityId(), delta); } } // Assign tooltip data to the tooltip display element EBUS_EVENT_ID(m_tooltipElement, UiTooltipDataPopulatorBus, PushDataToDisplayElement, GetEntityId()); // Auto-resize display element so that the text element is the same size as the size of its text string if (m_autoSize && m_textEntity.IsValid()) { AutoResize(); } // Auto-position display element if (m_autoPosition) { AutoPosition(); } // Check if there is a show animation to play IUiAnimationSystem* animSystem = nullptr; PrepareSequenceForPlay(m_showSequenceName, m_showSequence, animSystem); if (m_showSequence) { SetState(State::Showing); animSystem->PlaySequence(m_showSequence, nullptr, false, false); } else { SetState(State::Shown); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::AutoResize() { // Get the text string size AZ::Vector2 stringSize(-1.0f, -1.0f); EBUS_EVENT_ID_RESULT(stringSize, m_textEntity, UiTextBus, GetTextSize); if (stringSize.GetX() < 0.0f || stringSize.GetY() < 0.0f) { return; } // Get the text element's rect UiTransformInterface::Rect textRect; EBUS_EVENT_ID(m_textEntity, UiTransformBus, GetCanvasSpaceRectNoScaleRotate, textRect); // Get the difference between the text element's size and the string size AZ::Vector2 delta(stringSize.GetX() - textRect.GetWidth(), stringSize.GetY() - textRect.GetHeight()); UiTextInterface::WrapTextSetting wrapSetting; EBUS_EVENT_ID_RESULT(wrapSetting, m_textEntity, UiTextBus, GetWrapText); if (wrapSetting != UiTextInterface::WrapTextSetting::NoWrap) { // In order for the wrapping to remain the same after the resize, the // text element width would need to match the string width exactly. To accommodate // for slight variation in size, add a small value to ensure that the string will fit // inside the text element's bounds. The downside to this is there may be extra space // at the bottom, but this is unlikely. const float epsilon = 0.01f; delta.SetX(delta.GetX() + epsilon); } // Resize the display element by the difference ResizeElementByDelta(GetEntityId(), delta); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::ResizeElementByDelta(AZ::EntityId entityId, const AZ::Vector2& delta) { if ((delta.GetX() != 0.0f) || (delta.GetY() != 0.0f)) { // Resize the element based on the difference UiTransform2dInterface::Offsets offsets; EBUS_EVENT_ID_RESULT(offsets, entityId, UiTransform2dBus, GetOffsets); AZ::Vector2 pivot; EBUS_EVENT_ID_RESULT(pivot, entityId, UiTransformBus, GetPivot); if (delta.GetX() != 0.0f) { float leftOffsetDelta = delta.GetX() * pivot.GetX(); offsets.m_left -= leftOffsetDelta; offsets.m_right += delta.GetX() - leftOffsetDelta; } if (delta.GetY() != 0.0f) { float topOffsetDelta = delta.GetY() * pivot.GetY(); offsets.m_top -= topOffsetDelta; offsets.m_bottom += delta.GetY() - topOffsetDelta; } EBUS_EVENT_ID(entityId, UiTransform2dBus, SetOffsets, offsets); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::AutoPosition() { bool havePosition = false; AZ::Vector2 position(-1.0f, -1.0f); if (m_autoPositionMode == UiTooltipDisplayInterface::AutoPositionMode::OffsetFromMouse) { // Get current mouse position AZ::EntityId canvasId; EBUS_EVENT_ID_RESULT(canvasId, GetEntityId(), UiElementBus, GetCanvasEntityId); EBUS_EVENT_ID_RESULT(position, canvasId, UiCanvasBus, GetMousePosition); if (position.GetX() >= 0.0f && position.GetY() >= 0.0f) { // Check if the mouse is hovering over the tooltip element EBUS_EVENT_ID_RESULT(havePosition, m_tooltipElement, UiTransformBus, IsPointInRect, position); } } if (!havePosition) { // Get the pivot position of the tooltip element EBUS_EVENT_ID_RESULT(position, m_tooltipElement, UiTransformBus, GetViewportSpacePivot); } MoveToPosition(position, m_offset); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::MoveToPosition(const AZ::Vector2& point, const AZ::Vector2& offsetFromPoint) { // Move the display element so that its pivot is a certain distance from the point UiTransform2dInterface::Offsets offsets; EBUS_EVENT_ID_RESULT(offsets, GetEntityId(), UiTransform2dBus, GetOffsets); AZ::Vector2 pivot; EBUS_EVENT_ID_RESULT(pivot, GetEntityId(), UiTransformBus, GetViewportSpacePivot); AZ::Vector2 moveDist = (point + offsetFromPoint) - pivot; offsets += moveDist; EBUS_EVENT_ID(GetEntityId(), UiTransform2dBus, SetOffsets, offsets); // Make sure that the display element stays within the bounds of its parent // Get parent rect UiTransformInterface::Rect parentRect; AZ::Entity* parentEntity = nullptr; EBUS_EVENT_ID_RESULT(parentEntity, GetEntityId(), UiElementBus, GetParent); if (parentEntity) { EBUS_EVENT_ID(parentEntity->GetId(), UiTransformBus, GetCanvasSpaceRectNoScaleRotate, parentRect); } else { AZ::EntityId canvasEntityId; EBUS_EVENT_ID_RESULT(canvasEntityId, GetEntityId(), UiElementBus, GetCanvasEntityId); AZ::Vector2 size; EBUS_EVENT_ID_RESULT(size, canvasEntityId, UiCanvasBus, GetCanvasSize); parentRect.Set(0.0f, size.GetX(), 0.0f, size.GetY()); } // If the display element exceeds the top/bottom bounds of its parent, the offset // is flipped and applied to the top/bottom of the display element. // If positioning is to be relative to the tooltip element though, this step is skipped // to preserve the original position as much as possible. if (m_autoPositionMode != UiTooltipDisplayInterface::AutoPositionMode::OffsetFromElement) { CheckBoundsAndChangeYPosition(parentRect, point.GetY(), fabs(m_offset.GetY())); } ConstrainToBounds(parentRect); } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::CheckBoundsAndChangeYPosition(const UiTransformInterface::Rect& boundsRect, float yPoint, float yOffsetFromPoint) { // Get display element rect UiTransformInterface::Rect rect = GetAxisAlignedRect(); float yMoveDist = 0.0f; // Check upper and lower bounds if (rect.top < boundsRect.top) { // Move top of display element to an offset below the point yMoveDist = (yPoint + yOffsetFromPoint) - rect.top; } else if (rect.bottom > boundsRect.bottom) { // Move bottom of display element to an offset above the point yMoveDist = (yPoint - yOffsetFromPoint) - rect.bottom; } if (yMoveDist != 0.0f) { UiTransform2dInterface::Offsets offsets; EBUS_EVENT_ID_RESULT(offsets, GetEntityId(), UiTransform2dBus, GetOffsets); offsets.m_top += yMoveDist; offsets.m_bottom += yMoveDist; EBUS_EVENT_ID(GetEntityId(), UiTransform2dBus, SetOffsets, offsets); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::ConstrainToBounds(const UiTransformInterface::Rect& boundsRect) { // Get display element rect UiTransformInterface::Rect rect = GetAxisAlignedRect(); AZ::Vector2 moveDist(0.0f, 0.0f); // Check left and right bounds if (rect.left < boundsRect.left) { moveDist.SetX(boundsRect.left - rect.left); } else if (rect.right > boundsRect.right) { moveDist.SetX(boundsRect.right - rect.right); } // Check upper and lower bounds if (rect.top < boundsRect.top) { moveDist.SetY(boundsRect.top - rect.top); } else if (rect.bottom > boundsRect.bottom) { moveDist.SetY(boundsRect.bottom - rect.bottom); } if (moveDist.GetX() != 0.0f || moveDist.GetY() != 0.0f) { UiTransform2dInterface::Offsets offsets; EBUS_EVENT_ID_RESULT(offsets, GetEntityId(), UiTransform2dBus, GetOffsets); offsets += moveDist; EBUS_EVENT_ID(GetEntityId(), UiTransform2dBus, SetOffsets, offsets); } } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTransformInterface::Rect UiTooltipDisplayComponent::GetAxisAlignedRect() { UiTransformInterface::RectPoints points; EBUS_EVENT_ID(GetEntityId(), UiTransformBus, GetCanvasSpacePointsNoScaleRotate, points); AZ::Matrix4x4 transform; EBUS_EVENT_ID(GetEntityId(), UiTransformBus, GetLocalTransform, transform); points = points.Transform(transform); UiTransformInterface::Rect rect; rect.left = points.GetAxisAlignedTopLeft().GetX(); rect.right = points.GetAxisAlignedBottomRight().GetX(); rect.top = points.GetAxisAlignedTopLeft().GetY(); rect.bottom = points.GetAxisAlignedBottomRight().GetY(); return rect; } //////////////////////////////////////////////////////////////////////////////////////////////////// IUiAnimationSystem* UiTooltipDisplayComponent::GetAnimationSystem() { AZ::EntityId canvasId; EBUS_EVENT_ID_RESULT(canvasId, GetEntityId(), UiElementBus, GetCanvasEntityId); IUiAnimationSystem* animSystem = nullptr; EBUS_EVENT_ID_RESULT(animSystem, canvasId, UiCanvasBus, GetAnimationSystem); return animSystem; } //////////////////////////////////////////////////////////////////////////////////////////////////// IUiAnimSequence* UiTooltipDisplayComponent::GetSequence(const AZStd::string& sequenceName, IUiAnimationSystem*& animSystemOut) { IUiAnimSequence* sequence = nullptr; if (!sequenceName.empty() && (sequenceName != "")) { IUiAnimationSystem* animSystem = GetAnimationSystem(); if (animSystem) { sequence = animSystem->FindSequence(sequenceName.c_str()); animSystemOut = animSystem; } } return sequence; } //////////////////////////////////////////////////////////////////////////////////////////////////// void UiTooltipDisplayComponent::PrepareSequenceForPlay(const AZStd::string& sequenceName, IUiAnimSequence*& sequence, IUiAnimationSystem*& animSystemOut) { IUiAnimationSystem* animSystem = nullptr; if (!sequence) { sequence = GetSequence(sequenceName, animSystem); if (sequence) { m_listeningForAnimationEvents = true; animSystem->AddUiAnimationListener(sequence, this); } } else { animSystem = GetAnimationSystem(); if (!animSystem) { sequence = nullptr; } } if (sequence) { animSystemOut = animSystem; } } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayComponent::EntityComboBoxVec UiTooltipDisplayComponent::PopulateTextEntityList() { EntityComboBoxVec result; // add a first entry for "None" result.push_back(AZStd::make_pair(AZ::EntityId(AZ::EntityId()), "")); // allow the destination to be the same entity as the source by // adding this entity (if it has a text component) if (UiTextBus::FindFirstHandler(GetEntityId())) { result.push_back(AZStd::make_pair(AZ::EntityId(GetEntityId()), GetEntity()->GetName())); } // Get a list of all descendant elements that support the UiTextBus LyShine::EntityArray matchingElements; EBUS_EVENT_ID(GetEntityId(), UiElementBus, FindDescendantElements, [](const AZ::Entity* entity) { return UiTextBus::FindFirstHandler(entity->GetId()) != nullptr; }, matchingElements); // add their names to the StringList and their IDs to the id list for (auto childEntity : matchingElements) { result.push_back(AZStd::make_pair(AZ::EntityId(childEntity->GetId()), childEntity->GetName())); } return result; } //////////////////////////////////////////////////////////////////////////////////////////////////// UiTooltipDisplayComponent::SequenceComboBoxVec UiTooltipDisplayComponent::PopulateSequenceList() { SequenceComboBoxVec result; // add a first entry for "None" result.push_back(""); IUiAnimationSystem* animSystem = GetAnimationSystem(); if (animSystem) { for (int i = 0; i < animSystem->GetNumSequences(); i++) { IUiAnimSequence* sequence = animSystem->GetSequence(i); if (sequence) { result.push_back(sequence->GetName()); } } } // Sort the sequence names SequenceComboBoxVec::iterator iterBegin = result.begin(); ++iterBegin; if (iterBegin < result.end()) { AZStd::sort(iterBegin, result.end(), [](const AZStd::string s1, const AZStd::string s2) { return s1 < s2; }); } return result; } //////////////////////////////////////////////////////////////////////////////////////////////////// // PRIVATE STATIC MEMBER FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// bool UiTooltipDisplayComponent::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { // conversion from version 1: // - Need to convert Vec2 to AZ::Vector2 if (classElement.GetVersion() <= 1) { if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "Offset")) { return false; } } return true; }