You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/MiniGUI/MiniGUI.cpp

863 lines
23 KiB
C++

/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implementation of the MiniGUI class
#include "CrySystem_precompiled.h"
#include "MiniGUI.h"
#include "DrawContext.h"
#include "MiniButton.h"
#include "MiniMenu.h"
#include "MiniInfoBox.h"
#include "MiniTable.h"
#include <ISystem.h>
#include <IRenderer.h>
#include <LyShine/Bus/UiCursorBus.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
namespace minigui
{
CRYREGISTER_SINGLETON_CLASS(CMiniGUI)
}
MINIGUI_BEGIN
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::InitMetrics()
{
m_metrics.clrText = ColorB(255, 255, 255, 255);
m_metrics.clrTextSelected = ColorB(0, 255, 0, 255);
m_metrics.fTextSize = 12.0f;
m_metrics.clrTitle = ColorB(255, 255, 255, 255);
m_metrics.fTitleSize = 14.0f;
const int backgroundAlpha = 255;
m_metrics.clrBackground = ColorB(20, 20, 20, backgroundAlpha);
m_metrics.clrBackgroundHighlight = ColorB(10, 10, 150, backgroundAlpha);
m_metrics.clrBackgroundSelected = ColorB(10, 120, 10, backgroundAlpha);
m_metrics.clrFrameBorder = ColorB(255, 0, 0, 255);
m_metrics.clrFrameBorderHighlight = ColorB(255, 255, 0, 255);
m_metrics.clrFrameBorderOutOfFocus = ColorB(0, 0, 0, 255);
m_metrics.clrChecked = ColorB(0, 0, 0, 255);
m_metrics.outOfFocusAlpha = 32;
}
class CMiniCtrlRoot
: public CMiniCtrl
{
public:
CMiniCtrlRoot() {};
virtual EMiniCtrlType GetType() const { return eCtrlType_Unknown; };
virtual void OnPaint([[maybe_unused]] class CDrawContext& dc) {};
};
//////////////////////////////////////////////////////////////////////////
CMiniGUI::CMiniGUI()
: m_enabled(false)
, m_inFocus(true)
, m_pDPadMenu(NULL)
, m_pMovingCtrl(NULL)
{
}
//////////////////////////////////////////////////////////////////////////
CMiniGUI::~CMiniGUI()
{
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Init()
{
m_pEventListener = NULL;
InitMetrics();
AzFramework::InputChannelEventListener::Connect();
m_pRootCtrl = new CMiniCtrlRoot;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Reset()
{
m_pRootCtrl->Reset();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SaveState()
{
m_pRootCtrl->SaveState();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::RestoreState()
{
m_pRootCtrl->RestoreState();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetEnabled(bool status)
{
m_enabled = status;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetInFocus(bool status)
{
if (status)
{
m_inFocus = true;
}
else
{
CloseDPadMenu();
m_inFocus = false;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Done()
{
AzFramework::InputChannelEventListener::Disconnect();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Draw()
{
FUNCTION_PROFILER_FAST(GetISystem(), PROFILE_SYSTEM, g_bProfilerEnabled);
// When console opened hide MiniGui
bool bConsoleOpened = gEnv->pConsole->IsOpened();
if (m_enabled && !bConsoleOpened)
{
ProcessInput();
CDrawContext dc(&m_metrics);
dc.StartDrawing();
{
// Draw all controls.
m_pRootCtrl->DrawCtrl(dc);
}
dc.StopDrawing();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::ProcessInput()
{
//check we are not in digital selection mode
if (!m_pDPadMenu)
{
float mx(0), my(0);
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
AzFramework::InputSystemCursorRequestBus::EventResult(systemCursorPositionNormalized,
AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::GetSystemCursorPositionNormalized);
mx = systemCursorPositionNormalized.GetX() * gEnv->pRenderer->GetWidth();
my = systemCursorPositionNormalized.GetY() * gEnv->pRenderer->GetHeight();
//update moving control
if (m_pMovingCtrl)
{
m_pMovingCtrl->Move(mx, my);
}
IMiniCtrl* pCtrl = GetCtrlFromPoint(mx, my);
if (pCtrl)
{
SetHighlight(pCtrl, true, mx, my);
}
else
{
SetHighlight(m_highlightedCtrl, false, mx, my);
}
}
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniGUI::GetCtrlFromPoint(float x, float y) const
{
// Draw all controls.
return m_pRootCtrl->GetCtrlFromPoint(x, y);
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetHighlight(IMiniCtrl* pCtrl, bool bEnable, float x, float y)
{
if (pCtrl)
{
if (m_highlightedCtrl && m_highlightedCtrl != pCtrl)
{
m_highlightedCtrl->OnEvent(x, y, eCtrlEvent_MouseOff);
m_highlightedCtrl->ClearFlag(eCtrl_Highlight);
}
if (bEnable)
{
pCtrl->OnEvent(x, y, eCtrlEvent_MouseOver);
pCtrl->SetFlag(eCtrl_Highlight);
m_highlightedCtrl = pCtrl;
}
else
{
pCtrl->OnEvent(x, y, eCtrlEvent_MouseOff);
pCtrl->ClearFlag(eCtrl_Highlight);
m_highlightedCtrl = NULL;
}
}
else
{
assert(bEnable == false);
if (m_highlightedCtrl)
{
m_highlightedCtrl->OnEvent(x, y, eCtrlEvent_MouseOff);
m_highlightedCtrl->ClearFlag(eCtrl_Highlight);
m_highlightedCtrl = NULL;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetFocus(IMiniCtrl* pCtrl, bool bEnable)
{
if (m_focusCtrl)
{
m_focusCtrl->ClearFlag(eCtrl_Focus);
}
m_focusCtrl = pCtrl;
if (m_focusCtrl)
{
if (bEnable)
{
m_focusCtrl->SetFlag(eCtrl_Focus);
}
else
{
m_focusCtrl->ClearFlag(eCtrl_Focus);
}
}
}
//////////////////////////////////////////////////////////////////////////
SMetrics& CMiniGUI::Metrics()
{
return m_metrics;
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniGUI::CreateCtrl(IMiniCtrl* pParentCtrl, int nCtrlID, EMiniCtrlType type, int nCtrlFlags, const Rect& rc, const char* title)
{
CMiniCtrl* pCtrl = 0;
// Test code.
switch (type)
{
case eCtrlType_Button:
pCtrl = new CMiniButton;
break;
case eCtrlType_Menu:
pCtrl = new CMiniMenu;
break;
case eCtrlType_InfoBox:
pCtrl = new CMiniInfoBox;
break;
case eCtrlType_Table:
pCtrl = new CMiniTable;
break;
default:
assert(0 && "Unknown MiniGUI control type");
break;
}
;
if (pCtrl)
{
pCtrl->SetGUI(this);
pCtrl->SetFlag(nCtrlFlags);
pCtrl->SetTitle(title);
pCtrl->SetRect(rc);
pCtrl->SetId(nCtrlID);
if (pCtrl->CheckFlag(eCtrl_AutoResize))
{
pCtrl->AutoResize();
}
if (pCtrl->CheckFlag(eCtrl_CloseButton))
{
pCtrl->CreateCloseButton();
}
if (pParentCtrl)
{
pParentCtrl->AddSubCtrl(pCtrl);
}
else
{
m_pRootCtrl->AddSubCtrl(pCtrl);
}
if (type == eCtrlType_Menu && pParentCtrl == NULL)
{
m_rootMenus.push_back(pCtrl);
}
}
return pCtrl;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::OnCommand(SCommand& cmd)
{
if (m_pEventListener)
{
m_pEventListener->OnCommand(cmd);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::OnMouseInputEvent(const AzFramework::InputChannel& inputChannel)
{
if (!m_inFocus || !m_enabled)
{
return;
}
const AzFramework::InputChannel::PositionData2D* positionData2D = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
if (!positionData2D)
{
return;
}
const float mx = positionData2D->m_normalizedPosition.GetX() * gEnv->pRenderer->GetWidth();
const float my = positionData2D->m_normalizedPosition.GetY() * gEnv->pRenderer->GetHeight();
IMiniCtrl* pCtrl = GetCtrlFromPoint(mx, my);
if (pCtrl)
{
const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
if (channelId == AzFramework::InputDeviceMouse::Button::Left)
{
if (inputChannel.IsStateBegan())
{
pCtrl->OnEvent(mx, my, eCtrlEvent_LButtonDown);
}
else if (inputChannel.IsStateEnded())
{
pCtrl->OnEvent(mx, my, eCtrlEvent_LButtonUp);
}
}
}
}
void CMiniGUI::SetDPadMenu(IMiniCtrl* pMenu)
{
m_pDPadMenu = (CMiniMenu*)pMenu;
UiCursorBus::Broadcast(&UiCursorInterface::DecrementVisibleCounter);
}
void CMiniGUI::CloseDPadMenu()
{
if (m_pDPadMenu)
{
CMiniMenu* closeMenu = m_pDPadMenu;
//close menu and all parent menus
do
{
closeMenu->Close();
closeMenu = (CMiniMenu*)closeMenu->GetParent();
} while (closeMenu->GetType() == eCtrlType_Menu);
m_pDPadMenu->ClearFlag(eCtrl_Highlight);
m_pDPadMenu = NULL;
UiCursorBus::Broadcast(&UiCursorInterface::IncrementVisibleCounter);
}
}
void CMiniGUI::UpdateDPadMenu(const AzFramework::InputChannel& inputChannel)
{
const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
const bool isPressed = inputChannel.IsStateBegan();
if (m_pDPadMenu)
{
if (channelId == AzFramework::InputDeviceGamepad::Button::B)
{
CloseDPadMenu();
return;
}
if (isPressed)
{
if (channelId == AzFramework::InputDeviceGamepad::Button::DD ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LD)
{
m_pDPadMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadDown);
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::DU ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LU)
{
m_pDPadMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadUp);
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::DL ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LL)
{
CMiniMenu* pNewMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadLeft);
//get previous root menu
if (pNewMenu == NULL)
{
int i = 0, nRootMenus = m_rootMenus.size();
for (i = 0; i < nRootMenus; i++)
{
if (m_rootMenus[i] == m_pDPadMenu)
{
break;
}
}
if (i > 0)
{
m_pDPadMenu->Close();
m_pDPadMenu->ClearFlag(eCtrl_Highlight);
m_pDPadMenu = (CMiniMenu*)m_rootMenus[i - 1];
m_pDPadMenu->Open();
m_pDPadMenu->SetFlag(eCtrl_Highlight);
}
//else selected menu remains the same
}
else
{
m_pDPadMenu = pNewMenu;
}
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::DR ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LR)
{
CMiniMenu* pNewMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadRight);
//get next root menu
if (pNewMenu == NULL)
{
int i = 0, nRootMenus = m_rootMenus.size();
for (i = 0; i < nRootMenus; i++)
{
if (m_rootMenus[i] == m_pDPadMenu)
{
break;
}
}
if (i < nRootMenus - 1)
{
m_pDPadMenu->Close();
m_pDPadMenu->ClearFlag(eCtrl_Highlight);
m_pDPadMenu = (CMiniMenu*)m_rootMenus[i + 1];
m_pDPadMenu->Open();
m_pDPadMenu->SetFlag(eCtrl_Highlight);
}
//else selected menu remains the same
}
else
{
m_pDPadMenu = pNewMenu;
}
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::A)
{
m_pDPadMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_LButtonDown);
}
}
}
}
bool CMiniGUI::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
{
const AzFramework::InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
if (AzFramework::InputDeviceMouse::IsMouseDevice(deviceId))
{
OnMouseInputEvent(inputChannel);
return false;
}
if (!m_inFocus)
{
return false;
}
if (!AzFramework::InputDeviceGamepad::IsGamepadDevice(deviceId))
{
return false;
}
if (m_pDPadMenu)
{
UpdateDPadMenu(inputChannel);
}
else
{
float posX = 0.0f;
float posY = 0.0f;
const AzFramework::InputChannel::PositionData2D* positionData2D = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
if (positionData2D)
{
posX = positionData2D->m_normalizedPosition.GetX() * gEnv->pRenderer->GetWidth();
posY = positionData2D->m_normalizedPosition.GetY() * gEnv->pRenderer->GetHeight();
}
IMiniCtrl* pCtrl = GetCtrlFromPoint(posX, posY);
if (pCtrl)
{
const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
if (channelId == AzFramework::InputDeviceGamepad::Button::A)
{
switch (inputChannel.GetState())
{
case AzFramework::InputChannel::State::Began:
pCtrl->OnEvent(posX, posY, eCtrlEvent_LButtonDown);
break;
case AzFramework::InputChannel::State::Ended:
pCtrl->OnEvent(posX, posY, eCtrlEvent_LButtonUp);
break;
case AzFramework::InputChannel::State::Updated:
pCtrl->OnEvent(posX, posY, eCtrlEvent_LButtonPressed);
//if we've clicked on a menu, enter menu selection mode, disable mouse
if (pCtrl->GetType() == eCtrlType_Menu)
{
SetDPadMenu(pCtrl);
}
break;
}
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetEventListener(IMiniGUIEventListener* pListener)
{
m_pEventListener = pListener;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::RemoveAllCtrl()
{
m_highlightedCtrl = NULL;
//reset all console variables to default state
Reset();
m_pRootCtrl->RemoveAllSubCtrl();
}
//////////////////////////////////////////////////////////////////////////
//CMiniCtrl
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::Reset()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->Reset();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::SaveState()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->SaveState();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::RestoreState()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->RestoreState();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::AddSubCtrl(IMiniCtrl* pCtrl)
{
assert(pCtrl);
_smart_ptr<IMiniCtrl> pTempCtrl(pCtrl);
IMiniCtrl* pParent = pCtrl->GetParent();
if (pParent)
{
pParent->RemoveSubCtrl(pCtrl);
}
static_cast<CMiniCtrl*>(pCtrl)->m_pParent = this;
m_subCtrls.push_back(pCtrl);
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::RemoveSubCtrl(IMiniCtrl* pCtrl)
{
assert(pCtrl);
_smart_ptr<IMiniCtrl> pTempCtrl(pCtrl);
IMiniCtrl* pParent = pCtrl->GetParent();
if (pParent == this)
{
static_cast<CMiniCtrl*>(pCtrl)->m_pParent = 0;
for (int i = 0, num = (int)m_subCtrls.size(); i < num; i++)
{
if (m_subCtrls[i] == pCtrl)
{
m_subCtrls.erase(m_subCtrls.begin() + i);
break;
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::RemoveAllSubCtrl()
{
int nSubCtrls = m_subCtrls.size();
if (nSubCtrls)
{
for (int i = 0; i < nSubCtrls; i++)
{
IMiniCtrl* pSubCtrl = m_subCtrls[i].get();
pSubCtrl->RemoveAllSubCtrl();
}
m_subCtrls.clear();
}
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniCtrl::GetCtrlFromPoint(float x, float y)
{
if (is_flag(eCtrl_Hidden))
{
return 0;
}
for (int i = 0, num = (int)m_subCtrls.size(); i < num; i++)
{
float lx = x - m_rect.left;
float ly = y - m_rect.top;
IMiniCtrl* pHit = m_subCtrls[i]->GetCtrlFromPoint(lx, ly);
if (pHit)
{
return pHit;
}
}
if (m_rect.IsPointInside(x, y))
{
return this;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::DrawCtrl(CDrawContext& dc)
{
OnPaint(dc);
dc.PushClientRect(m_rect);
for (int i = 0, num = (int)m_subCtrls.size(); i < num; i++)
{
CMiniCtrl* pCtrl = static_cast<CMiniCtrl*>((IMiniCtrl*)m_subCtrls[i]);
if (!pCtrl->is_flag(eCtrl_Hidden))
{
pCtrl->DrawCtrl(dc);
}
}
dc.PopClientRect();
}
//////////////////////////////////////////////////////////////////////////
int CMiniCtrl::GetSubCtrlCount() const
{
return (int)m_subCtrls.size();
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniCtrl::GetSubCtrl(int nIndex) const
{
assert(nIndex >= 0 && nIndex < (int)m_subCtrls.size());
return m_subCtrls[nIndex];
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::SetRect(const Rect& rc)
{
m_rect = rc;
if (m_pCloseButton)
{
float width = rc.Width();
//relative position of cross box
Rect closeRect(width - 20.f, 0.f, width, 20.f);
m_pCloseButton->SetRect(closeRect);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::SetVisible(bool state)
{
if (state)
{
clear_flag(eCtrl_Hidden);
}
else
{
set_flag(eCtrl_Hidden);
}
if (m_pCloseButton)
{
m_pCloseButton->SetVisible(state);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::AutoResize()
{
uint32 stringLen = m_title.length();
if (stringLen)
{
//just an approximation for now - should take into account font size / kerning
m_rect.right = m_rect.left + (8.5f * stringLen);
}
m_requiresResize = false;
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::StartMoving(float x, float y)
{
if (!m_moving)
{
m_prevX = x;
m_prevY = y;
m_moving = true;
m_pGUI->SetMovingCtrl(this);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::StopMoving()
{
if (m_moving)
{
m_moving = false;
m_pGUI->SetMovingCtrl(NULL);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::Move(float x, float y)
{
if (m_moving)
{
float moveX = x - m_prevX;
float moveY = y - m_prevY;
m_rect.top += moveY;
m_rect.bottom += moveY;
m_rect.left += moveX;
m_rect.right += moveX;
m_prevX = x;
m_prevY = y;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::OnEvent(float x, float y, EMiniCtrlEvent event)
{
switch (event)
{
case eCtrlEvent_LButtonDown:
{
if (is_flag(eCtrl_Highlight | eCtrl_Moveable))
{
StartMoving(x, y);
}
}
break;
case eCtrlEvent_LButtonUp:
if (m_moving)
{
StopMoving();
}
break;
case eCtrlEvent_MouseOver:
break;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::CreateCloseButton()
{
if (m_pGUI)
{
m_pCloseButton = m_pGUI->CreateCtrl(this, 100, eCtrlType_Button, 0, Rect(0, 0, 100, 20), "X");
if (m_pCloseButton)
{
m_pCloseButton->SetConnectedCtrl(this);
float width = m_rect.Width();
//relative position of cross box
Rect closeRect(width - 20.f, 0.f, width, 20.f);
m_pCloseButton->SetRect(closeRect);
}
}
}
MINIGUI_END