mirror of
https://github.com/ocornut/imgui.git
synced 2024-11-28 09:30:56 +01:00
InputTextMultiline() painfully merged most computation passes into one, better clipping, much faster for large text (#200)
This commit is contained in:
parent
84987ac3e0
commit
73db855c77
106
imgui.cpp
106
imgui.cpp
@ -551,8 +551,9 @@ static void CloseInactivePopups();
|
||||
// Helpers: String
|
||||
static int ImStricmp(const char* str1, const char* str2);
|
||||
static int ImStrnicmp(const char* str1, const char* str2, int count);
|
||||
static char* ImStrdup(const char *str);
|
||||
static char* ImStrdup(const char* str);
|
||||
static size_t ImStrlenW(const ImWchar* str);
|
||||
static const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
|
||||
static const char* ImStristr(const char* haystack, const char* needle, const char* needle_end);
|
||||
static size_t ImFormatString(char* buf, size_t buf_size, const char* fmt, ...);
|
||||
static size_t ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args);
|
||||
@ -786,6 +787,13 @@ static size_t ImStrlenW(const ImWchar* str)
|
||||
return n;
|
||||
}
|
||||
|
||||
static const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
|
||||
{
|
||||
while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
|
||||
buf_mid_line--;
|
||||
return buf_mid_line;
|
||||
}
|
||||
|
||||
static const char* ImStristr(const char* haystack, const char* needle, const char* needle_end)
|
||||
{
|
||||
if (!needle_end)
|
||||
@ -7102,16 +7110,54 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
|
||||
ImVec2 text_size(0.f, 0.f);
|
||||
if (g.ActiveId == id || (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetID("#SCROLLY")))
|
||||
{
|
||||
ImVec2 render_scroll = ImVec2((edit_state.Id == id) ? edit_state.ScrollX : 0.0f, 0.0f);
|
||||
edit_state.CursorAnim += g.IO.DeltaTime;
|
||||
|
||||
// 1. Display the text (this can be more easily clipped)
|
||||
// 2. Handle scrolling, highlight selection, display cursor: those all requires some form of 1d->2d cursor position calculation, which we will try to merge to minimize the cost.
|
||||
ImVec2 cursor_offset;
|
||||
InputTextCalcTextSizeW(edit_state.Text.begin(), edit_state.Text.begin() + edit_state.StbState.cursor, NULL, &cursor_offset);
|
||||
// We need to:
|
||||
// - Display the text (this can be more easily clipped)
|
||||
// - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
|
||||
// - Measure text height (for scrollbar)
|
||||
// We are attempting to do most of that in one main pass to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
|
||||
const ImWchar* text_begin = edit_state.Text.begin();
|
||||
const ImWchar* text_end = text_begin + edit_state.CurLenW;
|
||||
ImVec2 cursor_offset, select_start_offset;
|
||||
|
||||
if (is_multiline)
|
||||
text_size = InputTextCalcTextSizeW(edit_state.Text.begin(), edit_state.Text.begin() + edit_state.CurLenW);
|
||||
{
|
||||
// Count lines + find lines numbers of cursor and select_start
|
||||
int matches_remaining = 0;
|
||||
int matches_line_no[2] = { -1, -999 };
|
||||
const ImWchar* matches_ptr[2];
|
||||
matches_ptr[0] = text_begin + edit_state.StbState.cursor; matches_remaining++;
|
||||
if (edit_state.StbState.select_start != edit_state.StbState.select_end)
|
||||
{
|
||||
matches_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
|
||||
matches_line_no[1] = -1;
|
||||
matches_remaining++;
|
||||
}
|
||||
matches_remaining += is_multiline ? 1 : 0; // So that we never exit the loop until all lines are counted.
|
||||
|
||||
int line_count = 0;
|
||||
for (const ImWchar* s = text_begin; s < text_end+1; s++)
|
||||
if ((*s) == '\n' || s == text_end)
|
||||
{
|
||||
line_count++;
|
||||
if (matches_line_no[0] == -1 && s >= matches_ptr[0]) { matches_line_no[0] = line_count; if (--matches_remaining <= 0) break; }
|
||||
if (matches_line_no[1] == -1 && s >= matches_ptr[1]) { matches_line_no[1] = line_count; if (--matches_remaining <= 0) break; }
|
||||
}
|
||||
|
||||
// Calculate 2d position
|
||||
IM_ASSERT(matches_line_no[0] != -1);
|
||||
cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(matches_ptr[0], text_begin), matches_ptr[0]).x;
|
||||
cursor_offset.y = matches_line_no[0] * g.FontSize;
|
||||
if (matches_line_no[1] >= 0)
|
||||
{
|
||||
select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(matches_ptr[1], text_begin), matches_ptr[1]).x;
|
||||
select_start_offset.y = matches_line_no[1] * g.FontSize;
|
||||
}
|
||||
|
||||
// Calculate text height
|
||||
if (is_multiline)
|
||||
text_size = ImVec2(size.x, line_count * g.FontSize);
|
||||
}
|
||||
|
||||
// Scroll
|
||||
if (edit_state.CursorFollow)
|
||||
@ -7119,9 +7165,9 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
|
||||
// Horizontal scroll in chunks of quarter width
|
||||
const float scroll_increment_x = size.x * 0.25f;
|
||||
if (cursor_offset.x < edit_state.ScrollX)
|
||||
render_scroll.x = edit_state.ScrollX = ImMax(0.0f, cursor_offset.x - scroll_increment_x);
|
||||
edit_state.ScrollX = ImMax(0.0f, cursor_offset.x - scroll_increment_x);
|
||||
else if (cursor_offset.x - size.x >= edit_state.ScrollX)
|
||||
render_scroll.x = edit_state.ScrollX = cursor_offset.x - size.x + scroll_increment_x;
|
||||
edit_state.ScrollX = cursor_offset.x - size.x + scroll_increment_x;
|
||||
|
||||
// Vertical scroll
|
||||
if (is_multiline)
|
||||
@ -7137,30 +7183,38 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
|
||||
}
|
||||
}
|
||||
edit_state.CursorFollow = false;
|
||||
ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
|
||||
|
||||
// Draw selection
|
||||
int select_begin_idx = edit_state.StbState.select_start;
|
||||
int select_end_idx = edit_state.StbState.select_end;
|
||||
if (select_begin_idx != select_end_idx)
|
||||
if (edit_state.StbState.select_start != edit_state.StbState.select_end)
|
||||
{
|
||||
// FIXME-OPT
|
||||
ImWchar* text_selected_begin = edit_state.Text.begin() + ImMin(select_begin_idx,select_end_idx);
|
||||
ImWchar* text_selected_end = edit_state.Text.begin() + ImMax(select_begin_idx,select_end_idx);
|
||||
ImVec2 rect_pos;
|
||||
InputTextCalcTextSizeW(edit_state.Text.begin(), text_selected_begin, NULL, &rect_pos);
|
||||
const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
|
||||
const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
|
||||
|
||||
float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
|
||||
float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
|
||||
ImU32 bg_color = draw_window->Color(ImGuiCol_TextSelectedBg);
|
||||
ImVec2 rect_pos = render_pos + select_start_offset - render_scroll;
|
||||
for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
|
||||
{
|
||||
ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
|
||||
if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
|
||||
ImRect rect(render_pos - render_scroll + rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), render_pos - render_scroll + rect_pos + ImVec2(rect_size.x, bg_offy_dn));
|
||||
rect.Clip(clip_rect);
|
||||
if (rect.Overlaps(clip_rect))
|
||||
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
|
||||
rect_pos.x = 0.0f;
|
||||
if (rect_pos.y > clip_rect.w + g.FontSize)
|
||||
break;
|
||||
if (rect_pos.y < clip_rect.y)
|
||||
{
|
||||
while (p < text_selected_end)
|
||||
if (*p++ == '\n')
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
|
||||
if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines
|
||||
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
|
||||
rect.Clip(clip_rect);
|
||||
if (rect.Overlaps(clip_rect))
|
||||
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
|
||||
}
|
||||
rect_pos.x = render_pos.x - render_scroll.x;
|
||||
rect_pos.y += g.FontSize;
|
||||
}
|
||||
}
|
||||
@ -7168,7 +7222,7 @@ static bool InputTextEx(const char* label, char* buf, size_t buf_size, const ImV
|
||||
draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, draw_window->Color(ImGuiCol_Text), buf, buf+edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
|
||||
|
||||
// Draw blinking cursor
|
||||
ImVec2 cursor_screen_pos = render_pos + cursor_offset - ImVec2(edit_state.ScrollX, 0.0f);
|
||||
ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
|
||||
if (g.InputTextState.CursorIsVisible())
|
||||
draw_window->DrawList->AddLine(cursor_screen_pos + ImVec2(0,-g.FontSize+1), cursor_screen_pos + ImVec2(0,-1), window->Color(ImGuiCol_Text));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user