Bind Second Hot Bar 4ever;

JamePOWER1

Active Pirate
Registered
LV
0
 
Joined
May 2, 2026
Messages
34
Reaction score
8
Points
28
Age
32
Location
Seattle
I don't know about you guys, but the second hotbar not working right off the bat when logging into the client really bugged me. I decided to open this can of worms and try to make it feel much smoother to use (at least for my own QoL).

Here is how it works now:

CTRL + T behavior is enabled by default.
Chat is disabled by default (you must press ENTER first to start typing).
Pressing ENTER on confirmation screens (NPCs, trades, banking) still works perfectly.
You no longer need to hold SHIFT (without having toggled CTRL + T) to use the bind second hot Bar slots.

Fixing one thing kept breaking another, but it's finally fully functional. I'll keep tweaking this when I have some free time because I know the code can be made a lot more efficient. Any tips, code reviews, or feedback are highly appreciated!

on ~\UIEquipForm.cpp find and change to true
C++:
bool g_IsNumberTopBar{ true };

on function ~\CEquipMgr::ExecFastKey remove it
C++:
if(key == VK_RETURN && g_IsNumberTopBar && !g_FKeysInUse && !CCozeForm::GetInstance()->IsChatBoxActive())
    CCozeForm::GetInstance()->ActivateChatBox();

and instead of that detour above, leave it like this
C++:
if (key == VK_RETURN && g_IsNumberTopBar && !g_FKeysInUse && !CCozeForm::GetInstance()->IsChatBoxActive())
{
    //if (CCompent::GetActive() == nullptr)
    CCompent* pActive = CCompent::GetActive();
    CEdit* pOtherEdit = dynamic_cast<CEdit*>(pActive);
    bool bOtherEditActive = (pOtherEdit != nullptr) &&
        (pActive != CCozeForm::GetInstance()->GetEdtMsg());
    if (!bOtherEditActive)
        CCozeForm::GetInstance()->ActivateChatBox();
}
if (g_IsNumberTopBar && !g_FKeysInUse && !g_pGameApp->IsCtrlPress() && key != 8 && !CCozeForm::GetInstance()->IsChatBoxActive())
{
    auto numberID = key - '0';
    if (numberID >= 0 && numberID <= 9)
    {
        if (numberID == 0)
        {
            numberID = 10;
        }
        numberID -= 1;
        key = numberID;

        if (CCozeForm::GetInstance()->IsChatBoxActive() ||
            CCozeForm::GetInstance()->GetChatUserActivated())
        {
            CCozeForm::GetInstance()->DisableChatBox();
        }
        g_InputBox.ClearText();
        if (CCozeForm::GetInstance()->GetEdtMsg())
            CCozeForm::GetInstance()->GetEdtMsg()->SetCaption("");
    }
}

on ~\UICozeForm.h class ~\CCozeForm add the public methods below
C++:
 CEdit* GetEdtMsg() const { return m_edtMsg; }
  bool GetChatUserActivated() const { return m_bChatUserActivated; }
  bool IsChatEditActiveRaw() const;
  void ResetChatState();

event added protected
C++:
static bool EventEditMsgChar(CGuiData* pSender, char& key);

private member added
C++:
mutable bool m_bChatUserActivated = false;

signature added in public
C++:
void ResetChatState();

on ~\UICozeForm.cpp top of file add this
C++:
bool g_bChatUserActivated = false;

bool g_IsChatEditActive()
{
    auto* pCoze = GUI::CCozeForm::GetInstance();
    if (!pCoze) return false;
    return pCoze->IsChatEditActiveRaw();
}

same file bool CCozeForm::Init()
C++:
//find this
m_edtMsg->evtKeyDown=EventEditMsg;

//add right below
m_edtMsg->evtKeyChar = EventEditMsgChar;
m_edtMsg->SetPlaceholder("Press ENTER to use chat");

extern bool g_IsNumberTopBar;
if (g_IsNumberTopBar)
    m_edtMsg->SetIsFocus(false);

