|
@@ -135,7 +135,6 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
|
|
|
|
|
|
// For InputTextEx()
|
|
// For InputTextEx()
|
|
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
|
|
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
|
|
-static int InputTextCalcTextLenAndLineCount(ImGuiContext* ctx, const char* text_begin, const char** out_text_end, float wrap_width);
|
|
|
|
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0);
|
|
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0);
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|
|
@@ -2038,7 +2037,8 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags
|
|
if (!ret)
|
|
if (!ret)
|
|
{
|
|
{
|
|
EndPopup();
|
|
EndPopup();
|
|
- IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
|
|
|
|
|
|
+ if (!g.IO.ConfigDebugBeginReturnValueOnce && !g.IO.ConfigDebugBeginReturnValueLoop) // Begin may only return false with those debug tools activated.
|
|
|
|
+ IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
g.BeginComboDepth++;
|
|
g.BeginComboDepth++;
|
|
@@ -3943,46 +3943,6 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, si
|
|
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
|
|
return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
|
|
}
|
|
}
|
|
|
|
|
|
-// This is only used in the path where the multiline widget is inactive.
|
|
|
|
-static int InputTextCalcTextLenAndLineCount(ImGuiContext* ctx, const char* text_begin, const char** out_text_end, float wrap_width)
|
|
|
|
-{
|
|
|
|
- int line_count = 0;
|
|
|
|
- const char* s = text_begin;
|
|
|
|
- if (wrap_width == 0.0f)
|
|
|
|
- {
|
|
|
|
- while (true)
|
|
|
|
- {
|
|
|
|
- const char* s_eol = strchr(s, '\n');
|
|
|
|
- line_count++;
|
|
|
|
- if (s_eol == NULL)
|
|
|
|
- {
|
|
|
|
- s = s + ImStrlen(s);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- s = s_eol + 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- // FIXME-WORDWRAP, FIXME-OPT: This is very suboptimal.
|
|
|
|
- // We basically want both text_end and text_size, they could more optimally be emitted from a RenderText call that uses word-wrapping.
|
|
|
|
- ImGuiContext& g = *ctx;
|
|
|
|
- ImFont* font = g.Font;
|
|
|
|
- const char* text_end = text_begin + strlen(text_begin);
|
|
|
|
- while (s < text_end)
|
|
|
|
- {
|
|
|
|
- s = ImFontCalcWordWrapPositionEx(font, g.FontSize, s, text_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
|
|
|
|
- s = (*s == '\n') ? s + 1 : s;
|
|
|
|
- line_count++;
|
|
|
|
- }
|
|
|
|
- if (text_end > text_begin && text_end[-1] == '\n')
|
|
|
|
- line_count++;
|
|
|
|
- IM_ASSERT(s == text_end);
|
|
|
|
- }
|
|
|
|
- *out_text_end = s;
|
|
|
|
- return line_count;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
|
|
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
|
|
{
|
|
{
|
|
ImGuiContext& g = *ctx;
|
|
ImGuiContext& g = *ctx;
|
|
@@ -4560,6 +4520,97 @@ void ImGui::InputTextDeactivateHook(ImGuiID id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int* ImLowerBound(int* in_begin, int* in_end, int v)
|
|
|
|
+{
|
|
|
|
+ int* in_p = in_begin;
|
|
|
|
+ for (size_t count = (size_t)(in_end - in_p); count > 0; )
|
|
|
|
+ {
|
|
|
|
+ size_t count2 = count >> 1;
|
|
|
|
+ int* mid = in_p + count2;
|
|
|
|
+ if (*mid < v)
|
|
|
|
+ {
|
|
|
|
+ in_p = ++mid;
|
|
|
|
+ count -= count2 + 1;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ count = count2;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return in_p;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool?
|
|
|
|
+// 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive)
|
|
|
|
+static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end)
|
|
|
|
+{
|
|
|
|
+ ImGuiContext& g = *GImGui;
|
|
|
|
+ int size = 0;
|
|
|
|
+ const char* s;
|
|
|
|
+ if (flags & ImGuiInputTextFlags_WordWrap)
|
|
|
|
+ {
|
|
|
|
+ for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s)
|
|
|
|
+ {
|
|
|
|
+ if (size++ <= max_output_buffer_size)
|
|
|
|
+ line_index->Offsets.push_back((int)(s - buf));
|
|
|
|
+ s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (buf_end != NULL)
|
|
|
|
+ {
|
|
|
|
+ for (s = buf; s < buf_end; s = s ? s + 1 : buf_end)
|
|
|
|
+ {
|
|
|
|
+ if (size++ <= max_output_buffer_size)
|
|
|
|
+ line_index->Offsets.push_back((int)(s - buf));
|
|
|
|
+ s = (const char*)ImMemchr(s, '\n', buf_end - s);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ const char* s_eol;
|
|
|
|
+ for (s = buf; ; s = s_eol + 1)
|
|
|
|
+ {
|
|
|
|
+ if (size++ <= max_output_buffer_size)
|
|
|
|
+ line_index->Offsets.push_back((int)(s - buf));
|
|
|
|
+ if ((s_eol = strchr(s, '\n')) != NULL)
|
|
|
|
+ continue;
|
|
|
|
+ s += strlen(s);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (out_buf_end != NULL)
|
|
|
|
+ *out_buf_end = buf_end = s;
|
|
|
|
+ if (size == 0)
|
|
|
|
+ {
|
|
|
|
+ line_index->Offsets.push_back(0);
|
|
|
|
+ size++;
|
|
|
|
+ }
|
|
|
|
+ if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size)
|
|
|
|
+ {
|
|
|
|
+ line_index->Offsets.push_back((int)(buf_end - buf));
|
|
|
|
+ size++;
|
|
|
|
+ }
|
|
|
|
+ return size;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextState* state, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, int cursor_n)
|
|
|
|
+{
|
|
|
|
+ const char* cursor_ptr = buf + cursor_n;
|
|
|
|
+ int* it_begin = line_index->Offsets.begin();
|
|
|
|
+ int* it_end = line_index->Offsets.end();
|
|
|
|
+ const int* it = ImLowerBound(it_begin, it_end, cursor_n);
|
|
|
|
+ if (it > it_begin)
|
|
|
|
+ if (it == it_end || *it != cursor_n || (cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0 && state != NULL && state->LastMoveDirectionLR == ImGuiDir_Right))
|
|
|
|
+ it--;
|
|
|
|
+
|
|
|
|
+ const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it);
|
|
|
|
+ const char* line_start = line_index->get_line_begin(buf, line_no);
|
|
|
|
+ ImVec2 offset;
|
|
|
|
+ offset.x = InputTextCalcTextSize(&g, line_start, cursor_ptr, buf_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x;
|
|
|
|
+ offset.y = (line_no + 1) * g.FontSize;
|
|
|
|
+ return offset;
|
|
|
|
+}
|
|
|
|
+
|
|
// Edit a string of text
|
|
// Edit a string of text
|
|
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
|
|
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
|
|
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
|
|
// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
|
|
@@ -5332,15 +5383,42 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
buf_display = hint;
|
|
buf_display = hint;
|
|
buf_display_end = hint + ImStrlen(hint);
|
|
buf_display_end = hint + ImStrlen(hint);
|
|
}
|
|
}
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ if (render_cursor || render_selection || g.ActiveId == id)
|
|
|
|
+ buf_display_end = buf_display + state->TextLen; //-V595
|
|
|
|
+ else if (is_multiline && !is_wordwrap)
|
|
|
|
+ buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path.
|
|
|
|
+ else
|
|
|
|
+ buf_display_end = buf_display + ImStrlen(buf_display);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Calculate visibility
|
|
|
|
+ int line_visible_n0 = 0, line_visible_n1 = 1;
|
|
|
|
+ if (is_multiline)
|
|
|
|
+ CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);
|
|
|
|
+
|
|
|
|
+ // Build line index for easy data access (makes code below simpler and faster)
|
|
|
|
+ ImGuiTextIndex* line_index = &g.InputTextLineIndex;
|
|
|
|
+ line_index->Offsets.resize(0);
|
|
|
|
+ int line_count = 1;
|
|
|
|
+ if (is_multiline)
|
|
|
|
+ line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, (render_cursor && state && state->CursorFollow) ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end);
|
|
|
|
+ line_index->EndOffset = (int)(buf_display_end - buf_display);
|
|
|
|
+ line_visible_n1 = ImMin(line_visible_n1, line_count);
|
|
|
|
+
|
|
|
|
+ // Store text height (we don't need width)
|
|
|
|
+ text_size = ImVec2(inner_size.x, line_count * g.FontSize);
|
|
|
|
+ //GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255));
|
|
|
|
+
|
|
|
|
+ // Calculate blinking cursor position
|
|
|
|
+ const ImVec2 cursor_offset = render_cursor && state ? InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state->Stb->cursor) : ImVec2(0.0f, 0.0f);
|
|
|
|
+ ImVec2 draw_scroll;
|
|
|
|
|
|
// Render text. We currently only render selection when the widget is active or while scrolling.
|
|
// Render text. We currently only render selection when the widget is active or while scrolling.
|
|
- // FIXME: This is one of the messiest piece of the whole codebase.
|
|
|
|
|
|
+ const ImU32 text_col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
|
|
if (render_cursor || render_selection)
|
|
if (render_cursor || render_selection)
|
|
{
|
|
{
|
|
- IM_ASSERT(state != NULL);
|
|
|
|
- if (!is_displaying_hint)
|
|
|
|
- buf_display_end = buf_display + state->TextLen;
|
|
|
|
-
|
|
|
|
// Render text (with cursor and selection)
|
|
// Render text (with cursor and selection)
|
|
// This is going to be messy. We need to:
|
|
// This is going to be messy. We need to:
|
|
// - Display the text (this alone can be more easily clipped)
|
|
// - Display the text (this alone can be more easily clipped)
|
|
@@ -5348,85 +5426,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
// - Measure text height (for scrollbar)
|
|
// - 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)
|
|
// 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)
|
|
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
|
|
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
|
|
- const char* text_begin = buf_display;
|
|
|
|
- const char* text_end = text_begin + state->TextLen;
|
|
|
|
- ImVec2 cursor_offset;
|
|
|
|
- float select_start_offset_y = 0.0f; // Offset of beginning of non-wrapped line for selection.
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- // Find lines numbers straddling cursor and selection min position
|
|
|
|
- int cursor_line_no = render_cursor ? -1 : -1000;
|
|
|
|
- int selmin_line_no = render_selection ? -1 : -1000;
|
|
|
|
- const char* cursor_ptr = render_cursor ? text_begin + state->Stb->cursor : NULL;
|
|
|
|
- const char* selmin_ptr = render_selection ? text_begin + ImMin(state->Stb->select_start, state->Stb->select_end) : NULL;
|
|
|
|
- const char* cursor_line_start = NULL;
|
|
|
|
- const char* selmin_line_start = NULL;
|
|
|
|
- bool cursor_straddle_word_wrap = false;
|
|
|
|
-
|
|
|
|
- // Count lines and find line number for cursor and selection ends
|
|
|
|
- // FIXME: Switch to zero-based index to reduce confusion.
|
|
|
|
- int line_count = 1;
|
|
|
|
- if (is_multiline)
|
|
|
|
- {
|
|
|
|
- if (!is_wordwrap)
|
|
|
|
- {
|
|
|
|
- for (const char* s = text_begin; (s = (const char*)ImMemchr(s, '\n', (size_t)(text_end - s))) != NULL; s++)
|
|
|
|
- {
|
|
|
|
- if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_no = line_count; }
|
|
|
|
- if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_no = line_count; }
|
|
|
|
- line_count++;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- bool is_start_of_non_wrapped_line = true;
|
|
|
|
- int line_count_for_non_wrapped_line = 1;
|
|
|
|
- for (const char* s = text_begin; s < text_end; s = (*s == '\n') ? s + 1 : s)
|
|
|
|
- {
|
|
|
|
- const char* s_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, text_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
|
|
|
|
- const char* s_prev = s;
|
|
|
|
- s = s_eol;
|
|
|
|
- if (cursor_line_no == -1 && s >= cursor_ptr) { cursor_line_start = s_prev; cursor_line_no = line_count; }
|
|
|
|
- if (selmin_line_no == -1 && s >= selmin_ptr) { selmin_line_start = s_prev; selmin_line_no = line_count_for_non_wrapped_line; }
|
|
|
|
- if (s == cursor_ptr && *cursor_ptr != '\n' && *cursor_ptr != 0)
|
|
|
|
- cursor_straddle_word_wrap = true;
|
|
|
|
- is_start_of_non_wrapped_line = (*s == '\n');
|
|
|
|
- line_count++;
|
|
|
|
- if (is_start_of_non_wrapped_line)
|
|
|
|
- line_count_for_non_wrapped_line = line_count;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- //IMGUI_DEBUG_LOG("%d\n", selmin_line_no);
|
|
|
|
- }
|
|
|
|
- if (cursor_line_no == -1)
|
|
|
|
- cursor_line_no = line_count;
|
|
|
|
- if (cursor_line_start == NULL)
|
|
|
|
- cursor_line_start = ImStrbol(cursor_ptr, text_begin);
|
|
|
|
- if (selmin_line_no == -1)
|
|
|
|
- selmin_line_no = line_count;
|
|
|
|
- if (selmin_line_start == NULL)
|
|
|
|
- selmin_line_start = ImStrbol(cursor_ptr, text_begin);
|
|
|
|
-
|
|
|
|
- // Calculate 2d position by finding the beginning of the line and measuring distance
|
|
|
|
- if (render_cursor)
|
|
|
|
- {
|
|
|
|
- cursor_offset.x = InputTextCalcTextSize(&g, cursor_line_start, cursor_ptr, text_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x;
|
|
|
|
- cursor_offset.y = cursor_line_no * g.FontSize;
|
|
|
|
- if (is_multiline && cursor_straddle_word_wrap && state->LastMoveDirectionLR == ImGuiDir_Left)
|
|
|
|
- cursor_offset = ImVec2(0.0f, cursor_offset.y + g.FontSize);
|
|
|
|
- }
|
|
|
|
- if (selmin_line_no >= 0)
|
|
|
|
- select_start_offset_y = selmin_line_no * g.FontSize;
|
|
|
|
-
|
|
|
|
- // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
|
|
|
|
- if (is_multiline)
|
|
|
|
- {
|
|
|
|
- if (is_wordwrap && text_end > text_begin && text_end[-1] != '\n')
|
|
|
|
- line_count--;
|
|
|
|
- text_size = ImVec2(inner_size.x, line_count * g.FontSize);
|
|
|
|
- }
|
|
|
|
- state->LineCount = line_count;
|
|
|
|
- }
|
|
|
|
|
|
+ IM_ASSERT(state != NULL);
|
|
|
|
+ state->LineCount = line_count;
|
|
|
|
|
|
// Scroll
|
|
// Scroll
|
|
float new_scroll_y = scroll_y;
|
|
float new_scroll_y = scroll_y;
|
|
@@ -5444,7 +5445,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- state->Scroll.y = 0.0f;
|
|
|
|
|
|
+ state->Scroll.x = 0.0f;
|
|
}
|
|
}
|
|
|
|
|
|
// Vertical scroll
|
|
// Vertical scroll
|
|
@@ -5452,7 +5453,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
{
|
|
{
|
|
// Test if cursor is vertically visible
|
|
// Test if cursor is vertically visible
|
|
if (cursor_offset.y - g.FontSize < scroll_y)
|
|
if (cursor_offset.y - g.FontSize < scroll_y)
|
|
- new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
|
|
|
|
|
|
+ new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
|
|
else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)
|
|
else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)
|
|
new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
|
|
new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
|
|
}
|
|
}
|
|
@@ -5471,103 +5472,86 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|
scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y);
|
|
scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y);
|
|
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
|
|
draw_pos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag
|
|
draw_window->Scroll.y = scroll_y;
|
|
draw_window->Scroll.y = scroll_y;
|
|
|
|
+ CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);
|
|
|
|
+ line_visible_n1 = ImMin(line_visible_n1, line_count);
|
|
}
|
|
}
|
|
|
|
|
|
// Draw selection
|
|
// Draw selection
|
|
- const ImVec2 draw_scroll = ImVec2(state->Scroll.x, 0.0f);
|
|
|
|
|
|
+ draw_scroll.x = state->Scroll.x;
|
|
if (render_selection)
|
|
if (render_selection)
|
|
{
|
|
{
|
|
- const char* text_selected_begin = text_begin + ImMin(state->Stb->select_start, state->Stb->select_end);
|
|
|
|
- const char* text_selected_end = text_begin + ImMax(state->Stb->select_start, state->Stb->select_end);
|
|
|
|
|
|
+ const ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
|
|
|
|
+ const 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.
|
|
|
|
+ const float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
|
|
|
|
+ const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
|
|
|
|
|
|
- ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
|
|
|
|
- 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_min_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
|
|
|
|
- ImVec2 rect_pos = draw_pos - draw_scroll;
|
|
|
|
- rect_pos.y += select_start_offset_y;
|
|
|
|
- for (const char* p = ImStrbol(text_selected_begin, text_begin); p < text_selected_end; rect_pos.y += g.FontSize)
|
|
|
|
|
|
+ const char* text_selected_begin = buf_display + ImMin(state->Stb->select_start, state->Stb->select_end);
|
|
|
|
+ const char* text_selected_end = buf_display + ImMax(state->Stb->select_start, state->Stb->select_end);
|
|
|
|
+ for (int line_n = line_visible_n0; line_n < line_visible_n1; line_n++)
|
|
{
|
|
{
|
|
- if (rect_pos.y > clip_rect.Max.y + g.FontSize)
|
|
|
|
- break;
|
|
|
|
- const char* p_eol = is_wordwrap ? ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks) : (const char*)ImMemchr((void*)p, '\n', text_selected_end - p);
|
|
|
|
- if (p_eol == NULL)
|
|
|
|
- p_eol = text_selected_end;
|
|
|
|
- const char* p_next = is_wordwrap ? (*p_eol == '\n' ? p_eol + 1 : p_eol) : (p_eol + 1);
|
|
|
|
- if (rect_pos.y >= clip_rect.Min.y)
|
|
|
|
- {
|
|
|
|
- const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p;
|
|
|
|
- const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol;
|
|
|
|
- if ((*p_eol == '\n' && text_selected_begin <= p_eol) || (text_selected_begin < p_eol))
|
|
|
|
- {
|
|
|
|
- ImVec2 rect_offset = CalcTextSize(p, line_selected_begin);
|
|
|
|
- ImVec2 rect_size = CalcTextSize(line_selected_begin, line_selected_end);
|
|
|
|
- rect_size.x = ImMax(rect_size.x, bg_min_width); // So we can see selected empty lines
|
|
|
|
- ImRect rect(rect_pos + ImVec2(rect_offset.x, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_offset.x + rect_size.x, bg_offy_dn));
|
|
|
|
- rect.ClipWith(clip_rect);
|
|
|
|
- if (rect.Overlaps(clip_rect))
|
|
|
|
- draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- p = p_next;
|
|
|
|
|
|
+ const char* p = line_index->get_line_begin(buf_display, line_n);
|
|
|
|
+ const char* p_eol = line_index->get_line_end(buf_display, line_n);
|
|
|
|
+ const bool p_eol_is_wrap = (p_eol < buf_display_end && *p_eol != '\n');
|
|
|
|
+ if (p_eol_is_wrap)
|
|
|
|
+ p_eol++;
|
|
|
|
+ const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p;
|
|
|
|
+ const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol;
|
|
|
|
+
|
|
|
|
+ float rect_width = 0.0f;
|
|
|
|
+ if (line_selected_begin < line_selected_end)
|
|
|
|
+ rect_width += CalcTextSize(line_selected_begin, line_selected_end).x;
|
|
|
|
+ if (text_selected_begin <= p_eol && text_selected_end > p_eol && !p_eol_is_wrap)
|
|
|
|
+ rect_width += bg_eol_width; // So we can see selected empty lines
|
|
|
|
+ if (rect_width == 0.0f)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ ImRect rect;
|
|
|
|
+ rect.Min.x = draw_pos.x - draw_scroll.x + CalcTextSize(p, line_selected_begin).x;
|
|
|
|
+ rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize;
|
|
|
|
+ rect.Max.x = rect.Min.x + rect_width;
|
|
|
|
+ rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize;
|
|
|
|
+ rect.Min.y -= bg_offy_up;
|
|
|
|
+ rect.ClipWith(clip_rect);
|
|
|
|
+ draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash.
|
|
|
|
- // FIXME-OPT: Multiline could submit a smaller amount of contents to AddText() since we already iterated through it.
|
|
|
|
- if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
|
|
|
|
- {
|
|
|
|
- ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
|
|
|
|
- if (col & IM_COL32_A_MASK)
|
|
|
|
- g.Font->RenderText(draw_window->DrawList, g.FontSize, draw_pos - draw_scroll, col, clip_rect.AsVec4(), buf_display, buf_display_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
|
|
|
|
- //draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, wrap_width, is_multiline ? NULL : &clip_rect.AsVec4());
|
|
|
|
- }
|
|
|
|
|
|
+ // Find render position for right alignment (single-line only)
|
|
|
|
+ if (g.ActiveId != id && flags & ImGuiInputTextFlags_ElideLeft)
|
|
|
|
+ draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
|
|
|
|
+ //draw_scroll.x = state->Scroll.x; // Preserve scroll when inactive?
|
|
|
|
|
|
- // Draw blinking cursor
|
|
|
|
- if (render_cursor)
|
|
|
|
- {
|
|
|
|
- state->CursorAnim += io.DeltaTime;
|
|
|
|
- bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
|
|
|
|
- ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
|
|
|
|
- ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
|
|
|
|
- if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
|
|
|
|
- draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
|
|
|
|
|
|
+ // Render text
|
|
|
|
+ if ((is_multiline || (buf_display_end - buf_display) < buf_display_max_length) && (text_col & IM_COL32_A_MASK) && (line_visible_n0 < line_visible_n1))
|
|
|
|
+ g.Font->RenderText(draw_window->DrawList, g.FontSize,
|
|
|
|
+ draw_pos - draw_scroll + ImVec2(0.0f, line_visible_n0 * g.FontSize),
|
|
|
|
+ text_col, clip_rect.AsVec4(),
|
|
|
|
+ line_index->get_line_begin(buf_display, line_visible_n0),
|
|
|
|
+ line_index->get_line_end(buf_display, line_visible_n1 - 1),
|
|
|
|
+ wrap_width, ImDrawTextFlags_WrapKeepBlanks);
|
|
|
|
|
|
- // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
|
|
|
|
- // This is required for some backends (SDL3) to start emitting character/text inputs.
|
|
|
|
- // As per #6341, make sure we don't set that on the deactivating frame.
|
|
|
|
- if (!is_readonly && g.ActiveId == id)
|
|
|
|
- {
|
|
|
|
- ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
|
|
|
|
- ime_data->WantVisible = true;
|
|
|
|
- ime_data->WantTextInput = true;
|
|
|
|
- ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
|
|
|
|
- ime_data->InputLineHeight = g.FontSize;
|
|
|
|
- ime_data->ViewportId = window->Viewport->ID;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
|
|
+ // Render blinking cursor
|
|
|
|
+ if (render_cursor)
|
|
{
|
|
{
|
|
- // Render text only (no selection, no cursor)
|
|
|
|
- if (is_multiline)
|
|
|
|
- text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(&g, buf_display, &buf_display_end, wrap_width) * g.FontSize); // We don't need width
|
|
|
|
- else if (!is_displaying_hint && g.ActiveId == id)
|
|
|
|
- buf_display_end = buf_display + state->TextLen;
|
|
|
|
- else if (!is_displaying_hint)
|
|
|
|
- buf_display_end = buf_display + ImStrlen(buf_display);
|
|
|
|
|
|
+ state->CursorAnim += io.DeltaTime;
|
|
|
|
+ bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
|
|
|
|
+ ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
|
|
|
|
+ ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
|
|
|
|
+ if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
|
|
|
|
+ draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
|
|
|
|
|
|
- if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)
|
|
|
|
|
|
+ // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
|
|
|
|
+ // This is required for some backends (SDL3) to start emitting character/text inputs.
|
|
|
|
+ // As per #6341, make sure we don't set that on the deactivating frame.
|
|
|
|
+ if (!is_readonly && g.ActiveId == id)
|
|
{
|
|
{
|
|
- // Find render position for right alignment
|
|
|
|
- if (flags & ImGuiInputTextFlags_ElideLeft)
|
|
|
|
- draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
|
|
|
|
-
|
|
|
|
- const ImVec2 draw_scroll = /*state ? ImVec2(state->Scroll.x, 0.0f) :*/ ImVec2(0.0f, 0.0f); // Preserve scroll when inactive?
|
|
|
|
- ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
|
|
|
|
- if (col & IM_COL32_A_MASK)
|
|
|
|
- g.Font->RenderText(draw_window->DrawList, g.FontSize, draw_pos - draw_scroll, col, clip_rect.AsVec4(), buf_display, buf_display_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
|
|
|
|
- //draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, wrap_width, is_multiline ? NULL : &clip_rect.AsVec4());
|
|
|
|
|
|
+ ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
|
|
|
|
+ ime_data->WantVisible = true;
|
|
|
|
+ ime_data->WantTextInput = true;
|
|
|
|
+ ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
|
|
|
|
+ ime_data->InputLineHeight = g.FontSize;
|
|
|
|
+ ime_data->ViewportId = window->Viewport->ID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -7371,7 +7355,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|
// - (2) usage will fail with clipped items
|
|
// - (2) usage will fail with clipped items
|
|
// The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
|
|
// The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
|
|
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
|
|
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
|
|
- if (g.NavJustMovedToId == id)
|
|
|
|
|
|
+ if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0)
|
|
selected = pressed = auto_selected = true;
|
|
selected = pressed = auto_selected = true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -7424,8 +7408,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|
|
|
|
|
// Automatically close popups
|
|
// Automatically close popups
|
|
if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
|
|
if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
|
|
- if (!(flags & ImGuiSelectableFlags_SelectOnNav) || g.NavJustMovedToId != id)
|
|
|
|
- CloseCurrentPopup();
|
|
|
|
|
|
+ CloseCurrentPopup();
|
|
|
|
|
|
if (disabled_item && !disabled_global)
|
|
if (disabled_item && !disabled_global)
|
|
EndDisabled();
|
|
EndDisabled();
|