Giter Site home page Giter Site logo

Can not input CJK words about tgui HOT 9 CLOSED

xland avatar xland commented on August 26, 2024
Can not input CJK words

from tgui.

Comments (9)

texus avatar texus commented on August 26, 2024

If it renders these rectangles then it means that the characters aren't part of the font. TGUI uses DejaVuSans by default, which indeed doesn't support CJK.

You will need to call tgui::Font::setGlobalFont("filename.ttf") at the beginning of the program (after creating the Gui) with a font file that can display Chinese characters.

from tgui.

xland avatar xland commented on August 26, 2024

Thanks very much.
Is it necessary to set the default font to one that supports CJK?
If not, I'll close the Issue.

from tgui.

xland avatar xland commented on August 26, 2024

By the way.
The input method prompt box of the CJK does not follow the input cursor.
image

Which should look like this:
image

This is the key code:

if (HIMC himc = ImmGetContext(win->hwnd))
{
    COMPOSITIONFORM comp = {};
    comp.ptCurrentPos.x = x;
    comp.ptCurrentPos.y = y;
    comp.dwStyle = CFS_FORCE_POSITION;
    ImmSetCompositionWindow(himc, &comp);
    CANDIDATEFORM cand = {};
    cand.dwStyle = CFS_CANDIDATEPOS;
    cand.ptCurrentPos.x = x;
    cand.ptCurrentPos.y = y;
    ImmSetCandidateWindow(himc, &cand);
    ImmReleaseContext(win->hwnd, himc);
}

from tgui.

texus avatar texus commented on August 26, 2024

Is it necessary to set the default font to one that supports CJK?

The font used by the edit box needs to support CJK to display the characters. I assumed you wanted to use Chinese characters in the entire application so I suggested changing the global font so that all widgets would use that font, but you can also change the font of only the edit box like this:

tgui::Font font("path/to/simhei.ttf");
editBox->getRenderer()->setFont(font);

The input method prompt box of the CJK does not follow the input cursor.

I've never used an IMM/IME so I'm not familiar with how it should work. TGUI doesn't do anything special with IME, it doesn't even know it exists. So unless you tell the IME yourself, it has no idea where on the screen the TGUI edit box is located.

I know that SDL has a SDL_SetTextInputRect function that probably positions the IME next to the edit box, but TGUI currently only calls that function on Android and iOS to get a virtual keyboard. Maybe I could call that on desktops as well when using the SDL backend. SFML on the other hand has no such function available to my knowledge, so there isn't much that I can do to improve the default behavior in the SFML_GRAPHICS backend.

Is the code you showed with ImmGetContext something that you have in your own code? If so, can't you position the IME window yourself so that it appears below the edit box? Getting the exact location of the cursor would be a bit more complex, but it could be done if you really need it and aren't satisfied with just having the IME window at a fixed location next to the edit box.

from tgui.

texus avatar texus commented on August 26, 2024

I looked into this a little bit further, I guess you want the ImmSetCompositionWindow and ImmSetCandidateWindow functions to be called by TGUI when the caret of the edit box moves?

One question I still have is who is in charge of showing and hiding the IME window? If I don't show or hide the window and only move it around, then the window might be in the way of other content when you are done typing. So should the gui also be responsible for hiding the window when the edit box is unfocused (and showing the window when it gains focus again)? Or do you want control over when the IME window is shown in your own code?

from tgui.

xland avatar xland commented on August 26, 2024

Thanks for reply.

I guess you want the ImmSetCompositionWindow and ImmSetCandidateWindow functions to be called by TGUI when the caret of the edit box moves?

Yes.

Who is in charge of showing and hiding the IME window?

I think the operating system does the job.

Here's some of the code I found:

Here's how GTK solves this problem:
https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gtk/gtkimcontextime.c

case WM_IME_STARTCOMPOSITION:
      context_ime->preediting = TRUE;
      gtk_im_context_ime_set_cursor_location (context, NULL);
      g_signal_emit_by_name (context, "preedit-start");
      if (context_ime->use_preedit)
        retval = GDK_WIN32_MESSAGE_FILTER_REMOVE;
      break;
case WM_IME_ENDCOMPOSITION:
  context_ime->preediting = FALSE;
  g_signal_emit_by_name (context, "preedit-changed");
  g_signal_emit_by_name (context, "preedit-end");
  if (context_ime->use_preedit)
    retval = GDK_WIN32_MESSAGE_FILTER_REMOVE;
  break;
case WM_IME_NOTIFY:
  switch (msg->wParam)
    {
    case IMN_SETOPENSTATUS:
      context_ime->opened = ImmGetOpenStatus (himc);
      gtk_im_context_ime_set_preedit_font (context);
      break;
    default:
      break;
    }
  break;

And FLTK did this:
https://github.com/fltk/fltk/blob/5f189aa2139da497de03b2dfc157dbffd176a27c/src/Fl_win32.cxx

