DragonNest/Common/EtInterface/EtUIDialog.h
Cussrro 268e12d15a 1
2024-12-21 12:09:12 +08:00

527 lines
20 KiB
C++

#pragma once
#include "EtUIDialogInfo.h"
#include "EtUIControlProperty.h"
#include "EtUIDialogBase.h"
#include "LostDeviceProcess.h"
#include "EtUITemplate.h"
#define UI_FILE_STRING "Eternity Engine UI File 0.3"
#define UI_HEADER_RESERVED ( 1024 - sizeof( SUIFileHeader ) )
#define UI_FILE_VERSION_01 0x0101
#define UI_FILE_VERSION 0x0102
#define PRE_FIX_COMBOBOX_ELLIPSIS
struct SUIFileHeader
{
char szHeaderString[ 256 ];
int nVersion;
int nTemplateCount;
int nControlCount;
int nFontCount;
SUIFileHeader()
{
ZeroMemory( this, sizeof(SUIFileHeader) );
strcpy_s( szHeaderString, sizeof(szHeaderString), UI_FILE_STRING );
nVersion = UI_FILE_VERSION;
}
};
class CEtUIControl;
class CEtUITemplate;
struct SUIElement;
class CEtUIDialog;
class CEtUICallback
{
public:
CEtUICallback() {}
~CEtUICallback() {}
public:
virtual void OnUICallbackProc( int nID, UINT nCommand, CEtUIControl *pControl, UINT uiMsg = 0 ) {}
};
class CEtUIDialog : public CEtUIDialogBase
{
public:
CEtUIDialog( UI_DIALOG_TYPE dialogType = UI_TYPE_FOCUS, CEtUIDialog *pParentDialog = NULL, int nID = -1, CEtUICallback *pCallback = NULL, bool bAutoCursor = false );
virtual ~CEtUIDialog(void);
enum FadeModeEnum {
None,
AllowRender,
CancelRender,
};
protected:
bool m_bShow;
bool m_bAcceptInputMsgWhenHide; // show 상태던 아니던 메시지를 받을 수 있게 해주는 플래그. 엮여있는 다이얼로그를 hide 시키고 따로 렌더링하는 스킬트리에서 메시지 처리하기 위해 사용함.
HWND m_hWnd;
std::vector< CEtUIControl * > m_vecControl;
std::vector< CEtUITemplate * > m_vecUITemplate;
std::vector< SUIElement > m_vecDlgFrameElement;
std::vector< SUICoord > m_vecDlgFrameCoord;
EtTextureHandle m_hTexture;
EtTextureHandle m_hDlgTexture;
CEtUIControl *m_pDefaultControl;
std::vector< CEtUIControl* > m_TempControlList;
int m_nDialogID;
CEtUICallback *m_pCallback;
SUIDialogInfo m_DlgInfo;
float m_fScreenWidth;
float m_fScreenWidthRatio;
float m_fScreenWidthBorder;
float m_fScreenHeight;
float m_fScreenHeightRatio;
float m_fScreenHeightBorder;
std::string m_strCmdControlName; // Note : ProcessCommand안에서 사용될 컨트롤 이름
SUIColorEx m_renderDlgColor;
bool m_bFadeOut;
FadeModeEnum m_FadeMode;
bool m_bMouseInDialog;
bool m_bEndInitialize;
float m_fMouseX;
float m_fMouseY;
SHORT m_HotKeyState;
int m_nHotKey;
CEtUIDialog *m_pParentDialog; // Note : 자신의 부모 윈도우.
std::list<CEtUIDialog*> m_listChildDialog; // Note : 자식 윈도우들.
std::list<CEtUIDialog*> m_listChildModalDialog; // Note : 자식인데 모달로 생성된 윈도우들.
std::string m_strDialogFileName;
SUICoord m_BaseDlgCoord;
bool m_bPassMessageToChild;
bool m_bShowModal;
bool m_bAutoCursor;
bool m_bCursor;
// 리스트박스 아이템이나, 엘리먼트 아이템일 경우 일반 다이얼로그와 구분을 위해 체크를 해둡니다.
bool m_bElementDialog;
bool m_bElementDialogShowState;
std::map< std::string, CEtUIDialog*> m_cachedControls;
std::vector< std::pair<CEtUIDialog*, bool> > m_vecChildRenderPriorityInfo;
public:
// 마켓다이얼로그의 강제스케일때문에 에디트박스 캐럿이 안맞는 문제가 발생했습니다.
// 어쩔 수 없는 경우 빼고는 최대한 다른 곳에서는 사용하지 말아주세요.
//
// 기존의 강제스케일 방식의 단점은,
// FontMng와 연동되지 않아 마우스 드래그 및 렌더링에 별도처리를 해줘야한다.
// 어차피 보여질 다이얼로그(마켓,인벤,툴팁)에 대해 스케일을 셋팅하는 방식이라면 s_fDialogScale을 건드리는게 낫다.
// 이므로, s_fDialogScale값을 강제로 바꾸는 방식으로 변경하게 되었습니다.
static void SetForceDialogScale( float fScale );
public:
static float s_fScreenMouseX;
static float s_fScreenMouseY;
protected:
// Note : 아래는 툴팁에 관련된 스태틱 변수들. 지금은 툴팁이 한번에 하나만 출력되는 구조이다.
// 하지만 이런 구조는 별로 유연하지 못한듯 하다. 나중에는 스태틱이 아니라 다이얼로그마다
// 이 변수들과 함수를 가지는게 좋겠다.
//
static int s_nTooltipStringIndex;
static std::wstring s_strTooltipString;
static CEtUIDialog *s_pTooltipDlg;
static float s_fDialogScale;
static float s_fDialogScaleValueVert;
static float s_fDialogScaleValueHori;
static float s_fMinDialogScale;
static float s_fMaxDialogScale;
static int s_nUISize;
static void (_stdcall *s_pAutoCursorPtr)( bool bShow );
static int s_nDialogTextureSize;
float m_fLastShowDelta;
bool m_bLoadedTexture;
public:
static CEtUIControl *s_pMouseEnterControl;
static void ReleaseMouseEnterControl();
static void SetMouseEnterControl( CEtUIControl *pControl );
static void SetAutoCursorCallback( void (_stdcall *pFunc)( bool bShow ) ) { s_pAutoCursorPtr = pFunc; }
// FadeForBlow는 UI숨긴 상태에서로 렌더해야한다고 한다. 할수없이 상위단으로 뺀다.
static CEtUIDialog *s_pFadeDlg;
public:
void AddChildDialog( CEtUIDialog *pDialog );
void AddChildModalDialog( CEtUIDialog *pDialog );
void SetChildRenderPriority(CEtUIDialog* pDlg, bool bTail);
void RefreshChildRenderPriority();
protected:
// EtUIListBoxEx가 나오기전까지는 자식 다이얼로그를 중간에 지운적이 없어서인지,
// 아예 자식다이얼로그를 지우는 함수자체가 없었다.
// 그러다 EtUIListBoxEx의 아이템을 자식 다이얼로그로 처리하려 할때 추가한 함수다.
// (실제로는 자식 다이얼로그로 처리하지 않기때문에 사용하지는 않는다.)
//
// 현재는 EtUIDialog의 소멸자에서 자신이 삭제될때 부모에게 리스트에서 빼라는 용도로 사용된다.
void DelChildDialog( CEtUIDialog *pDialog );
void DelChildModalDialog( CEtUIDialog *pDialog );
public:
bool IsRootDialog() { return (m_pParentDialog==NULL)?true:false; }
bool IsChildDialog( const CEtUIDialog *pDialog );
CEtUIDialog* GetParentDialog() { return m_pParentDialog; }
public:
// Note : 자식 윈도우의 Show()를 할때는 그 윈도우의 Show()를 호출하지 않고 아래 함수를 호출한다.
//
void ShowChildDialog( CEtUIDialog *pDialog, bool bShow );
protected:
void ShowChildDialog( std::list<CEtUIDialog*> &listDialog, CEtUIDialog *pDialog, bool bShow );
bool IsAllowRender();
public:
static void SetTooltipDlg( CEtUIDialog *pTooltipDlg ) { s_pTooltipDlg = pTooltipDlg; }
static void ShowTooltipDlg( CEtUIControl *pControl, bool bShow, int nStringIndex = 0, DWORD color = 0xffffffff, bool bPermanent = false );
static void ShowTooltipDlg( CEtUIControl *pControl, bool bShow, const std::wstring &strTooltip, DWORD color = 0xffffffff, bool bPermanent = false );
static int GetTooltipStringIndex() { return s_nTooltipStringIndex; }
static bool IsTooltipControl(CEtUIControl* pCtrl);
static bool IsShowTooltipDlg() { return s_pTooltipDlg->IsShow(); }
virtual void SetTooltipControl( CEtUIControl *pControl ) {}
virtual void SetTooltipInfo(DWORD color = 0xffffffff, bool bPermanent = true) { _ASSERT(0); }
virtual void ClearTooltipInfo() { _ASSERT(0); }
virtual CEtUIControl* GetTooltipCtrl() const { _ASSERT(0); return NULL; }
static float GetDialogScale() { return s_fDialogScale; }
static void UpDialogSize();
static void DownDialogSize();
static void CalcDialogScaleValue( int nWidth = 0, int nHeight = 0 );
// 해상도에 따른 스케일값 구해 적용하기.
static void CalcDialogScaleByResolution( int nWidth, int nHeight );
static void SetDialogSize( int nStep, int nWidth = 0, int nHeight = 0 );
static int GetTextureMemorySize() { return s_nDialogTextureSize; }
void ProcessMemoryOptimize( float fElapsedTime );
void FreeDialogTexture();
void LoadDialogTexture();
public:
void SetFadeMode( FadeModeEnum Mode ) { m_FadeMode = Mode; }
bool IsMouseInDlg() { return m_bMouseInDialog; }
void GetMouseMovePoints( float &fMouseX, float &fMouseY );
void GetScreenMouseMovePoints( float &fMouseX, float &fMouseY );
void SetRenderColor( DWORD dwColor )
{
m_renderDlgColor.dwColor[UI_STATE_NORMAL] = dwColor;
m_renderDlgColor.dwCurrentColor = dwColor;
}
void SetSoundEnable( bool bEnable ) { m_DlgInfo.bSound = bEnable; }
// Note : 부모 윈도우가 보이지 않아도 자식의 메세지프로시져에 들어가도록 한다.
// 예를 들면 채팅창에서 채팅창이 안보일때 마우스 클릭으로 채팅창에 포커스가 가도록 하기 위해 쓰였다.
//
void SetPassMessageToChild( bool bPass ) { m_bPassMessageToChild = bPass; }
void SetShowModal( bool bShowModal ) { m_bShowModal = bShowModal; }
void SetElementDialog( bool bElement ) { m_bElementDialog = bElement; }
bool IsElementDialog() { return m_bElementDialog; }
// ElementDialog의 경우엔 보통 UI_TYPE_SELF로 생성해서 직접 MsgProc및 Render되는 경우가 많기때문에
// 현재 보여지고 있는 다이얼로그인지를 IsShow함수로 검사할 수 없을때가 많다. 이땐 직접 설정해서 얻어오는 수밖에 없다.
virtual void SetElementDialogShowState( bool bShow ) { m_bElementDialogShowState = bShow; }
bool IsShowElementDialog() { return m_bElementDialogShowState; }
// 핫키버그때문에 어쩔 수 없이 만든 함수.
void SetHotKeyState( SHORT HotKeyState ) { m_HotKeyState = HotKeyState; }
bool IsCmdControl( const char *szCmdCtlName );
protected:
void UpdateScreen();
void UpdateFrameRectsEx();
void SetCmdControlName( const char *szCmdCtlName ) { m_strCmdControlName = szCmdCtlName; }
const std::string& GetCmdControlName() const { return m_strCmdControlName; }
void FadeIn( float fElapsedTime );
void FadeOut( float fElapsedTime );
void SetFadeIn() { m_bFadeOut = false; }
void SetFadeOut() { m_bFadeOut = true; }
virtual bool OnMouseMove( float fX, float fY );
public:
void CreateTestUIControl();
bool Load_01( CStream &Stream, SUIFileHeader &fileheader );
bool Load( CStream &Stream );
bool Save( CStream &Stream );
const char *GetDialogFileName() { return m_strDialogFileName.c_str(); }
const char *GetDialogUITextureFileName() { return m_DlgInfo.szUITexturename; }
void SetDialogID( int nID ) { m_nDialogID = nID; }
int GetDialogID() { return m_nDialogID; }
virtual void Show( bool bShow );
virtual bool IsShow() const { return m_bShow; }
void SetAcceptInputMsgWhenHide( bool bAcceptMsg ) { m_bAcceptInputMsgWhenHide = bAcceptMsg; };
HWND GetHWnd() { return m_hWnd; }
void SetHWnd( HWND hWnd ) { m_hWnd = hWnd; }
void SetCallback( CEtUICallback *pCallback ) { m_pCallback = pCallback; }
CEtUICallback *GetCallBack() { return m_pCallback; }
void SetDlgInfo( SUIDialogInfo &DlgInfo );
void GetDlgInfo( SUIDialogInfo &DlgInfo );
void ReloadDlgTexture();
void DeleteDlgTexture();
// Note : 게임의 기본 디폴트 해상도는 1024x768.
// Border는 현재 스크린의 크기에서 디폴트 스크린의 크기를 뺀 부분의 사이즈입니다.
// Ratio는 디폴트 크기와 현재 스크린의 크기의 비율입니다.
//
float GetScreenWidth() { return m_fScreenWidth; }
float GetScreenWidthBorderSize() { return m_fScreenWidthBorder; }
float GetScreenWidthRatio() { return m_fScreenWidthRatio; }
float GetScreenHeight() { return m_fScreenHeight; }
float GetScreenHeightBorderSize() { return m_fScreenHeightBorder; }
float GetScreenHeightRatio() { return m_fScreenHeightRatio; }
//void GetScreenSize( float &fWidth, float &fHeight );
float GetXCoord();
float GetYCoord();
virtual void MoveDialog( float fX, float fY );
void SetAllignType( UIAllignHoriType Hori, UIAllignVertType Vert ) { m_DlgInfo.AllignHori = Hori; m_DlgInfo.AllignVert = Vert; }
void GetAllignType( UIAllignHoriType &Hori, UIAllignVertType &Vert ) { Hori = m_DlgInfo.AllignHori; Vert = m_DlgInfo.AllignVert; }
virtual void SetDlgCoord( const SUICoord &DlgCoord ) { m_DlgInfo.DlgCoord = DlgCoord; UpdateRects(); }
void GetDlgCoord( SUICoord &Coord ) { Coord = m_DlgInfo.DlgCoord; }
const SUICoord& GetDlgCoord() const { return m_DlgInfo.DlgCoord; }
SUICoord DlgCoordToScreenCoord( const SUICoord &Coord );
SUICoord ScreenCoordToDlgCoord( SUICoord &Coord );
float Width() { return m_DlgInfo.DlgCoord.fWidth; }
float Height() { return m_DlgInfo.DlgCoord.fHeight; }
void GetPosition( float &fX, float &fY );
void SetPosition( float fX, float fY );
CEtUIControl *GetControlAtPoint( float fX, float fY );
int GetTemplateCount() { return ( int )m_vecUITemplate.size(); }
void SetTemplate( int nIndex, CEtUITemplate &Template );
int AddTemplate( CEtUITemplate &Template );
void DeleteTemplate( int nIndex );
void DeleteAllTemplate();
CEtUITemplate *GetTemplate( int nIndex );
float GetFontHeight( int &nFontSetIndex, int nFontHeight );
//ID3DXFont *GetFont( int &nFontSetIndex, int nFontHeight );
const wchar_t* GetUIString( int nCategoryID, int nIndex );
void PointToFloat( POINT pt, float &fX, float &fY );
void SetUITextureName( const char *pszTextureName ) { strcpy_s( m_DlgInfo.szUITexturename, _MAX_PATH, pszTextureName ); }
EtTextureHandle GetUITexture() { return m_hTexture; }
void SetUITexture( EtTextureHandle hTexture ) { SAFE_RELEASE_SPTR( m_hTexture ); m_hTexture = hTexture; }
// Note : 같은 클래스 타입의 컨트롤 리스트를 반환한다.
// ex) std::vector<CButton*> vector
//
template < typename T > void GetGontrolList( std::vector< T* > &vecControl );
std::vector<CEtUIControl*>& GetControlList() { return m_vecControl; }
int GetControlCount() { return ( int )m_vecControl.size(); }
CEtUIControl *GetControl( const char *pszControlName );
template < typename T >
T* _GC( const char *pszControlName );
template < typename T >
T* GetControl( const char *pszControlName, bool *pExistControl = NULL );
//#if defined(_DEBUG) || defined(_RDEBUG)
static bool s_bUITest;
static std::map<std::string, int> s_mapUITest;
//#endif
CEtUIControl *GetControl( int nIndex ) { return m_vecControl[ nIndex ]; }
void SetControl( int nIndex, CEtUIControl *pControl ) { m_vecControl[ nIndex ] = pControl; }
CEtUIControl *CreateControl( UI_CONTROL_TYPE Type );
void DeleteControl( CEtUIControl *pControl );
void DeleteAllControl();
virtual bool FindControl( std::vector< CEtUIControl* > &vecControl, int nTypeCount, UI_CONTROL_TYPE *pType, bool bCheckCoveredControl, std::vector<SUICoord> &vecDlgCoord );
void MoveToHead( CEtUIControl *pControl );
void MoveToTail( CEtUIControl *pControl );
//void DeleteAllFont();
bool IsUsableFocusControl();
int FindControlIndex( CEtUIControl *pControl );
CEtUIControl *GetNextControl( CEtUIControl *pControl );
CEtUIControl *GetPrevControl( CEtUIControl *pControl );
bool OnCycleFocus( bool bForward );
void RequestFocus( CEtUIControl* pControl );
void PushFocusControl();
void PopFocusControl();
#ifdef PRE_FIX_COMBOBOX_ELLIPSIS
bool MakeStringWithEllipsis(SUIElement* pElement, float lineWidth, std::wstring& text, const std::wstring& symbol);
#else
void MakeStringWithEllipsis(SUIElement* pElement, float lineWidth, std::wstring& text, const std::wstring& symbol);
#endif
// Note : 아래 함수들은 UITool에서 쓰인다.
//
void ShowAllControl( bool bShow );
void EnableAllControl( bool bEnable );
void UpdateAllControl();
void ClearRadioButtonGroup( int nButtonGroup );
void DrawDlgFrame();
void CalcTextRect( LPCWSTR strText, SUIElement *pElement, SUICoord &Coord, int nCount = -1, DWORD dwFontFormat = 0xffffffff );
void CalcTextRect( LPCWSTR szText, int fontIndex, int fontHeight, SUICoord &CalcCoord, DWORD dwFontFormat, bool bUseCache );
void DrawDlgText( LPCWSTR szText, SUIElement *pElement, DWORD dwFontColor, const SUICoord &Coord, int nCount = -1, DWORD dwFontFormat = 0xffffffff, bool bClip = false, float ZValue = 0.f, DWORD dwBgColor = 0, int nBorderFlag = 0 );
void DrawSprite( EtTextureHandle hTexture, SUICoord &UVCoord, DWORD dwUIColor, SUICoord &Coord, float fRotate = 0.0f, float ZValue = 0.f );
void DrawSprite( SUICoord &UVCoord, DWORD dwUIColor, SUICoord &Coord, float fRotate = 0.0f, float ZValue = 0.f );
void DrawRect( SUICoord &Coord, DWORD dwColor );
public:
virtual void Initialize( const char *pFileName, bool bShow );
virtual void Initialize( bool bShow );
virtual void InitialUpdate() {}
virtual CEtUIControl *CreateControl( SUIControlProperty *pProperty );
virtual bool MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
virtual void ProcessCommand( UINT nCommand, bool bTriggeredByUser, CEtUIControl *pControl, UINT uMsg = 0 );
virtual void Render( float fElapsedTime );
virtual void Process( float fElapsedTime );
virtual void UpdateDlgCoord( float fX, float fY, float fWidth, float fHeight );
virtual void UpdateRects();
virtual void OnCloseCompletely() {}
// 스킬트리에서 사용함. 형제 다이얼로그의 포인터를 리턴.
// content 다이얼로그가 스킬트리처럼 겹쳐있으면 입력 메시지가 작은 다이얼로그에 오지 않기 때문에
// CEtUIDialog::ShowChildDialog() 함수 호출 시에 체크해서 포커싱 처리시에 가장 뒤에 오도록 셋팅합니다.
virtual void GetContentDialog( /*IN OUT*/ std::vector<CEtUIDialog*>& vlpContentDialogs ) {};
virtual void OnChangeResolution();
static void ProcessChangeResolution();
virtual void OnEndInitialize();
virtual void OnHandleMsg(int protocol, char* pData) {}
};
template < typename T >
void CEtUIDialog::GetGontrolList( std::vector< T* > &vecControl )
{
for( int i=0; i<(int)m_vecControl.size(); i++ )
{
if( m_vecControl[i] )
{
if( typeid(*m_vecControl[i]) == typeid( T ) )
{
vecControl.push_back( (T*)m_vecControl[i] );
}
}
}
}
#define CONTROL( cls, name ) _GC<CEtUI##cls>(#name)
#define DN_CONTROL( cls, name ) _GC<CDn##cls>(#name)
// 최적화 버전
template < typename T >
T* CEtUIDialog::_GC( const char *pszControlName )
{
std::map< std::string, CEtUIDialog*>::iterator it = m_cachedControls.find(pszControlName);
if( it != m_cachedControls.end() ) {
return (T*)it->second;
}
bool bExistControl = false;
T *pResult = GetControl<T>( pszControlName, &bExistControl);
if( bExistControl ) { // 존재하지 않는 경우엔 계속 경고메세지가 떠서 알 수 있도록 한다..
m_cachedControls[ pszControlName ] = (CEtUIDialog*)pResult;
}
else {
static bool bFirst = true;
if( bFirst ) {
ASSERT( false );
bFirst = false;
}
}
return pResult;
}
template < typename T >
T* CEtUIDialog::GetControl( const char *pszControlName, bool *pExistControl )
{
for( int i = 0; i < ( int )m_vecControl.size(); i++ )
{
if( strcmp( pszControlName, m_vecControl[ i ]->GetControlName() ) == 0 )
{
#if defined(_DEBUG) || defined(_RDEBUG) // 혹시 모를 실수에 대비해 디버깅 시에만 타입체크를 해준다.
T* pResult = dynamic_cast<T*>(m_vecControl[ i ]);
if( !pResult ) {
ASSERT( 0 && "UI Control 캐스팅 실패!!");
T* pTempControl = new T( 0 );
m_TempControlList.push_back( pTempControl );
if( pExistControl ) *pExistControl = false;
return pTempControl;
}
#else
T* pResult = static_cast<T*>(m_vecControl[ i ]);
#endif
if( pExistControl ) *pExistControl = true;
return pResult;
}
}
//#if defined(_DEBUG) || defined(_RDEBUG)
//assert( 0 && "UI Control 을 찾을 수 없습니다! 자세한 정보는 EtUIDialog.h의 374라인으로");
char szFileName[256];
_GetFullFileName(szFileName, _countof(szFileName), m_strDialogFileName.c_str());
CDebugSet::ToLogFile(" [UI Error] %s \"%s\" 未找到!\n", szFileName, pszControlName);
//#endif
// 파일에 출력할때는 한번 출력한거 건너뛰게 하기 위해, string 맵에 넣어두고 없을때만 출력.
if( s_bUITest )
{
std::string szKey = szFileName;
szKey += "_";
szKey += pszControlName;
std::map<std::string, int>::iterator it = s_mapUITest.find( szKey );
if( it == s_mapUITest.end() )
{
FILE *fp = NULL;
fopen_s( &fp, "uitest.txt", "at" );
fprintf_s( fp, "[UI Error] \"%s\" 파일의 %s Control 을 찾을 수 없습니다!\n", szFileName, pszControlName );
fclose( fp );
s_mapUITest.insert( make_pair( szKey, 1 ) );
}
}
// 컨트롤이 없어도 뻑은 나지 않는다. 대신 동작은 제대로 하지 않음..
for( DWORD i=0; i<m_TempControlList.size(); i++ ) {
if( strcmp( m_TempControlList[i]->GetControlName(), pszControlName ) == NULL ) return static_cast<T*>(m_TempControlList[i]);
}
T* pTempControl = new T( 0 );
pTempControl->SetParent( this );
if( pszControlName ) _strcpy( pTempControl->GetProperty()->szUIName, _countof(pTempControl->GetProperty()->szUIName), pszControlName, (int)strlen(pszControlName) );
m_TempControlList.push_back( pTempControl );
if( pExistControl ) *pExistControl = false;
return pTempControl;
}