@ -3174,7 +3174,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
ImGuiIO & io = g . IO ;
ImGuiIO & io = g . IO ;
const ImGuiStyle & style = g . Style ;
const ImGuiStyle & style = g . Style ;
const bool RENDER_SELECTION_WHEN_INACTIVE = tru e;
const bool RENDER_SELECTION_WHEN_INACTIVE = fals e;
const bool is_multiline = ( flags & ImGuiInputTextFlags_Multiline ) ! = 0 ;
const bool is_multiline = ( flags & ImGuiInputTextFlags_Multiline ) ! = 0 ;
const bool is_readonly = ( flags & ImGuiInputTextFlags_ReadOnly ) ! = 0 ;
const bool is_readonly = ( flags & ImGuiInputTextFlags_ReadOnly ) ! = 0 ;
const bool is_password = ( flags & ImGuiInputTextFlags_Password ) ! = 0 ;
const bool is_password = ( flags & ImGuiInputTextFlags_Password ) ! = 0 ;
@ -3255,7 +3255,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
bool select_all = ( g . ActiveId ! = id ) & & ( ( flags & ImGuiInputTextFlags_AutoSelectAll ) ! = 0 | | user_nav_input_start ) & & ( ! is_multiline ) ;
bool select_all = ( g . ActiveId ! = id ) & & ( ( flags & ImGuiInputTextFlags_AutoSelectAll ) ! = 0 | | user_nav_input_start ) & & ( ! is_multiline ) ;
const bool init_make_active = ( focus_requested | | user_clicked | | user_scroll_finish | | user_nav_input_start ) ;
const bool init_make_active = ( focus_requested | | user_clicked | | user_scroll_finish | | user_nav_input_start ) ;
if ( init_make_active & & g . ActiveId ! = id )
const bool init_state = ( init_make_active | | user_scroll_active ) ;
if ( init_state & & g . ActiveId ! = id )
{
{
// Access state even if we don't own it yet.
// Access state even if we don't own it yet.
state = & g . InputTextState ;
state = & g . InputTextState ;
@ -3268,15 +3269,16 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
memcpy ( state - > InitialTextA . Data , buf , buf_len + 1 ) ;
memcpy ( state - > InitialTextA . Data , buf , buf_len + 1 ) ;
// Start edition
// Start edition
const int prev_len_w = state - > CurLenW ;
const char * buf_end = NULL ;
const char * buf_end = NULL ;
state - > TextW . resize ( buf_size + 1 ) ; // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string.
state - > TextW . resize ( buf_size + 1 ) ; // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string.
state - > TextA . resize ( 0 ) ;
state - > TextAIsValid = false ; // TextA is not valid yet (we will display buf until then)
state - > CurLenW = ImTextStrFromUtf8 ( state - > TextW . Data , buf_size , buf , NULL , & buf_end ) ;
state - > CurLenW = ImTextStrFromUtf8 ( state - > TextW . Data , buf_size , buf , NULL , & buf_end ) ;
state - > CurLenA = ( int ) ( buf_end - buf ) ; // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
state - > CurLenA = ( int ) ( buf_end - buf ) ; // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.
// Preserve cursor position and undo/redo stack if we come back to same widget
// Preserve cursor position and undo/redo stack if we come back to same widget
// FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar) .
// FIXME: For non-readonly widgets we might be able to require that TextAIsValid && TextA == buf ? (untested) and discard undo stack if user buffer has changed .
const bool recycle_state = ( state - > ID = = id ) & & ( prev_len_w = = state - > CurLenW ) ;
const bool recycle_state = ( state - > ID = = id ) ;
if ( recycle_state )
if ( recycle_state )
{
{
// Recycle existing cursor/selection/undo stack but clamp position
// Recycle existing cursor/selection/undo stack but clamp position
@ -3297,7 +3299,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
select_all = true ;
select_all = true ;
}
}
if ( init_make_active)
if ( g. ActiveId ! = id & & init_make_active)
{
{
IM_ASSERT ( state & & state - > ID = = id ) ;
IM_ASSERT ( state & & state - > ID = = id ) ;
SetActiveID ( id , window ) ;
SetActiveID ( id , window ) ;
@ -3308,32 +3310,38 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
g . ActiveIdAllowNavDirFlags = ( ( 1 < < ImGuiDir_Up ) | ( 1 < < ImGuiDir_Down ) ) ;
g . ActiveIdAllowNavDirFlags = ( ( 1 < < ImGuiDir_Up ) | ( 1 < < ImGuiDir_Down ) ) ;
}
}
// Release focus when we click outside
// 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)
if ( ! init_make_active & & io . MouseClicked [ 0 ] )
clear_active_id = true ;
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped)
if ( g . ActiveId = = id & & state = = NULL )
if ( g . ActiveId = = id & & state = = NULL )
ClearActiveID ( ) ;
ClearActiveID ( ) ;
// Release focus when we click outside
if ( g . ActiveId = = id & & io . MouseClicked [ 0 ] & & ! init_state & & ! init_make_active )
clear_active_id = true ;
bool value_changed = false ;
bool value_changed = false ;
bool enter_pressed = false ;
bool enter_pressed = false ;
int backup_current_text_length = 0 ;
int backup_current_text_length = 0 ;
// Process mouse inputs and character inputs
// When read-only we always use the live data passed to the function
if ( g . ActiveId = = id )
// FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :(
if ( is_readonly & & state ! = NULL )
{
{
IM_ASSERT ( state ! = NULL ) ;
const bool will_render_cursor = ( g . ActiveId = = id ) | | ( state & & user_scroll_active ) ;
if ( is_readonly & & ! g . ActiveIdIsJustActivated )
const bool will_render_selection = state & & state - > HasSelection ( ) & & ( RENDER_SELECTION_WHEN_INACTIVE | | will_render_cursor ) ;
if ( will_render_cursor | | will_render_selection )
{
{
// When read-only we always use the live data passed to the function
const char * buf_end = NULL ;
const char * buf_end = NULL ;
state - > TextW . resize ( buf_size + 1 ) ;
state - > TextW . resize ( buf_size + 1 ) ;
state - > CurLenW = ImTextStrFromUtf8 ( state - > TextW . Data , state - > TextW . Size , buf , NULL , & buf_end ) ;
state - > CurLenW = ImTextStrFromUtf8 ( state - > TextW . Data , state - > TextW . Size , buf , NULL , & buf_end ) ;
state - > CurLenA = ( int ) ( buf_end - buf ) ;
state - > CurLenA = ( int ) ( buf_end - buf ) ;
state - > CursorClamp ( ) ;
state - > CursorClamp ( ) ;
}
}
}
// Process mouse inputs and character inputs
if ( g . ActiveId = = id )
{
IM_ASSERT ( state ! = NULL ) ;
backup_current_text_length = state - > CurLenA ;
backup_current_text_length = state - > CurLenA ;
state - > BufCapacityA = buf_size ;
state - > BufCapacityA = buf_size ;
state - > UserFlags = flags ;
state - > UserFlags = flags ;
@ -3546,6 +3554,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
// FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
if ( ! is_readonly )
if ( ! is_readonly )
{
{
state - > TextAIsValid = true ;
state - > TextA . resize ( state - > TextW . Size * 4 + 1 ) ;
state - > TextA . resize ( state - > TextW . Size * 4 + 1 ) ;
ImTextStrToUtf8 ( state - > TextA . Data , state - > TextA . Size , state - > TextW . Data , NULL ) ;
ImTextStrToUtf8 ( state - > TextA . Data , state - > TextA . Size , state - > TextW . Data , NULL ) ;
}
}
@ -3676,15 +3685,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
// without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
// Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.
// Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.
const int buf_display_max_length = 2 * 1024 * 1024 ;
const int buf_display_max_length = 2 * 1024 * 1024 ;
const char * buf_display = NULL ;
// Select which buffer we are going to display. We set buf to NULL to prevent accidental usage from now on.
const char * buf_display_end = NULL ;
const char * buf_display = ( g . ActiveId = = id & & state & & ! is_readonly ) ? state - > TextA . Data : buf ;
IM_ASSERT ( buf_display ) ;
buf = NULL ;
// 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: We could remove the '&& render_cursor' to keep rendering selection when inactive.
// FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive.
const bool render_cursor = ( g . ActiveId = = id ) | | user_scroll_active ;
const bool render_cursor = ( g . ActiveId = = id ) | | ( state & & user_scroll_active ) ;
const bool render_selection = state & & state - > HasSelection ( ) & & ( RENDER_SELECTION_WHEN_INACTIVE | | render_cursor ) ;
const bool render_selection = state & & state - > HasSelection ( ) & & ( RENDER_SELECTION_WHEN_INACTIVE | | render_cursor ) ;
if ( render_cursor | | render_selection )
if ( render_cursor | | render_selection )
{
{
@ -3820,9 +3826,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
}
}
// 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.
// 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.
const int buf_display_len = state - > CurLenA ;
buf_display = ( ! is_readonly & & state - > TextAIsValid ) ? state - > TextA . Data : buf ;
if ( is_multiline | | buf_display_len < buf_display_max_length )
buf_display_end = buf_display + state - > CurLenA ;
draw_window - > DrawList - > AddText ( g . Font , g . FontSize , draw_pos - draw_scroll , GetColorU32 ( ImGuiCol_Text ) , buf_display , buf_display + buf_display_len , 0.0f , is_multiline ? NULL : & clip_rect ) ;
if ( is_multiline | | ( buf_display_end - buf_display ) < buf_display_max_length )
draw_window - > DrawList - > AddText ( g . Font , g . FontSize , draw_pos - draw_scroll , GetColorU32 ( ImGuiCol_Text ) , buf_display , buf_display_end , 0.0f , is_multiline ? NULL : & clip_rect ) ;
// Draw blinking cursor
// Draw blinking cursor
if ( render_cursor )
if ( render_cursor )
@ -3845,13 +3852,15 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
else
else
{
{
// Render text only (no selection, no cursor)
// Render text only (no selection, no cursor)
const char * buf_end = NULL ;
buf_display = ( g . ActiveId = = id & & ! is_readonly & & state - > TextAIsValid ) ? state - > TextA . Data : buf ;
if ( is_multiline )
if ( is_multiline )
text_size = ImVec2 ( size . x , InputTextCalcTextLenAndLineCount ( buf_display , & buf_end ) * g . FontSize ) ; // We don't need width
text_size = ImVec2 ( size . x , InputTextCalcTextLenAndLineCount ( buf_display , & buf_display_end ) * g . FontSize ) ; // We don't need width
else if ( g . ActiveId = = id )
buf_display_end = buf_display + state - > CurLenA ;
else
else
buf_end = buf_display + strlen ( buf_display ) ;
buf_ display_ end = buf_display + strlen ( buf_display ) ;
if ( is_multiline | | ( buf_ end - buf_display ) < buf_display_max_length )
if ( is_multiline | | ( buf_ display_ end - buf_display ) < buf_display_max_length )
draw_window - > DrawList - > AddText ( g . Font , g . FontSize , draw_pos , GetColorU32 ( ImGuiCol_Text ) , buf_display , buf_ end, 0.0f , is_multiline ? NULL : & clip_rect ) ;
draw_window - > DrawList - > AddText ( g . Font , g . FontSize , draw_pos , GetColorU32 ( ImGuiCol_Text ) , buf_display , buf_ display_ end, 0.0f , is_multiline ? NULL : & clip_rect ) ;
}
}
if ( is_multiline )
if ( is_multiline )
@ -3866,7 +3875,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Log as text
// Log as text
if ( g . LogEnabled & & ! is_password )
if ( g . LogEnabled & & ! is_password )
LogRenderedText ( & draw_pos , buf_display , NULL ) ;
LogRenderedText ( & draw_pos , buf_display , buf_display_end ) ;
if ( label_size . x > 0 )
if ( label_size . x > 0 )
RenderText ( ImVec2 ( frame_bb . Max . x + style . ItemInnerSpacing . x , frame_bb . Min . y + style . FramePadding . y ) , label ) ;
RenderText ( ImVec2 ( frame_bb . Max . x + style . ItemInnerSpacing . x , frame_bb . Min . y + style . FramePadding . y ) , label ) ;