@@ -4507,12 +4507,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
4507
4507
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
4508
4508
const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
4509
4509
const bool init_state = (init_make_active || user_scroll_active);
4510
+ bool readonly_swapped_text_data = false;
4510
4511
if (init_reload_from_user_buf)
4511
4512
{
4512
4513
int new_len = (int)strlen(buf);
4513
4514
state->WantReloadUserBuf = false;
4514
4515
InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
4515
- state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
4516
+ state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
4516
4517
state->TextLen = new_len;
4517
4518
memcpy(state->TextA.Data, buf, state->TextLen + 1);
4518
4519
state->Stb->select_start = state->ReloadSelectionStart;
@@ -4537,14 +4538,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
4537
4538
// Preserve cursor position and undo/redo stack if we come back to same widget
4538
4539
// FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
4539
4540
bool recycle_state = (state->ID == id && !init_changed_specs);
4540
- if (recycle_state && (state->TextLen != buf_len || (strncmp(state->TextA.Data, buf, buf_len) != 0)))
4541
+ if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
4541
4542
recycle_state = false;
4542
4543
4543
4544
// Start edition
4544
4545
state->ID = id;
4545
- state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
4546
4546
state->TextLen = (int)strlen(buf);
4547
- memcpy(state->TextA.Data, buf, state->TextLen + 1);
4547
+ if (!is_readonly)
4548
+ {
4549
+ state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
4550
+ memcpy(state->TextA.Data, buf, state->TextLen + 1);
4551
+ }
4548
4552
4549
4553
// Find initial scroll position for right alignment
4550
4554
state->Scroll = ImVec2(0.0f, 0.0f);
@@ -4610,6 +4614,21 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
4610
4614
state->Scroll.y = draw_window->Scroll.y;
4611
4615
}
4612
4616
4617
+ if (g.ActiveId == id && is_readonly)
4618
+ {
4619
+ // FIXME: Refresh buffer because cursor/selection code uses that data (see repro #8242)
4620
+ // The "simple" way would be to copy buf into state->TextA, like in the block above.
4621
+ // But because we like to live dangerously, we do a little swap....
4622
+ // Removing the swap and only doing a TextA.clear() is a way to identify who's using TextA.Data.
4623
+ state->TextLen = (int)strlen(buf);
4624
+ state->TextA.clear();
4625
+ state->TextA.Data = buf; // Ouch
4626
+ state->TextA.Size = state->TextLen + 1;
4627
+ readonly_swapped_text_data = true; // Need to always ensure that every code path below lead to this being handled
4628
+ //state->TextA.resize(buf_size + 1);
4629
+ //memcpy(state->TextA.Data, buf, state->TextLen + 1);
4630
+ }
4631
+
4613
4632
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
4614
4633
if (g.ActiveId == id && state == NULL)
4615
4634
ClearActiveID();
@@ -5008,10 +5027,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5008
5027
callback_data.UserData = callback_user_data;
5009
5028
5010
5029
// FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
5030
+ char* callback_buf = is_readonly ? buf : state->TextA.Data;
5031
+
5011
5032
state->CallbackTextBackup.resize(state->TextLen + 1);
5012
- memcpy(state->CallbackTextBackup.Data, state->TextA.Data , state->TextLen + 1);
5033
+ memcpy(state->CallbackTextBackup.Data, callback_buf , state->TextLen + 1);
5013
5034
5014
- char* callback_buf = is_readonly ? buf : state->TextA.Data;
5015
5035
callback_data.EventKey = event_key;
5016
5036
callback_data.Buf = callback_buf;
5017
5037
callback_data.BufTextLen = state->TextLen;
@@ -5142,7 +5162,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5142
5162
// - Measure text height (for scrollbar)
5143
5163
// 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)
5144
5164
// FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
5145
- const char* text_begin = state->TextA.Data ;
5165
+ const char* text_begin = buf_display ;
5146
5166
const char* text_end = text_begin + state->TextLen;
5147
5167
ImVec2 cursor_offset, select_start_offset;
5148
5168
@@ -5304,6 +5324,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
5304
5324
if (is_password && !is_displaying_hint)
5305
5325
PopFont();
5306
5326
5327
+ if (readonly_swapped_text_data)
5328
+ {
5329
+ IM_ASSERT(state->TextA.Data == buf);
5330
+ state->TextA.Size = 0;
5331
+ state->TextA.Data = NULL;
5332
+ }
5333
+
5307
5334
if (is_multiline)
5308
5335
{
5309
5336
// For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...
0 commit comments