search for void CCozeForm::EventSendMsg
C++:
void CCozeForm::EventSendMsg(CGuiData *pSender)
{
    CCozeForm::GetInstance()->SendMsg();
    extern bool g_IsNumberTopBar;
    if (g_IsNumberTopBar)
    {
        CCozeForm::GetInstance()->m_bChatUserActivated = false;
        //g_bChatUserActivated = false;
        CCompent::SetActive(nullptr);
    }
}

replace it with this
C++:
void CCozeForm::EventSendMsg(CGuiData* pSender)
{
    extern bool g_IsNumberTopBar;
    if (g_IsNumberTopBar && !CCozeForm::GetInstance()->m_bChatUserActivated)
    {
        g_InputBox.ClearText();
        if (CCozeForm::GetInstance()->m_edtMsg)
            CCozeForm::GetInstance()->m_edtMsg->SetCaption("");
        CCompent::SetActive(nullptr);
        return;
    }
    CCozeForm::GetInstance()->SendMsg();
    if (g_IsNumberTopBar)
    {
        CCozeForm::GetInstance()->m_bChatUserActivated = false;
        g_bChatUserActivated = false;
        CCompent::SetActive(nullptr);
    }
}

on bool CCozeForm::EventEditMsg
C++:
//add at the beginning
extern bool g_IsNumberTopBar;
if (g_IsNumberTopBar && !CCozeForm::GetInstance()->m_bChatUserActivated)
    return false;

just below add this function
C++:
bool CCozeForm::EventEditMsgChar(CGuiData* pSender, char& key)
{
        extern bool g_IsNumberTopBar;
    if (g_IsNumberTopBar && !CCozeForm::GetInstance()->m_bChatUserActivated)
        return false;
    return true;
}

on bool GUI::CCozeForm::IsChatBoxActive() change the entire function to this
C++:
bool GUI::CCozeForm::IsChatBoxActive() const
{
    extern bool g_IsNumberTopBar;
    if (m_edtMsg && m_edtMsg == CCompent::GetActive())
    {
        if (g_IsNumberTopBar && !m_bChatUserActivated)
            return false;
        return true;
    }
    return false;
}

just below add this
C++:
bool GUI::CCozeForm::IsChatEditActiveRaw() const
{
    return m_edtMsg && m_edtMsg == CCompent::GetActive();
}

find void CCozeForm::ActivateChatBox()
C++:
//leave it like that
void GUI::CCozeForm::ActivateChatBox()
{
    if (m_edtMsg) m_edtMsg->SetIsFocus(true);
    m_bChatUserActivated = true;
    g_bChatUserActivated = true;
    CCompent::SetActive(m_edtMsg);
}

on void GUI::CCozeForm::DisableChatBox()
C++:
void GUI::CCozeForm::DisableChatBox()
{
    m_bChatUserActivated = false;
    g_bChatUserActivated = false;
    if (m_edtMsg)
    {
        m_edtMsg->SetIsFocus(false);
        m_edtMsg->SetCaption("");   
    }
    if (m_edtMsg && m_edtMsg == CCompent::GetActive())
        CCompent::SetActive(nullptr);
}

at the end of the file, add this function
C++:
void GUI::CCozeForm::ResetChatState()
{
    m_bChatUserActivated = false;
    g_bChatUserActivated = false;
    if (m_edtMsg)
    {
        m_edtMsg->SetIsFocus(false);
        m_edtMsg->SetCaption("");
    }
    CCompent::SetActive(nullptr);
}

on ~\WorldScene.cpp find CWorldScene::_Init()
C++:
CFormMgr::s_Mgr.SetEnabled( true );

//right below that, add the following

extern bool g_IsNumberTopBar;
if (g_IsNumberTopBar)
{
    CCozeForm::GetInstance()->ResetChatState();
}

on BOOL CWorldScene::_InitUI()
C++:
//leave the beginning like this
CForm* form = CFormMgr::s_Mgr.Find("frmMainChat");
if (form)
{
    CEdit* edit = dynamic_cast<CEdit*>(form->Find("edtSay"));
    if (edit)
    {
        edit->SetIsParseText(true);
        extern bool g_IsNumberTopBar;
        if (g_IsNumberTopBar)
            edit->SetIsFocus(false);
    }
    form->Show();
}

