Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't type Korean in the text box. #7979

Open
Tadminster opened this issue Sep 13, 2024 · 8 comments
Open

Can't type Korean in the text box. #7979

Tadminster opened this issue Sep 13, 2024 · 8 comments

Comments

@Tadminster
Copy link

Tadminster commented Sep 13, 2024

Version/Branch of Dear ImGui:

Version 1.91.0, Branch: docking

Back-ends:

imgui_impl_win32.cpp + imgui_impl_dx11.cpp

Compiler, OS:

Windows 11 + MSVC 2022 + VS 2022 Preview

Full config/build information:

Dear ImGui 1.91.2 WIP (19114)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1942
define: _MSVC_LANG=201402
--------------------------------
io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx11
io.ConfigFlags: 0x00000003
 NavEnableKeyboard
 NavEnableGamepad
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x0000000E
 HasMouseCursors
 HasSetMousePos
 RendererHasVtxOffset
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1264.00,761.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

My Issue/Question:

I added a Korean font (malgun.ttf) that comes with Windows to use Korean in ImGui.

The result is

  1. ImGui now outputs Hangul correctly.
  2. you can't enter Hangul in filters or text boxes.
  3. if I write Hangul in another window, copy it, and paste it, it looks fine.

I have searched a lot in the last two days to solve this problem, and tried various things,
but I couldn't solve the problem.

What I've tried

  1. Try a different font that supports Korean (google NotoSansKR etc..)
  2. WM_IME_CHAR support in WndProc function (JX-Master's answer from Problem with chinese or japanese input #1807)
  3. Try adding #pragma execution_character_set(“utf-8”) to my code (https://github.com/ocornut/imgui/blob/master/docs/FONTS.md#loading-font-data-from-memory)
  4. Change the way I type on my computer (Change IME)

Screenshots/Video:

ImGui.mp4

Minimal, Complete and Verifiable Example code:

My code is still in its early stages, and I've changed very little from the examples provided by ImGui.

//////////////////////////////////////////////////////////////////////////////
ImGuiManager.cpp
//////////////////////////////////////////////////////////////////////////////
#include "ImGuiManager.h"

void ImGuiManager::Init()
{
// ImGui Init
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();

io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // 키보드 입력 활성화
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // 게임패드 입력 활성화
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // 도킹 활성화
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // 다중 뷰포트 활성화
io.ConfigViewportsNoAutoMerge = true;
io.ConfigViewportsNoDefaultParent = true;
io.ConfigDockingTransparentPayload = true;

//Korean font settings
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\malgun.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesKorean());

//
SetupStyle();

}

void ImGuiManager::SetupImGuiContext(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* deviceContext)
{
// Initialize the platform/renderer backend
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX11_Init(device, deviceContext);
}

bool ImGuiManager::IsDone() const
{
return !isWindowOpen;
}

void ImGuiManager::Release()
{
// ImGui Shutdonw and Release Resources
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}

void ImGuiManager::Update()
{
// Start a new frame
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();

// Set the size of the window
// ImGuiCond_FirstUseEver는 창이 처음 생성될 때만 크기를 설정하도록 하는 옵션
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);


static Searcher log;
// Create an ImGui window
ImGui::Begin("ImGui Window", &isWindowOpen);
{
    
    if (ImGui::SmallButton("[Debug] Add 5 entries"))
    {
        static int counter = 0;
        const char* categories[3] = { "카테", "고리", "에요" };
        const char* words[] = { "너구리", "오징어"};
        for (int n = 0; n < 5; n++)
        {
            const char* category = categories[counter % IM_ARRAYSIZE(categories)];
            const char* word = words[counter % IM_ARRAYSIZE(words)];
            log.AddLog("[%s] word: '%s'\n",
               category, word);
            counter++;
        }
    }
}
ImGui::End();

log.Draw(u8"ImGui Window", &isWindowOpen);

}

void ImGuiManager::LateUpdate()
{
// Use when additional updates are required
}

void ImGuiManager::Render()
{
// ImGui Rendering Handling
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

// Render additional platform windows
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
    ImGui::UpdatePlatformWindows();
    ImGui::RenderPlatformWindowsDefault();
}

}

void ImGuiManager::ResizeScreen()
{
// Handle actions required by screen size changes
}

void ImGuiManager::SetupStyle()
{
// Setting up styles
ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();

if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
    style.WindowRounding = 0.0f;
    style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}

}

//////////////////////////////////////////////////////////////////////////////
ImGuiManager.h
//////////////////////////////////////////////////////////////////////////////
#pragma once

