diff --git a/src/cata_imgui.cpp b/src/cata_imgui.cpp index efd34887a4ace..42228fbf5f8cd 100644 --- a/src/cata_imgui.cpp +++ b/src/cata_imgui.cpp @@ -10,6 +10,7 @@ #include "input.h" #include "output.h" #include "ui_manager.h" +#include "input_context.h" static ImGuiKey cata_key_to_imgui( int cata_key ); @@ -461,6 +462,13 @@ class cataimgui::window_impl } }; +class cataimgui::filter_box_impl +{ + public: + std::array text; + ImGuiID id; +}; + cataimgui::window::window( int window_flags ) { p_impl = nullptr; @@ -599,3 +607,51 @@ cataimgui::bounds cataimgui::window::get_bounds() { return { -1.f, -1.f, -1.f, -1.f }; } + +void cataimgui::window::draw_filter( const input_context &ctxt, bool filtering_active ) +{ + if( !filter_impl ) { + filter_impl = std::make_unique(); + filter_impl->id = 0; + filter_impl->text[0] = '\0'; + } + + if( !filtering_active ) { + action_button( "FILTER", ctxt.get_button_text( "FILTER" ) ); + ImGui::SameLine(); + action_button( "RESET_FILTER", ctxt.get_button_text( "RESET_FILTER" ) ); + ImGui::SameLine(); + } else { + action_button( "QUIT", ctxt.get_button_text( "QUIT", _( "Cancel" ) ) ); + ImGui::SameLine(); + action_button( "TEXT.CONFIRM", ctxt.get_button_text( "TEXT.CONFIRM", _( "OK" ) ) ); + ImGui::SameLine(); + } + ImGui::BeginDisabled( !filtering_active ); + ImGui::InputText( "##FILTERBOX", filter_impl->text.data(), + filter_impl->text.size() ); + ImGui::EndDisabled(); + if( !filter_impl->id ) { + filter_impl->id = GImGui->LastItemData.ID; + } +} + +std::string cataimgui::window::get_filter() +{ + if( filter_impl ) { + return std::string( filter_impl->text.data() ); + } else { + return std::string(); + } +} + +void cataimgui::window::clear_filter() +{ + if( filter_impl && filter_impl->id != 0 ) { + ImGuiInputTextState *input_state = ImGui::GetInputTextState( filter_impl->id ); + if( input_state ) { + input_state->ClearText(); + filter_impl->text[0] = '\0'; + } + } +} diff --git a/src/cata_imgui.h b/src/cata_imgui.h index 4f9707993d804..50db9dbe4e519 100644 --- a/src/cata_imgui.h +++ b/src/cata_imgui.h @@ -18,6 +18,7 @@ struct item_info_data; struct point; class ImVec2; class Font; +class input_context; namespace cataimgui { @@ -78,6 +79,7 @@ void imvec2_to_point( ImVec2 *src, point *dest ); class window { std::unique_ptr p_impl; + std::unique_ptr filter_impl; bounds cached_bounds; protected: explicit window( int window_flags = 0 ); @@ -102,6 +104,8 @@ class window size_t get_text_height( const char *text ); size_t str_width_to_pixels( size_t len ); size_t str_height_to_pixels( size_t len ); + std::string get_filter(); + void clear_filter(); void mark_resized(); protected: @@ -112,6 +116,7 @@ class window std::string button_action; virtual bounds get_bounds(); virtual void draw_controls() = 0; + void draw_filter( const input_context &ctxt, bool filtering_active ); }; #if !(defined(TILES) || defined(WIN32)) diff --git a/src/input_context.cpp b/src/input_context.cpp index 7569703ba4758..ce36e2caf2220 100644 --- a/src/input_context.cpp +++ b/src/input_context.cpp @@ -35,7 +35,7 @@ #include "imgui/imgui.h" enum class kb_menu_status { - remove, reset, add, add_global, execute, show + remove, reset, add, add_global, execute, show, filter }; class keybindings_ui : public cataimgui::window @@ -48,7 +48,6 @@ class keybindings_ui : public cataimgui::window const nc_color unbound_key = c_light_red; const nc_color h_unbound_key = h_light_red; input_context *ctxt; - char filter_text_impl[255]; // NOLINT(modernize-avoid-c-arrays) public: // current status: adding/removing/reseting/executing/showing keybindings kb_menu_status status = kb_menu_status::show, last_status = kb_menu_status::execute; @@ -61,7 +60,7 @@ class keybindings_ui : public cataimgui::window std::string hotkeys; int highlight_row_index = -1; size_t scroll_offset = 0; - std::string filter_text; + //std::string filter_text; keybindings_ui( bool permit_execute_action, input_context *parent ); void init(); @@ -321,6 +320,44 @@ std::string input_context::get_desc( const std::string &action_descriptor, return rval; } + +std::string input_context::get_button_text( const std::string &action_descriptor ) const +{ + std::string action_name = get_action_name( action_descriptor ); + if( action_name.empty() ) { + action_name = action_descriptor; + } + return get_button_text( action_descriptor, action_name ); +} + +std::string input_context::get_button_text( const std::string &action_descriptor, + const std::string &action_text ) const +{ + if( action_descriptor == "ANY_INPUT" ) { + return ""; // what sort of crazy button would this be? + } + + bool is_local = false; + const std::vector &events = inp_mngr.get_input_for_action( action_descriptor, + category, &is_local ); + if( events.empty() ) { + return action_text; + } + + std::vector inputs_to_show; + for( const input_event &event : events ) { + if( is_event_type_enabled( event.type ) ) { + inputs_to_show.push_back( event ); + } + } + + if( inputs_to_show.empty() ) { + return action_text; + } else { + return string_format( "[%s] %s", inputs_to_show[0].long_description(), action_text ); + } +} + std::string input_context::get_desc( const std::string &action_descriptor, const std::string &text, @@ -608,7 +645,6 @@ static const std::map fallback_keys = { keybindings_ui::keybindings_ui( bool permit_execute_action, input_context *parent ) : cataimgui::window( "KEYBINDINGS", ImGuiWindowFlags_NoNav ) { - filter_text_impl[0] = '\0'; this->ctxt = parent; legend.push_back( colorize( _( "Unbound keys" ), unbound_key ) ); @@ -655,23 +691,24 @@ void keybindings_ui::draw_controls() ImGui::SetCursorPosX( str_width_to_pixels( width ) - ( get_text_width( button_text_no_color ) + ( ImGui::GetStyle().FramePadding.x * 2 ) + ImGui::GetStyle().ItemSpacing.x ) ); + ImGui::BeginDisabled( status == kb_menu_status::filter ); action_button( buttons[legend_idx].first, button_text_no_color ); + ImGui::EndDisabled(); } for( ; legend_idx < legend.size(); legend_idx++ ) { draw_colored_text( legend[legend_idx], c_white ); } - if( last_status != status && status == kb_menu_status::show ) { - ImGui::SetKeyboardFocusHere( 0 ); + draw_filter( *ctxt, status == kb_menu_status::filter ); + if( last_status != status && status == kb_menu_status::filter ) { + ImGui::SetKeyboardFocusHere( -1 ); } - strncpy( filter_text_impl, filter_text.c_str(), filter_text.length() ); - ImGui::InputText( "##NOLABEL", filter_text_impl, std::extent_v< decltype( filter_text_impl )>, - status == kb_menu_status::show ? ImGuiInputTextFlags_None : ImGuiInputTextFlags_ReadOnly ); - filter_text.assign( filter_text_impl ); ImGui::Separator(); + + if( last_status != status && status == kb_menu_status::show ) { + ImGui::SetNextWindowFocus(); + } if( ImGui::BeginTable( "KB_KEYS", 2, ImGuiTableFlags_ScrollY ) ) { - if( last_status != status && status != kb_menu_status::show ) { - ImGui::SetKeyboardFocusHere( 0 ); - } + ImGui::TableSetupColumn( "Action Name", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_NoSort ); float keys_col_width = str_width_to_pixels( width ) - str_width_to_pixels( TERMX >= 100 ? 62 : 52 ); @@ -1299,10 +1336,13 @@ action_id input_context::display_menu_imgui( const bool permit_execute_action ) ctxt.register_action( "ADD_LOCAL" ); ctxt.register_action( "ADD_GLOBAL" ); ctxt.register_action( "TEXT.CLEAR" ); + ctxt.register_action( "TEXT.CONFIRM" ); ctxt.register_action( "PAGE_UP" ); ctxt.register_action( "PAGE_DOWN" ); ctxt.register_action( "END" ); ctxt.register_action( "HOME" ); + ctxt.register_action( "FILTER" ); + ctxt.register_action( "RESET_FILTER" ); ctxt.register_action( "TEXT.INPUT_FROM_FILE" ); if( permit_execute_action ) { ctxt.register_action( "EXECUTE" ); @@ -1357,13 +1397,19 @@ action_id input_context::display_menu_imgui( const bool permit_execute_action ) } kb_menu.filtered_registered_actions = filter_strings_by_phrase( org_registered_actions, - kb_menu.filter_text ); + kb_menu.get_filter() ); // In addition to the modifiable hotkeys, we also check for hardcoded // keys, e.g. '+', '-', '=', '.' in order to prevent the user from // entering an unrecoverable state. - if( action == "ADD_LOCAL" - || raw_input_char == fallback_keys.at( fallback_action::add_local ) ) { + if( kb_menu.status == kb_menu_status::filter ) { + if( action == "QUIT" ) { + kb_menu.clear_filter( ); + kb_menu.status = kb_menu_status::show; + } else if( action == "TEXT.CONFIRM" ) { + kb_menu.status = kb_menu_status::show; + } + } else if( action == "ADD_LOCAL" ) { if( !kb_menu.filtered_registered_actions.empty() ) { kb_menu.status = kb_menu_status::add; } @@ -1391,19 +1437,21 @@ action_id input_context::display_menu_imgui( const bool permit_execute_action ) } else if( action == "PAGE_UP" || action == "PAGE_DOWN" || action == "HOME" || action == "END" ) { continue; // do nothing - on tiles version for some reason this counts as pressing various alphabet keys } else if( action == "TEXT.CLEAR" ) { - kb_menu.filter_text.assign( "" ); + kb_menu.clear_filter(); kb_menu.filtered_registered_actions = filter_strings_by_phrase( org_registered_actions, "" ); } else if( !kb_menu.get_is_open() ) { break; + } else if( action == "FILTER" ) { + kb_menu.status = kb_menu_status::filter; + } else if( action == "RESET_FILTER" ) { + kb_menu.clear_filter(); } else if( action == "QUIT" ) { if( kb_menu.status != kb_menu_status::show ) { kb_menu.status = kb_menu_status::show; } else { break; } - } else if( action == "TEXT.INPUT_FROM_FILE" ) { - kb_menu.filter_text += get_input_string_from_file(); } else if( action == "HELP_KEYBINDINGS" ) { // update available hotkeys in case they've changed kb_menu.hotkeys = ctxt.get_available_single_char_hotkeys( display_help_hotkeys ); diff --git a/src/input_context.h b/src/input_context.h index b88624b4519c1..544104496bb95 100644 --- a/src/input_context.h +++ b/src/input_context.h @@ -222,6 +222,27 @@ class input_context unsigned int max_limit = 0, const input_event_filter &evt_filter = allow_all_keys ) const; + /** + * Get a description for the action parameter along with a printable hotkey + * for the description in the format [%s] %s + * + * @param action_descriptor The action descriptor for which to return + * a description of the bound keys. + */ + std::string get_button_text( const std::string &action_descriptor ) const; + + /** + * Get a description for the action parameter along with a printable hotkey + * for the description in the format [%s] %s + * + * @param action_descriptor The action descriptor for which to return + * a description of the bound keys. + * + * @param action_text The human readable description of the action. + */ + std::string get_button_text( const std::string &action_descriptor, + const std::string &action_text ) const; + /** * Get a description based on `text`. If a bound key for `action_descriptor` * satisfying `evt_filter` is contained in `text`, surround the key with