/* * 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 <StdAfx.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> CRYREGISTER_SINGLETON_CLASS(minigui::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(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