static void get_imm_module() {
  s_imm_module = LoadLibrary("IMM32.DLL");
  if (!s_imm_module)
    Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n"
              "Please check your input method manager library accessibility.");
  flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx");
  flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext");
  flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow");
  flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext");
}
void Fl_WinAPI_Screen_Driver::set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
{
  if (!win) return;
  Fl_Window* tw = win->top_window();
  if (!tw->shown())
    return;
  HIMC himc = flImmGetContext(fl_xid(tw));
  if (himc) {
    COMPOSITIONFORM cfs;
    float s = Fl_Graphics_Driver::default_driver().scale();
    cfs.dwStyle = CFS_POINT;
    cfs.ptCurrentPos.x = int(X * s);
    cfs.ptCurrentPos.y = int(Y * s) - int(tw->labelsize() * s);
    // Attempt to have temporary text entered by input method use scaled font.
    // Does good, but still not always effective.
    Fl_GDI_Font_Descriptor *desc = (Fl_GDI_Font_Descriptor*)Fl_Graphics_Driver::default_driver().font_descriptor();
    if (desc) SelectObject((HDC)Fl_Graphics_Driver::default_driver().gc(), desc->fid);
    MapWindowPoints(fl_xid(win), fl_xid(tw), &cfs.ptCurrentPos, 1);
    flImmSetCompositionWindow(himc, &cfs);
    flImmReleaseContext(fl_xid(tw), himc);
  }
}

And RmlUi did this:(I'm involved in this PR)
mikke89/RmlUi@89a357b

#ifdef _MSC_VER
	#pragma comment(lib, "imm32")
#endif
void SystemInterface_Win32::ActivateKeyboard(Rml::Vector2f caret_position, float /*line_height*/)
{
	// Adjust the position of the input method editor (IME) to the caret.
	if (HIMC himc = ImmGetContext(window_handle))
	{
		COMPOSITIONFORM comp = {};
		comp.ptCurrentPos.x = (LONG)caret_position.x;
		comp.ptCurrentPos.y = (LONG)caret_position.y;
		comp.dwStyle = CFS_FORCE_POSITION;
		ImmSetCompositionWindow(himc, &comp);

		CANDIDATEFORM cand = {};
		cand.dwStyle = CFS_CANDIDATEPOS;
		cand.ptCurrentPos.x = (LONG)caret_position.x;
		cand.ptCurrentPos.y = (LONG)caret_position.y;
		ImmSetCandidateWindow(himc, &cand);

		ImmReleaseContext(window_handle, himc);
	}
}
void WidgetTextInput::SetKeyboardActive(bool active)
{
	if (SystemInterface* system = GetSystemInterface())
	{
		if (active)
		{
			// Activate the keyboard and submit the cursor position and line height to enable clients to adjust the input method editor (IME). Note
			// that the cursor is extended by one pixel along the top and bottom, we reverse this extension here.
			const Vector2f element_offset = parent->GetAbsoluteOffset() - scroll_offset;
			const Vector2f absolute_cursor_position = element_offset + cursor_position + Vector2f(0, 1);
			const float line_height = cursor_size.y - 2.f;
			system->ActivateKeyboard(absolute_cursor_position, line_height);
		}
		else
		{
			system->DeactivateKeyboard();
		}
	}
}

Here are some of the information provided by Microsoft.
(I'm not familiar with Mac OS and Linux)

Using an Input Method Editor in a Game

Input Method Editor (IME) requirements

Example: Getting WMI Data from the Local Computer

from tgui.

texus avatar texus commented on August 26, 2024

Thanks for the information. I will look into this more in the next few days, I expect to have something working this weekend.

I'm still a bit confused about what to do on focus and unfocus. GTK and RmlUi don't seem to do anything special, but FLTK calls ImmAssociateContextEx and SDL calls ImmAssociateContext. Do you know what the effect would be of calling ImmAssociateContextEx(hwnd, 0, IACE_DEFAULT); when the edit box is focused and ImmAssociateContextEx(hwnd, 0, 0); on unfocus, like FLTK is doing? Would that be a good thing to do or should TGUI only call ImmSetCompositionWindow and ImmSetCandidateWindow?

I'm probably going to call the SDL functions when TGUI is built with the SDL backend (as SDL also has implementations for macOS and Linux), and fall back to calling ImmSetCompositionWindow and ImmSetCandidateWindow myself and only supporting Windows when SFML is used.

from tgui.

texus avatar texus commented on August 26, 2024

The IME candidate list will now follow the text cursor on Windows.
I won't bother with implementing this for Linux or macOS until there is someone that actually needs it there.

I saw no effect from calling ImmSetCandidateWindow in any of my tests, so I'm only using ImmSetCompositionWindow.

from tgui.

xland avatar xland commented on August 26, 2024

Sorry for late reply.
I'm on a vacation and can't focus on this issue.

from tgui.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.