본문 바로가기

Win32 api

Win32 api 강의 52화.

- UI 포커스에 관한 구현.

간단한 기능인데, 생각보다 고려할 게 많았다.

// CUIMgr.h
...
class CUIMgr
{
    SINGLETON(CUIMgr)
private:
    CUI*        m_pFocusParentUI;       // 현재 포커스된 parent UI.
    CUI*        m_pSelectedParentUI;    // 현재 선택된(클릭된) parent UI.

public:
    void SetSelectedParentUI(CUI* pUI);

private:
    void UIMouseCheck(CUI* pUI);
    CUI* FindFocusedChildUI(CUI* pUI);
    CUI* FindFocusedParentUI();
    ...
};

 

먼저 focus 된 것과 select 된 것을 분리해서 기록하기로 함.

그리고 일단 parent ui 만을 기준으로 했다. child ui 는 그냥 이후에 추가하면 될 듯?

// CUIMgr.cpp
...
void CUIMgr::late_update()
{
    // 현재 씬에서 포커스된 UI 요소의 최상위 부모 UI를 찾음.
    m_pFocusParentUI = FindFocusedParentUI();
    // 그 부모 UI로부터 포커스를 받는 자식 UI를 찾고 마우스 입력을 검사.
    // nullptr 일 때도 처리.
    if (m_pFocusParentUI) UIMouseCheck(m_pFocusParentUI->GetFocusedChildUI());
    else UIMouseCheck(nullptr);
    ...

 

우선 focus 된 부모를 찾는다. 찾는 과정에서 각 부모 ui는 최종적으로 focus 된 child 를 저장해둘 거임.

부모를 찾았다면, 여기서 최종 focus 된 child 를 GetFocusedChildUI 로 불러온다. 이걸 UIMouseCheck 에 넘겨주면?

 

최종 UI 에 대한 마우스 입력 처리를 가능하게 함.

없다면 nullptr 에 대한 처리를 하도록 해줌.

 

// CUIMgr.cpp
...
CUI* CUIMgr::FindFocusedParentUI()
{
    CScene* pCurScene = CSceneMgr::GetInstance()->GetCurScene();
    if (pCurScene == nullptr)
        return nullptr;

    const vector<CObject*>& vecUI = pCurScene->GetObjByGroup(GROUP_TYPE::UI);

    CUI* pFocusedParentUI = nullptr;

    for (CObject* pObj : vecUI)
    {
        // UI 그룹이므로 static_cast 사용.
        CUI* pParentUI = static_cast<CUI*>(pObj);
        if (pParentUI == nullptr)
            continue;

        // 자식 UI에서 포커스된 UI 요소를 찾음
        if (CUI* pFocusedChildUI = FindFocusedChildUI(pParentUI))
        {
            // 해당 parent ui 의 포커스된 자식 UI 설정.
            pParentUI->SetFocusedChildUI(pFocusedChildUI);
            // 최종 포커스된 부모 UI 갱신.
            pFocusedParentUI = pParentUI;
        }
        else
        {
            pParentUI->SetFocusedChildUI(nullptr);
        }
    }

    return pFocusedParentUI;
}

 

FindFocusedParentUI 는 단순히 vector 를 순회하면서 자식중에 포커스된 것이 있으면, 해당 parent ui 를 반환값으로 지정한다.

이렇게 하면 뒤에 있는 UI 가 우선권을 가짐.

// CUIMgr.cpp
...
CUI* CUIMgr::FindFocusedChildUI(CUI* pParentUI)
{
    if(pParentUI == nullptr)
        return nullptr;

    static queue<CUI*> qUI = {};
    // 기존 포커스 UI 에 대한 button release 처리해줘야 함.
    bool bLButtonRelease = KEYRELEASECHECK(KEY::LBUTTON);

    // 큐 초기화.
    while (!qUI.empty())
        qUI.pop();

    // 부모 UI도 focus 검사 대상에 포함.
    qUI.push(pParentUI);

    CUI* pFocusedChildUI = nullptr;

    while (!qUI.empty())
    {
        CUI* pCurrentUI = qUI.front();
        qUI.pop();

        if(pCurrentUI == nullptr)
            continue;

        // 현재 UI가 마우스 Hover or LButtonDown 상태라면, 포커스를 받는 UI로 설정
        if (pCurrentUI->IsMouseHover() || pCurrentUI->IsLButtonDown())
        {
            // 기존 포커스 UI에 대한 button release 처리
            if (pFocusedChildUI && bLButtonRelease)
            {
                if (pFocusedChildUI->IsLButtonDown())
                {
                    pFocusedChildUI->OnLButtonUp();
                }
            }
            
            // 최종 포커스 UI 갱신
            pFocusedChildUI = pCurrentUI;
        }
        // 자식 UI들을 큐에 추가
        const vector<CUI*>& vecChildUIs = pCurrentUI->GetChildUIs();
        for (CUI* pChildUI : vecChildUIs)
        {
            qUI.push(pChildUI);
        }
    }

    return pFocusedChildUI;
}

 

이전에 GetTargetUI 를 이름 바꾸고 조금 수정한 버전. 이제보니 이름만 바꾼 듯 ㅇㅅㅇ.

// CUIMgr.cpp
...
void CUIMgr::UIMouseCheck(CUI* pUI)
{
    bool bLButtonTap = KEYTAPCHECK(KEY::LBUTTON);
    bool bLButtonRelease = KEYRELEASECHECK(KEY::LBUTTON);

    // LButton 이 눌렸다면 m_pFocusParentUI 를 맨 뒤로 보냄.
    // nullptr 일 때도 처리.
    if(bLButtonTap)
    {
        SetSelectedParentUI(m_pFocusParentUI);
    }

    // 이후는 nullptr 이면 수행하지 않음.
    if(pUI == nullptr)
        return;
    ...
}

 

UIMouseCheck 에서는 기존 코드에 전처리로 몇 줄 추가함.

LButton 이 눌린 상태면, m_pFocusParentUI 를 Select 된 것으로 갱신한다. nullptr 일 때도 해줌. 

// CUIMgr.cpp
...
void CUIMgr::SetSelectedParentUI(CUI* pUI)
{
    if (pUI == nullptr)
    {
        // nullptr이 전달되면 선택된 UI를 해제.
        m_pSelectedParentUI = nullptr;
        return;
    }

    CScene* pCurScene = CSceneMgr::GetInstance()->GetCurScene();
    if (pCurScene)
    {
        // 선택된 UI를 맨 뒤로 보냄.
        pCurScene->MoveObjectToBack(pUI, GROUP_TYPE::UI);
    }
    // 선택된 UI 설정.
    m_pSelectedParentUI = pUI;
}

 

해당 함수에서는 입력된 UI 를 Selected 로 선택한다. nullptr 이 아니라면 해당 UI 는 벡터 맨 뒤로 보내서 

맨 앞 렌더링될 수 있도록 함.

'Win32 api' 카테고리의 다른 글

Win32 api 강의 54화.  (0) 2025.09.27
Win32 api 강의 53화.  (0) 2025.09.26
Win32 api 강의 51화.  (0) 2025.09.25
Win32 api 강의 49 - 50화.  (0) 2025.09.25
Win32 api 강의 48화.  (0) 2025.09.24