mirror of
https://github.com/ocornut/imgui.git
synced 2024-11-24 15:50:25 +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
|
// Helpers: String
|
||||||
static int ImStricmp(const char* str1, const char* str2);
|
static int ImStricmp(const char* str1, const char* str2);
|
||||||
static int ImStrnicmp(const char* str1, const char* str2, int count);
|
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 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 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 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);
|
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;
|
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)
|
static const char* ImStristr(const char* haystack, const char* needle, const char* needle_end)
|
||||||
{
|
{
|
||||||
if (!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);
|
ImVec2 text_size(0.f, 0.f);
|
||||||
if (g.ActiveId == id || (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetID("#SCROLLY")))
|
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;
|
edit_state.CursorAnim += g.IO.DeltaTime;
|
||||||
|
|
||||||
// 1. Display the text (this can be more easily clipped)
|
// We need to:
|
||||||
// 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.
|
// - Display the text (this can be more easily clipped)
|
||||||
ImVec2 cursor_offset;
|
// - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
|
||||||
InputTextCalcTextSizeW(edit_state.Text.begin(), edit_state.Text.begin() + edit_state.StbState.cursor, NULL, &cursor_offset);
|
// - 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
|
// Scroll
|
||||||
if (edit_state.CursorFollow)
|
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
|
// Horizontal scroll in chunks of quarter width
|
||||||
const float scroll_increment_x = size.x * 0.25f;
|
const float scroll_increment_x = size.x * 0.25f;
|
||||||
if (cursor_offset.x < edit_state.ScrollX)
|
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)
|
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
|
// Vertical scroll
|
||||||
if (is_multiline)
|
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;
|
edit_state.CursorFollow = false;
|
||||||
|
ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f);
|
||||||
|
|
||||||
// Draw selection
|
// Draw selection
|
||||||
int select_begin_idx = edit_state.StbState.select_start;
|
if (edit_state.StbState.select_start != edit_state.StbState.select_end)
|
||||||
int select_end_idx = edit_state.StbState.select_end;
|
|
||||||
if (select_begin_idx != select_end_idx)
|
|
||||||
{
|
{
|
||||||
// FIXME-OPT
|
const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end);
|
||||||
ImWchar* text_selected_begin = edit_state.Text.begin() + ImMin(select_begin_idx,select_end_idx);
|
const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end);
|
||||||
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);
|
|
||||||
|
|
||||||
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_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;
|
float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
|
||||||
ImU32 bg_color = draw_window->Color(ImGuiCol_TextSelectedBg);
|
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; )
|
for (const ImWchar* p = text_selected_begin; p < text_selected_end; )
|
||||||
{
|
{
|
||||||
ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
|
if (rect_pos.y > clip_rect.w + g.FontSize)
|
||||||
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
|
break;
|
||||||
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));
|
if (rect_pos.y < clip_rect.y)
|
||||||
rect.Clip(clip_rect);
|
{
|
||||||
if (rect.Overlaps(clip_rect))
|
while (p < text_selected_end)
|
||||||
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
|
if (*p++ == '\n')
|
||||||
rect_pos.x = 0.0f;
|
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;
|
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_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
|
// 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())
|
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));
|
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