#include <imgui.h>
#include <imgui_impl_win32.h>
#include <imgui_impl_dx11.h>
#include <d3d11.h>
#include "Types.h"

class ImGuiManager : public Singleton ,public Scene
{
public:
void Init() override;
void Release() override;
void Update() override;
void LateUpdate() override;
void Render() override;
void ResizeScreen() override;

public:
// Additional ImGui initialization functions (can be called directly)
void SetupImGuiContext(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* deviceContext);

private:
bool isWindowOpen{ true };

public:
bool IsDone() const;

private:
void SetupStyle();
};

struct Searcher
{
ImGuiTextBuffer Buf;
ImGuiTextFilter Filter;
ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
bool AutoScroll; // Keep scrolling if already at the bottom.

Searcher()
{
    AutoScroll = true;
    Clear();
}

void    Clear()
{
    Buf.clear();
    LineOffsets.clear();
    LineOffsets.push_back(0);
}

void    AddLog(const char* fmt, ...) IM_FMTARGS(2)
{
    int old_size = Buf.size();
    va_list args;
    va_start(args, fmt);
    Buf.appendfv(fmt, args);
    va_end(args);
    for (int new_size = Buf.size(); old_size < new_size; old_size++)
        if (Buf[old_size] == '\n')
            LineOffsets.push_back(old_size + 1);
}

void    Draw(const char* title, bool* p_open = NULL)
{
    if (!ImGui::Begin(title, p_open))
    {
        ImGui::End();
        return;
    }

    // Options menu
    if (ImGui::BeginPopup("Options"))
    {
        ImGui::Checkbox("Auto-scroll", &AutoScroll);
        ImGui::EndPopup();
    }

    // Main window
    if (ImGui::Button("Options"))
        ImGui::OpenPopup("Options");
    ImGui::SameLine();
    bool clear = ImGui::Button("Clear");
    ImGui::SameLine();
    bool copy = ImGui::Button("복사");
    ImGui::SameLine();

    Filter.Draw("검색어", -100.0f);

    //ImGui::InputText 를 사용할 부분
    static char inputBuffer[256] = "한글";  // A buffer to store the entered text
    ImGui::InputText("아무말이나 입력", inputBuffer, IM_ARRAYSIZE(inputBuffer));



    // Output the entered text (for testing)
    ImGui::Text("입력된 텍스트: %s", inputBuffer);

    ImGui::Separator();

    if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))
    {
        if (clear)
            Clear();
        if (copy)
        {
            const char* text = "한글 테스트";
            ImGui::SetClipboardText(text);
        }

        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
        const char* buf = Buf.begin();
        const char* buf_end = Buf.end();
        if (Filter.IsActive())
        {
            for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
            {
                const char* line_start = buf + LineOffsets[line_no];
                const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
                if (Filter.PassFilter(line_start, line_end))
                    ImGui::TextUnformatted(line_start, line_end);
            }
        }
        else
        {
            ImGuiListClipper clipper;
            clipper.Begin(LineOffsets.Size);
            while (clipper.Step())
            {
                for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
                {
                    const char* line_start = buf + LineOffsets[line_no];
                    const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
                    ImGui::TextUnformatted(line_start, line_end);
                }
            }
            clipper.End();
        }
        ImGui::PopStyleVar();

        // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
        // Using a scrollbar or mouse-wheel will take away from the bottom edge.
        if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
            ImGui::SetScrollHereY(1.0f);
    }
    ImGui::EndChild();
    ImGui::End();
}

};

@ocornut
Copy link
Owner

ocornut commented Sep 16, 2024

  • Use Tools->Debug Log->IO to visualize calls to io.AddInputCharacter() and verify if there are correct.
  • How do you create your win32 window? Make sure to use CreateWindowW() and not CreateWindowA(), or configure project Character Set to use Unicode Character set.

I would be interested in your precise feedback, I wonder if this has something to do with #3653.

@Tadminster
Copy link
Author

