imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Incorrect visual selection in ImGui::InputTextMultiline with Japanese.

Open Dimoks opened this issue 11 months ago • 5 comments

Version/Branch of Dear ImGui:

master

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl2.cpp

Compiler, OS:

gcc (Rev3, Built by MSYS2 project) 14.2.0, Windows 10.

Full config/build information:

No response

Details:

If you wrap a line by characters, the visual selection will be on the next line, if you do not select from the beginning of the first line.

Screenshots/Video:

Image

Minimal, Complete and Verifiable Example code:

ImGuiIO &io = ImGui::GetIO();
io.FontGlobalScale = 1.0;
ImVector<ImWchar> ranges;
ImFontGlyphRangesBuilder builder;
builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
builder.AddRanges(io.Fonts->GetGlyphRangesJapanese());
builder.BuildRanges(&ranges);
io.Fonts->AddFontFromMemoryCompressedTTF(Unifont_compressed_data,
  Unifont_compressed_size, 16, nullptr, ranges.Data); // unifont-16.0.02
io.Fonts->Build();

struct {
  std::string selected_text;
  int selection_start = 0;
  int selection_end = 0;
} static selection;

const float wrap_width = ImGui::GetContentRegionAvail().x;
ImFont* font = ImGui::GetFont();

std::string text = u8" 誰も彼も眠った頃、夜の冷気で瞼を開ける。\r\n";

std::string wrapped_text;
const char* text_start = text.c_str();
const char* text_end = text_start + text.size();

while (text_start < text_end) {
  const char* line_end = font->CalcWordWrapPositionA(1.0f,
                                       text_start, text_end, wrap_width);
  if (line_end == text_start) line_end++;

  wrapped_text.append(text_start, line_end);
  if (line_end < text_end && *line_end != '\n') {
    wrapped_text += '\n';
  }

  text_start = line_end;
}

const float line_height = ImGui::GetTextLineHeightWithSpacing();
const float height = (std::count(wrapped_text.begin(),
                                  wrapped_text.end(), '\n') + 1) * line_height;

ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(0, 0, 0, 0));

std::string input_id = "##input_1";

ImGui::InputTextMultiline(
  input_id.c_str(),
  const_cast<char*>(wrapped_text.c_str()),
  wrapped_text.size() + 1,
  ImVec2(wrap_width, height),
  ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll);

ImGui::PopStyleColor();

if (ImGui::IsItemActive()) {
  ImGuiInputTextState* state = ImGui::GetInputTextState(ImGui::GetItemID());
  if (state && state->HasSelection()) {
    selection.selection_start = state->GetSelectionStart();
    selection.selection_end = state->GetSelectionEnd();
  } else {
    selection.selection_start = 0;
    selection.selection_end = 0;
  }
}

Dimoks avatar Apr 01 '25 05:04 Dimoks

Could you try reducing your code to the required minimum, e.g. the wrapping logic doesn't need to be there, you can use the final wrapped string + see if it happens with another font (or even with the default font that doesn't have japanese glyphs?).

ocornut avatar Apr 01 '25 20:04 ocornut

Sorry I posted an incorrect message, I had a theory and while investigating I discovered another thing, and posted the old draft.

I called ImGui::DebugTextEncoding(wrapped_text.c_str());: Image

And notice the wrapping code was wrong because it introduce \n in the middle of multibyte characters. The U+3000 (0xE3 0x80 0x80) becomes 0xE3 0x0A 0x80 0x80 which are two invalid values.

And InputText() seems to have issue dealing with the invalid UTF-8 codepoints, which wouldn't be the first time (see #6397).

So there are multiple problems:

  • (1) CalcWordWrapPosition didn't handle ideographic comma (、)and full stop (。) so the line was not wrapped. Fixed by b4bd596.
  • (2) CalcWordWrapPosition has a fallback when there's no wrapping, that it moves by a single character, but it used +1 instead of coutning UTf-8 bytes. Fixed by fcab22f.
  • (3) I am not sure I understand why the fallback in (2) is taken, I will investigate.
  • (4) InputText() has a bug dealing with \n and invalid UTF-8 codepoint, which were generated by the combinations of 1 and 2 above. I don't think this would affect you anymore, but I am going to investigate that too.

ocornut avatar Apr 01 '25 21:04 ocornut

There are errors in my code. Can you tell me how to fix them?

Dimoks avatar Apr 01 '25 21:04 Dimoks

There is no error in the code you posted above. You can update to get the 2 fixes I pushed already.

The issue in (3) could separately be improved but with the 2 fixes you shouldn't get that bug.

ocornut avatar Apr 01 '25 21:04 ocornut

There are errors in my code.

Sorry maybe you read email notification for my first message (which I deleted now, it was a draft before I realized the culprit was an a combination of issues in CalcWordWrapPosition). Read the second message on GitHub topic.

ocornut avatar Apr 01 '25 21:04 ocornut