//the rest remains intact
...form = CFormMgr::s_Mgr.Find( "frmMain800" );
if( form )
{
    form->Show();
}...

at the end of BOOL CWorldScene::_InitUI()
C++:
    //after this part
    g_pGameApp->GetCursor()->SetIsVisible( TRUE );
    
    //leave it like that
    extern bool g_IsNumberTopBar;
    if (g_IsNumberTopBar)
        CCozeForm::GetInstance()->ResetChatState();
    CCozeForm::GetInstance()->OnSystemMsg("Press ENTER to use chat");
    return TRUE;
}

be very CAREFUL now
on ~\Main.cpp (client of course)
find LRESULT APIENTRY EditSubclassProc on this switch [case WM_CHAR:] leave it that way:
C++:
case WM_CHAR:
    {
    extern bool g_IsNumberTopBar;
    extern bool g_bChatUserActivated;
    extern bool g_IsChatEditActive();
    if (g_IsNumberTopBar && !g_bChatUserActivated
        && wParam != VK_RETURN
        && wParam != VK_BACK
        && g_pGameApp && g_pGameApp->GetCurScene()
        && g_pGameApp->GetCurScene()->GetSceneTypeID() == enumWorldScene
        && g_IsChatEditActive())
        {
        WndProc(hwnd, uMsg, wParam, lParam);
        return 0;
        }
    }
    WndProc( hwnd, uMsg, wParam, lParam);
    if (GetKeyState(VK_CONTROL) & 0xff00)
    {
        return 0;
    }
    switch (wParam)
    {
    case VK_ESCAPE:
    case VK_RETURN:
    case VK_TAB:
        return 0;
    }
    break;

on ~\UIEdit.h find void SetCaption( const char * str ); add below
C++:
void   SetPlaceholder(const char* str) { _strPlaceholder = str ? str : ""; }
const char* GetPlaceholder() const { return _strPlaceholder.c_str(); }

find
C++:
protected:
    std::string        _str;
    std::string     _strVisible;

//add below
    std::string     _strPlaceholder;

on ~\UIEdit.cpp find CEdit::Render()
C++:
//Add this before the "GetRender().Reset();" at the end;
extern bool g_bChatUserActivated;
if (_str.empty() && !g_bChatUserActivated && !_strPlaceholder.empty())
{
    CGuiFont::s_Font.Render(
        (char*)_strPlaceholder.c_str(),
        GetX(), GetY(),
        0xFF888888
    );
}
 

Attachments

  • edtSayNewOne.webp
    edtSayNewOne.webp
    5 KB · Views: 23
  • Like
Reactions: ZkaRu and ximboliex
Hi Great Post!

I've got a small improvement, it's a personal feeling, but for anyone who feels the same as me, here it is.
So that pressing Enter doesn't disable Chat, only enable it when you manually press Ctrl+T.

Replace the void function CCozeForm::EventSendMsg with this
C++:
void CCozeForm::EventSendMsg(CGuiData* pSender)
{
    if (g_IsNumberTopBar && !CCozeForm::GetInstance()->m_bChatUserActivated)
    {
        g_InputBox.ClearText();
        if (CCozeForm::GetInstance()->m_edtMsg)
            CCozeForm::GetInstance()->m_edtMsg->SetCaption("");
        CCompent::SetActive(nullptr);
        return;
    }

    
    if (CCozeForm::GetInstance()->m_edtMsg)
    {
        const char* szText = CCozeForm::GetInstance()->m_edtMsg->GetCaption();
        if (!szText || szText[0] == '\0')
        {
            return;
        }
    }

    CCozeForm::GetInstance()->SendMsg();

    if (g_IsNumberTopBar)
    {
        CCozeForm::GetInstance()->m_bChatUserActivated = false;
        g_bChatUserActivated = false;
        CCompent::SetActive(nullptr);
    }
}