Tadminster commented Sep 17, 2024

  1. In Tools->Debug Log->IO, when I try to type Korean, it is not displayed correctly.
    (I've attached a video to help you understand.)
ImGuiKoreanTest.mp4
  1. The win32 window was created as follows:
    HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("ImGui Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr);

In WinUser.h, defined as follows:
#define CreateWindow CreateWindowW

So I think of CreateWindowW as creating a win32 window.

  1. Upon checking, in Project Properties -> Configuration Properties/Advanced -> Character Sets: 'Use Unicode character set'

=============Below is my WinMain code.

`// Main code
//int main(int, char**)
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, _T("AppClass"), nullptr };
::RegisterClassEx(&wc);
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("ImGui Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr);

if (!CreateDeviceD3D(hwnd))
{
CleanupDeviceD3D();
::UnregisterClass(wc.lpszClassName, wc.hInstance);
return 1;
}

::ShowWindow(hwnd, SW_HIDE);
::UpdateWindow(hwnd);

// ImGuiManager 싱글턴 인스턴스 초기화
ImGuiManager::GetInstance()->Init();
ImGuiManager::GetInstance()->SetupImGuiContext(hwnd, g_pd3dDevice, g_pd3dDeviceContext);

bool done = false;
while (!done)
{
MSG msg;
while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
if (msg.message == WM_QUIT)
done = true;
}

if (done || ImGuiManager::GetInstance()->IsDone())
    break;

if (g_ResizeWidth != 0 && g_ResizeHeight != 0)
{
    CleanupRenderTarget();
    g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, 0);
    g_ResizeWidth = g_ResizeHeight = 0;
    CreateRenderTarget();
}

// ImGuiManager의 Update와 Render 호출
ImGuiManager::GetInstance()->Update();
ImGuiManager::GetInstance()->Render();

const float clear_color[4] = { 0.45f, 0.55f, 0.60f, 1.0f };
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color);

g_pSwapChain->Present(1, 0);

}

ImGuiManager::GetInstance()->Release();
CleanupDeviceD3D();
::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance);

return 0;
}`

@ocornut
Copy link
Owner

ocornut commented Sep 17, 2024

(1) Can you compile example_win32_directx11 and test on it?
(You don't need to load Korean font, just look at Debug Log->IO if the value are correct)

(2) Does this makes any difference?
https://github.com/ocornut/imgui/pull/3653/files

@Tadminster
Copy link
Author

Tadminster commented Sep 18, 2024

  1. I compiled and tested example_win32_directx11, and it works fine.

Initially, example_win32_directx11 was also unable to type Korean.
I thought it might be a problem with my computer's IME, so I reformatted it.
(I was suspicious because I had previously developed a game with Unreal Engine and had tinkered with the IME settings for Korean input issues.)
After that, example_win32_directx11 was able to type Korean well.

But in my project, I still can't type Korean.
I'm investigating why this is happening with my project.
I'll comment back when I figure it out for sure.

  1. https://github.com/ocornut/imgui/pull/3653/files
    The second way, it didn't make any difference.
    Thanks for the different ways to do it.

@Tadminster
Copy link
Author

Tadminster commented Sep 18, 2024

Except for a few settings, my project is almost identical to example_win32_directx11.
However, it’s strange that Korean input doesn't work.

Here are a few other differences:

  1. I'm using ImGui through vcpkg.
  2. I changed the entry point from int main(int, char**) to int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow).
  3. I changed the encoding of the cpp file to Unicode (UTF-8 without BOM) - code page 65001.
  4. I enabled some additional options in the ImGui configuration context.

So, I tested it by removing the differences one by one.
At some point, I discovered the exact point where Korean input stopped working.

When I set io.ConfigViewportsNoAutoMerge to true in the ImGui configuration context, Korean input stops working.
Does this setting affect Korean input?

Please refer to the video below.

ImGuiKoreanInputTest.mp4

@ocornut
Copy link
Owner

ocornut commented Sep 18, 2024

This will make every imgui window use a platform (Windows) window created by the backend.

Presumably, even with the flag disabled, if you move a window out of the main viewport boundary, then you should have the same problem?

Either way it means looking at the _CreateWindow impl + the associated WndProc handler in second half of imgui_impl_win32 and trying to understand what is wrong and how it would differs how the main window was created.

@Tadminster
Copy link
Author

Tadminster commented Sep 20, 2024

Presumably, even with the flag disabled, if you move a window out of the main viewport boundary, then you should have the same problem?

Yes, even with the flag disabled, the same problem occurs when I move the window outside the main viewport boundary.
I looked into the issue further, but in the end, I wasn't able to solve the problem..

@ocornut
Copy link
Owner

ocornut commented Oct 3, 2024

I believe the problem is that the vcpkg version may not compiled not for Unicode but for multi-byte mode and our code in ImGui_ImplWin32_CreateWindow() use the default:

    vd->Hwnd = ::CreateWindowEx(
        vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle,       // Style, class name, window name
        rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,    // Window area
        vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr);          // Owner window, Menu, Instance, Param

I have pushed a tentative fix: 3293ef8
This is similar to #5725.

Because you are using backend precompiled by vcpkg I believe you cannot test this now and need to wait for a vcpkg update?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants