@@ -94,11 +94,10 @@ void Console::OnTickVisible()
9494 m_log_filter.Draw (" Filter" , ImGui::GetContentRegionAvail ().x - label_width);
9595 ImGui::Separator ();
9696
97- // calculate split sizes
97+ // calculate split sizes - no longer reserving space for autocomplete (it's a popup now)
9898 const float input_height = ImGui::GetFrameHeightWithSpacing () + ImGui::GetStyle ().ItemSpacing .y ;
99- const float autocomplete_height = m_show_autocomplete ? 200 .0f * spartan::Window::GetDpiScale () : 0 .0f ;
10099 const float available_height = ImGui::GetContentRegionAvail ().y ;
101- const float log_height = available_height - input_height - autocomplete_height ;
100+ const float log_height = available_height - input_height;
102101
103102 // safety first
104103 std::scoped_lock lock (m_mutex);
@@ -193,111 +192,154 @@ void Console::OnTickVisible()
193192 }
194193 }
195194
196- if (m_show_autocomplete && !m_filtered_cvars. empty ())
197- {
198- ImGui::Separator ( );
195+ // input field
196+ ImGui::Separator ();
197+ ImGui::SetNextItemWidth (- 1 . 0f );
199198
200- static const ImGuiTableFlags table_flags =
201- ImGuiTableFlags_RowBg |
202- ImGuiTableFlags_BordersOuter |
203- ImGuiTableFlags_ScrollY |
204- ImGuiTableFlags_Resizable |
205- ImGuiTableFlags_SizingStretchProp;
199+ ImGuiInputTextFlags input_flags =
200+ ImGuiInputTextFlags_EnterReturnsTrue |
201+ ImGuiInputTextFlags_CallbackHistory |
202+ ImGuiInputTextFlags_CallbackAlways;
206203
207- const ImVec2 autocomplete_size = ImVec2 (- 1 . 0f , autocomplete_height) ;
204+ bool reclaim_focus = false ;
208205
209- if (ImGui::BeginTable (" ##console_autocomplete" , 3 , table_flags, autocomplete_size))
210- {
211- ImGui::TableSetupColumn (" Name" , ImGuiTableColumnFlags_WidthStretch, 0 .4f );
212- ImGui::TableSetupColumn (" Value" , ImGuiTableColumnFlags_WidthStretch, 0 .2f );
213- ImGui::TableSetupColumn (" Description" , ImGuiTableColumnFlags_WidthStretch, 0 .4f );
214- ImGui::TableHeadersRow ();
206+ // store input field position for popup placement
207+ ImVec2 input_pos = ImGui::GetCursorScreenPos ();
208+ float input_width = ImGui::GetContentRegionAvail ().x ;
215209
216- for (size_t i = 0 ; i < m_filtered_cvars.size (); i++)
217- {
218- const ConsoleVariable* cvar = ConsoleRegistry::Get ().Find (m_filtered_cvars[i]);
210+ if (ImGui::InputText (" ##console_input" , m_input_buffer, IM_ARRAYSIZE (m_input_buffer), input_flags, [](ImGuiInputTextCallbackData* data) -> int
211+ {
212+ Console* console = static_cast <Console*>(data->UserData );
213+ return console->InputCallback (data);
214+ }, this ))
215+ {
216+ ExecuteCommand (m_input_buffer);
217+ m_input_buffer[0 ] = ' \0 ' ;
218+ m_show_autocomplete = false ;
219+ reclaim_focus = true ;
220+ }
219221
220- ImGui::TableNextRow ();
221- ImGui::PushID (static_cast <int >(i));
222+ bool input_active = ImGui::IsItemActive ();
222223
223- bool is_selected = (std::cmp_equal (i, m_autocomplete_selection));
224- if (is_selected)
225- {
226- ImGui::TableSetBgColor (ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32 (ImGuiCol_HeaderHovered));
227- }
224+ if (input_active)
225+ {
226+ if (m_show_autocomplete && !m_filtered_cvars.empty ())
227+ {
228+ if (ImGui::IsKeyPressed (ImGuiKey_DownArrow))
229+ {
230+ m_autocomplete_selection = (m_autocomplete_selection + 1 ) % static_cast <int >(m_filtered_cvars.size ());
231+ }
232+ else if (ImGui::IsKeyPressed (ImGuiKey_UpArrow))
233+ {
234+ m_autocomplete_selection = (m_autocomplete_selection - 1 + static_cast <int >(m_filtered_cvars.size ())) % static_cast <int >(m_filtered_cvars.size ());
235+ }
236+ else if (ImGui::IsKeyPressed (ImGuiKey_Tab))
237+ {
238+ ApplyAutocomplete ();
239+ reclaim_focus = true ;
240+ }
241+ else if (ImGui::IsKeyPressed (ImGuiKey_Escape))
242+ {
243+ m_show_autocomplete = false ;
244+ }
245+ }
246+ }
228247
229- ImGui::TableSetColumnIndex (0 );
230- if (ImGui::Selectable (std::string (cvar->m_name ).c_str (), is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick))
231- {
232- m_autocomplete_selection = static_cast <int >(i);
233- if (ImGui::IsMouseDoubleClicked (0 ))
234- {
235- ApplyAutocomplete ();
236- }
237- }
248+ if (ImGui::IsWindowAppearing () || reclaim_focus)
249+ {
250+ ImGui::SetKeyboardFocusHere (-1 );
251+ }
238252
239- ImGui::TableSetColumnIndex (1 );
240- std::string value_str = ConsoleRegistry::Get ().GetValueAsString (cvar->m_name ).value ();
241- ImGui::TextUnformatted (value_str.c_str ());
253+ // autocomplete popup - rendered as a floating window above the input
254+ if (m_show_autocomplete && !m_filtered_cvars.empty ())
255+ {
256+ const float popup_max_height = 250 .0f * spartan::Window::GetDpiScale ();
257+ const float row_height = ImGui::GetTextLineHeightWithSpacing ();
258+ const float header_height = row_height + ImGui::GetStyle ().ItemSpacing .y ;
259+ const float content_height = std::min (popup_max_height, header_height + row_height * static_cast <float >(m_filtered_cvars.size ()));
260+
261+ // position popup above the input field
262+ ImVec2 popup_pos = ImVec2 (input_pos.x , input_pos.y - content_height - ImGui::GetStyle ().ItemSpacing .y );
263+
264+ ImGui::SetNextWindowPos (popup_pos);
265+ ImGui::SetNextWindowSize (ImVec2 (input_width, content_height));
266+ ImGui::SetNextWindowBgAlpha (0 .92f );
267+
268+ ImGuiWindowFlags popup_flags =
269+ ImGuiWindowFlags_NoTitleBar |
270+ ImGuiWindowFlags_NoResize |
271+ ImGuiWindowFlags_NoMove |
272+ ImGuiWindowFlags_NoSavedSettings |
273+ ImGuiWindowFlags_NoFocusOnAppearing;
274+
275+ ImGui::PushStyleVar (ImGuiStyleVar_WindowRounding, 4 .0f );
276+ ImGui::PushStyleVar (ImGuiStyleVar_WindowBorderSize, 1 .0f );
277+ ImGui::PushStyleColor (ImGuiCol_Border, ImVec4 (0 .5f , 0 .5f , 0 .5f , 0 .5f ));
278+
279+ if (ImGui::Begin (" ##console_autocomplete_popup" , nullptr , popup_flags))
280+ {
281+ static const ImGuiTableFlags table_flags =
282+ ImGuiTableFlags_RowBg |
283+ ImGuiTableFlags_ScrollY |
284+ ImGuiTableFlags_SizingStretchProp;
242285
243- ImGui::TableSetColumnIndex (2 );
244- ImGui::TextWrapped (" %s" , std::string (cvar->m_hint ).c_str ());
286+ if (ImGui::BeginTable (" ##console_autocomplete" , 3 , table_flags))
287+ {
288+ ImGui::TableSetupColumn (" Name" , ImGuiTableColumnFlags_WidthStretch, 0 .4f );
289+ ImGui::TableSetupColumn (" Value" , ImGuiTableColumnFlags_WidthStretch, 0 .2f );
290+ ImGui::TableSetupColumn (" Description" , ImGuiTableColumnFlags_WidthStretch, 0 .4f );
291+ ImGui::TableHeadersRow ();
245292
246- ImGui::PopID ();
247- }
293+ for (size_t i = 0 ; i < m_filtered_cvars.size (); i++)
294+ {
295+ const ConsoleVariable* cvar = ConsoleRegistry::Get ().Find (m_filtered_cvars[i]);
248296
249- ImGui::EndTable ();
250- }
251- }
297+ ImGui::TableNextRow ();
298+ ImGui::PushID (static_cast <int >(i));
252299
253- {
254- ImGui::Separator ();
300+ bool is_selected = (std::cmp_equal (i, m_autocomplete_selection));
301+ if (is_selected)
302+ {
303+ ImGui::TableSetBgColor (ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32 (ImGuiCol_HeaderHovered));
304+ }
255305
256- ImGui::SetNextItemWidth (-1 .0f );
306+ ImGui::TableSetColumnIndex (0 );
307+ if (ImGui::Selectable (std::string (cvar->m_name ).c_str (), is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick))
308+ {
309+ m_autocomplete_selection = static_cast <int >(i);
310+ if (ImGui::IsMouseDoubleClicked (0 ))
311+ {
312+ ApplyAutocomplete ();
313+ }
314+ }
257315
258- ImGuiInputTextFlags input_flags =
259- ImGuiInputTextFlags_EnterReturnsTrue |
260- ImGuiInputTextFlags_CallbackHistory |
261- ImGuiInputTextFlags_CallbackAlways;
316+ // scroll to keep selected item visible
317+ if (is_selected && ImGui::IsKeyPressed (ImGuiKey_DownArrow))
318+ {
319+ ImGui::SetScrollHereY (0 .5f );
320+ }
321+ if (is_selected && ImGui::IsKeyPressed (ImGuiKey_UpArrow))
322+ {
323+ ImGui::SetScrollHereY (0 .5f );
324+ }
262325
263- bool reclaim_focus = false ;
326+ ImGui::TableSetColumnIndex (1 );
327+ std::string value_str = ConsoleRegistry::Get ().GetValueAsString (cvar->m_name ).value ();
328+ ImGui::TextUnformatted (value_str.c_str ());
264329
265- if (ImGui::InputText (" ##console_input" , m_input_buffer, IM_ARRAYSIZE (m_input_buffer), input_flags, [](ImGuiInputTextCallbackData* data) -> int
266- {
267- Console* console = static_cast <Console*>(data->UserData );
268- return console->InputCallback (data);
269- }, this ))
270- {
271- ExecuteCommand (m_input_buffer);
272- m_input_buffer[0 ] = ' \0 ' ;
273- m_show_autocomplete = false ;
274- reclaim_focus = true ;
275- }
330+ ImGui::TableSetColumnIndex (2 );
331+ ImGui::TextWrapped (" %s" , std::string (cvar->m_hint ).c_str ());
276332
277- if (ImGui::IsItemActive ())
278- {
279- if (m_show_autocomplete && !m_filtered_cvars.empty ())
280- {
281- if (ImGui::IsKeyPressed (ImGuiKey_DownArrow))
282- {
283- m_autocomplete_selection = (m_autocomplete_selection + 1 ) % static_cast <int >(m_filtered_cvars.size ());
284- }
285- else if (ImGui::IsKeyPressed (ImGuiKey_UpArrow))
286- {
287- m_autocomplete_selection = (m_autocomplete_selection - 1 + static_cast <int >(m_filtered_cvars.size ())) % static_cast <int >(m_filtered_cvars.size ());
288- }
289- else if (ImGui::IsKeyPressed (ImGuiKey_Tab))
290- {
291- ApplyAutocomplete ();
292- reclaim_focus = true ;
333+ ImGui::PopID ();
293334 }
335+
336+ ImGui::EndTable ();
294337 }
295338 }
339+ ImGui::End ();
296340
297- if (ImGui::IsWindowAppearing () || reclaim_focus)
298- {
299- ImGui::SetKeyboardFocusHere (-1 );
300- }
341+ ImGui::PopStyleColor ();
342+ ImGui::PopStyleVar (2 );
301343 }
302344}
303345
0 commit comments