From 198e3ac4116ce823db526a350dd8db28167338dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jianxiang=20Wang=20=28=E7=8E=8B=E5=81=A5=E7=BF=94=29?= Date: Thu, 9 Apr 2020 16:29:12 +0800 Subject: [PATCH] Migrate auto note manager and bionics menu to ui_adaptor (#39395) * Use ui_adaptor in auto note manager * Use ui_adaptor in bionics menu --- src/auto_note.cpp | 101 +++++++-------- src/bionics_ui.cpp | 304 +++++++++++++++++++++------------------------ src/game.cpp | 24 ++++ src/game.h | 6 + src/main.cpp | 9 +- src/ui_manager.cpp | 60 +++++++-- src/ui_manager.h | 7 ++ 7 files changed, 281 insertions(+), 230 deletions(-) diff --git a/src/auto_note.cpp b/src/auto_note.cpp index f119352f9368d..c47c76a7d748f 100644 --- a/src/auto_note.cpp +++ b/src/auto_note.cpp @@ -174,62 +174,37 @@ bool auto_note_manager_gui::was_changed() const void auto_note_manager_gui::show() { const int iHeaderHeight = 3; - const int iContentHeight = FULL_SCREEN_HEIGHT - 2 - iHeaderHeight; + int iContentHeight = 0; + catacurses::window w_border; + catacurses::window w_header; + catacurses::window w; - const int iOffsetX = TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0; - const int iOffsetY = TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0; + ui_adaptor ui; + ui.on_screen_resize( [&]( ui_adaptor & ui ) { + iContentHeight = FULL_SCREEN_HEIGHT - 2 - iHeaderHeight; - catacurses::window w_help = catacurses::newwin( FULL_SCREEN_HEIGHT / 2 + 2, - FULL_SCREEN_WIDTH * 3 / 4, - point( iOffsetX + 19 / 2, 7 + iOffsetY + FULL_SCREEN_HEIGHT / 2 / 2 ) ); + const int iOffsetX = TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0; + const int iOffsetY = TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0; - catacurses::window w_border = catacurses::newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, - point( iOffsetX, iOffsetY ) ); + w_border = catacurses::newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, + point( iOffsetX, iOffsetY ) ); - catacurses::window w_header = catacurses::newwin( iHeaderHeight, FULL_SCREEN_WIDTH - 2, - point( 1 + iOffsetX, 1 + iOffsetY ) ); + w_header = catacurses::newwin( iHeaderHeight, FULL_SCREEN_WIDTH - 2, + point( 1 + iOffsetX, 1 + iOffsetY ) ); - catacurses::window w = catacurses::newwin( iContentHeight, FULL_SCREEN_WIDTH - 2, - point( 1 + iOffsetX, iHeaderHeight + 1 + iOffsetY ) ); + w = catacurses::newwin( iContentHeight, FULL_SCREEN_WIDTH - 2, + point( 1 + iOffsetX, iHeaderHeight + 1 + iOffsetY ) ); - // =========================================================================== - // Perform initial draw. This includes things like the window border that do - // not need to be refreshed more than once. - - // == Draw border - draw_border( w_border, BORDER_COLOR, _( " AUTO NOTES MANAGER " ) ); - mvwputch( w_border, point( 0, 2 ), c_light_gray, LINE_XXXO ); - mvwputch( w_border, point( 79, 2 ), c_light_gray, LINE_XOXX ); - mvwputch( w_border, point( 61, FULL_SCREEN_HEIGHT - 1 ), c_light_gray, LINE_XXOX ); - wrefresh( w_border ); - - // == Draw header - int tmpx = 0; - tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "nable" ) ) + 2; - tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "isable" ) ) + 2; - shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( " - Toggle" ) ); - - // Draw horizontal line and corner pieces of the table - for( int x = 0; x < 78; x++ ) { - if( x == 60 ) { - mvwputch( w_header, point( x, 1 ), c_light_gray, LINE_OXXX ); - mvwputch( w_header, point( x, 2 ), c_light_gray, LINE_XOXO ); - } else { - mvwputch( w_header, point( x, 1 ), c_light_gray, LINE_OXOX ); - } - } - - mvwprintz( w_header, point( 1, 2 ), c_white, _( "Map Extra" ) ); - mvwprintz( w_header, point( 62, 2 ), c_white, _( "Enabled" ) ); - - wrefresh( w_header ); + ui.position_from_window( w_border ); + } ); + ui.mark_resize(); // =========================================================================== - // If the display cache contains no entries, the player might not have discovered any of // the map extras. In this case, we switch to a special state that alerts the user of this // in order to avoid confusion a completely empty GUI might normally create. const bool emptyMode = displayCache.empty(); + const int cacheSize = static_cast( displayCache.size() ); int currentLine = 0; int startPosition = 0; @@ -238,6 +213,7 @@ void auto_note_manager_gui::show() input_context ctx{ "AUTO_NOTES" }; ctx.register_action( "QUIT" ); ctx.register_action( "SWITCH_AUTO_NOTE_OPTION" ); + ctx.register_action( "HELP_KEYBINDINGS" ); if( !emptyMode ) { ctx.register_cardinal(); @@ -247,10 +223,35 @@ void auto_note_manager_gui::show() ctx.register_action( "DISABLE_MAPEXTRA_NOTE" ); } - // FIXME: temporarily disable redrawing of lower UIs before this UI is migrated to `ui_adaptor` - ui_adaptor ui( ui_adaptor::disable_uis_below {} ); + ui.on_redraw( [&]( const ui_adaptor & ) { + // == Draw border + draw_border( w_border, BORDER_COLOR, _( " AUTO NOTES MANAGER " ) ); + mvwputch( w_border, point( 0, 2 ), c_light_gray, LINE_XXXO ); + mvwputch( w_border, point( 79, 2 ), c_light_gray, LINE_XOXX ); + mvwputch( w_border, point( 61, FULL_SCREEN_HEIGHT - 1 ), c_light_gray, LINE_XXOX ); + wrefresh( w_border ); + + // == Draw header + int tmpx = 0; + tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "nable" ) ) + 2; + tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "isable" ) ) + 2; + shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( " - Toggle" ) ); + + // Draw horizontal line and corner pieces of the table + for( int x = 0; x < 78; x++ ) { + if( x == 60 ) { + mvwputch( w_header, point( x, 1 ), c_light_gray, LINE_OXXX ); + mvwputch( w_header, point( x, 2 ), c_light_gray, LINE_XOXO ); + } else { + mvwputch( w_header, point( x, 1 ), c_light_gray, LINE_OXOX ); + } + } + + mvwprintz( w_header, point( 1, 2 ), c_white, _( "Map Extra" ) ); + mvwprintz( w_header, point( 62, 2 ), c_white, _( "Enabled" ) ); + + wrefresh( w_header ); - while( true ) { mvwprintz( w_header, point( 39, 0 ), c_white, _( "Auto notes enabled:" ) ); int currentX = 60; @@ -273,8 +274,6 @@ void auto_note_manager_gui::show() } } - const int cacheSize = static_cast( displayCache.size() ); - draw_scrollbar( w_border, currentLine, iContentHeight, cacheSize, point( 0, 4 ) ); if( emptyMode ) { @@ -313,6 +312,10 @@ void auto_note_manager_gui::show() wrefresh( w_header ); wrefresh( w_border ); wrefresh( w ); + } ); + + while( true ) { + ui_manager::redraw(); const std::string currentAction = ctx.handle_input(); diff --git a/src/bionics_ui.cpp b/src/bionics_ui.cpp index b08d055bfac97..07f7a73b237b3 100644 --- a/src/bionics_ui.cpp +++ b/src/bionics_ui.cpp @@ -419,59 +419,75 @@ void player::power_bionics() bionic_tab_mode tab_mode = TAB_ACTIVE; //added title_tab_height for the tabbed bionic display - int TITLE_HEIGHT = 4; - int TITLE_TAB_HEIGHT = 3; - - // Main window - /** Total required height is: - * top frame line: + 1 - * height of title window: + TITLE_HEIGHT - * height of tabs: + TITLE_TAB_HEIGHT - * height of the biggest list of active/passive bionics: + bionic_count - * bottom frame line: + 1 - * TOTAL: TITLE_HEIGHT + TITLE_TAB_HEIGHT + bionic_count + 2 - */ - const int HEIGHT = std::min( TERMY, - std::max( FULL_SCREEN_HEIGHT, - TITLE_HEIGHT + TITLE_TAB_HEIGHT + - static_cast( my_bionics->size() ) + 2 ) ); - const int WIDTH = FULL_SCREEN_WIDTH + ( TERMX - FULL_SCREEN_WIDTH ) / 2; - const int START_X = ( TERMX - WIDTH ) / 2; - const int START_Y = ( TERMY - HEIGHT ) / 2; - //wBio is the entire bionic window - catacurses::window wBio = catacurses::newwin( HEIGHT, WIDTH, point( START_X, START_Y ) ); - - const int LIST_HEIGHT = HEIGHT - TITLE_HEIGHT - TITLE_TAB_HEIGHT - 2; - - const int DESCRIPTION_WIDTH = WIDTH - 2 - 40; - const int DESCRIPTION_START_Y = START_Y + TITLE_HEIGHT + TITLE_TAB_HEIGHT + 1; - const int DESCRIPTION_START_X = START_X + 1 + 40; - //w_description is the description panel that is controlled with ! key - catacurses::window w_description = catacurses::newwin( LIST_HEIGHT, DESCRIPTION_WIDTH, - point( DESCRIPTION_START_X, DESCRIPTION_START_Y ) ); - - // Title window - const int TITLE_START_Y = START_Y + 1; - const int HEADER_LINE_Y = TITLE_HEIGHT + TITLE_TAB_HEIGHT; - catacurses::window w_title = catacurses::newwin( TITLE_HEIGHT, WIDTH - 2, point( START_X + 1, - START_Y ) ); - - const int TAB_START_Y = TITLE_START_Y + 3; - //w_tabs is the tab bar for passive and active bionic groups - catacurses::window w_tabs = catacurses::newwin( TITLE_TAB_HEIGHT, WIDTH, - point( START_X, TAB_START_Y ) ); + const int TITLE_HEIGHT = 4; + const int TITLE_TAB_HEIGHT = 3; + int HEIGHT = 0; + int WIDTH = 0; + int LIST_HEIGHT = 0; + int list_start_y = 0; + int half_list_view_location = 0; + catacurses::window wBio; + catacurses::window w_description; + catacurses::window w_title; + catacurses::window w_tabs; + + ui_adaptor ui; + ui.on_screen_resize( [&]( ui_adaptor & ui ) { + // Main window + /** Total required height is: + * top frame line: + 1 + * height of title window: + TITLE_HEIGHT + * height of tabs: + TITLE_TAB_HEIGHT + * height of the biggest list of active/passive bionics: + bionic_count + * bottom frame line: + 1 + * TOTAL: TITLE_HEIGHT + TITLE_TAB_HEIGHT + bionic_count + 2 + */ + HEIGHT = std::min( TERMY, + std::max( FULL_SCREEN_HEIGHT, + TITLE_HEIGHT + TITLE_TAB_HEIGHT + + static_cast( my_bionics->size() ) + 2 ) ); + WIDTH = FULL_SCREEN_WIDTH + ( TERMX - FULL_SCREEN_WIDTH ) / 2; + const int START_X = ( TERMX - WIDTH ) / 2; + const int START_Y = ( TERMY - HEIGHT ) / 2; + //wBio is the entire bionic window + wBio = catacurses::newwin( HEIGHT, WIDTH, point( START_X, START_Y ) ); + + LIST_HEIGHT = HEIGHT - TITLE_HEIGHT - TITLE_TAB_HEIGHT - 2; + + const int DESCRIPTION_WIDTH = WIDTH - 2 - 40; + const int DESCRIPTION_START_Y = START_Y + TITLE_HEIGHT + TITLE_TAB_HEIGHT + 1; + const int DESCRIPTION_START_X = START_X + 1 + 40; + //w_description is the description panel that is controlled with ! key + w_description = catacurses::newwin( LIST_HEIGHT, DESCRIPTION_WIDTH, + point( DESCRIPTION_START_X, DESCRIPTION_START_Y ) ); + + // Title window + const int TITLE_START_Y = START_Y + 1; + const int HEADER_LINE_Y = TITLE_HEIGHT + TITLE_TAB_HEIGHT; + w_title = catacurses::newwin( TITLE_HEIGHT, WIDTH - 2, point( START_X + 1, + START_Y ) ); + + const int TAB_START_Y = TITLE_START_Y + 3; + //w_tabs is the tab bar for passive and active bionic groups + w_tabs = catacurses::newwin( TITLE_TAB_HEIGHT, WIDTH, + point( START_X, TAB_START_Y ) ); + + // offset for display: bionic with index i is drawn at y=list_start_y+i + // drawing the bionics starts with bionic[scroll_position] + // scroll_position; + list_start_y = HEADER_LINE_Y; + half_list_view_location = LIST_HEIGHT / 2; + + ui.position_from_window( wBio ); + } ); + ui.mark_resize(); int scroll_position = 0; int cursor = 0; //generate the tab title string and a count of the bionics owned bionic_menu_mode menu_mode = ACTIVATING; - // offset for display: bionic with index i is drawn at y=list_start_y+i - // drawing the bionics starts with bionic[scroll_position] - // scroll_position; - const int list_start_y = HEADER_LINE_Y; - int half_list_view_location = LIST_HEIGHT / 2; - int max_scroll_position = std::max( 0, static_cast( active.size() ) ); + int max_scroll_position = 0; input_context ctxt( "BIONICS" ); ctxt.register_updown(); @@ -481,116 +497,76 @@ void player::power_bionics() ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "CONFIRM" ); + ctxt.register_action( "QUIT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "TOGGLE_SAFE_FUEL" ); ctxt.register_action( "TOGGLE_AUTO_START" ); - bool recalc = false; - bool redraw = true; - - // FIXME: temporarily disable redrawing of lower UIs before this UI is migrated to `ui_adaptor` - ui_adaptor ui( ui_adaptor::disable_uis_below {} ); - - for( ;; ) { - if( recalc ) { - passive = filtered_bionics( *my_bionics, TAB_PASSIVE ); - active = filtered_bionics( *my_bionics, TAB_ACTIVE ); - - if( active.empty() && !passive.empty() ) { - tab_mode = TAB_PASSIVE; - } - - if( --cursor < 0 ) { - cursor = 0; - } - if( scroll_position > max_scroll_position && - cursor - scroll_position < LIST_HEIGHT - half_list_view_location ) { - scroll_position--; - } - - recalc = false; - // bionics were modified, so it's necessary to redraw the screen - redraw = true; - } - - //track which list we are looking at + ui.on_redraw( [&]( const ui_adaptor & ) { std::vector *current_bionic_list = ( tab_mode == TAB_ACTIVE ? &active : &passive ); - max_scroll_position = std::max( 0, static_cast( current_bionic_list->size() ) - LIST_HEIGHT ); - if( redraw ) { - redraw = false; - - werase( wBio ); - draw_border( wBio, BORDER_COLOR, _( " BIONICS " ) ); - mvwputch( wBio, point( getmaxx( wBio ) - 1, 2 ), BORDER_COLOR, LINE_XOXX ); // -| - - int max_width = 0; - std::vectorbps; - for( const body_part bp : all_body_parts ) { - const int total = get_total_bionics_slots( bp ); - const std::string s = string_format( "%s: %d/%d", - body_part_name_as_heading( bp, 1 ), - total - get_free_bionics_slots( bp ), total ); - bps.push_back( s ); - max_width = std::max( max_width, utf8_width( s ) ); - } - const int pos_x = WIDTH - 2 - max_width; - if( get_option < bool >( "CBM_SLOTS_ENABLED" ) ) { - for( size_t i = 0; i < bps.size(); ++i ) { - mvwprintz( wBio, point( pos_x, i + list_start_y ), c_light_gray, bps[i] ); - } + werase( wBio ); + draw_border( wBio, BORDER_COLOR, _( " BIONICS " ) ); + mvwputch( wBio, point( getmaxx( wBio ) - 1, 2 ), BORDER_COLOR, LINE_XOXX ); // -| + + int max_width = 0; + std::vector bps; + for( const body_part bp : all_body_parts ) { + const int total = get_total_bionics_slots( bp ); + const std::string s = string_format( "%s: %d/%d", + body_part_name_as_heading( bp, 1 ), + total - get_free_bionics_slots( bp ), total ); + bps.push_back( s ); + max_width = std::max( max_width, utf8_width( s ) ); + } + const int pos_x = WIDTH - 2 - max_width; + if( get_option < bool >( "CBM_SLOTS_ENABLED" ) ) { + for( size_t i = 0; i < bps.size(); ++i ) { + mvwprintz( wBio, point( pos_x, i + list_start_y ), c_light_gray, bps[i] ); } + } - if( current_bionic_list->empty() ) { - std::string msg; - switch( tab_mode ) { - case TAB_ACTIVE: - msg = _( "No activatable bionics installed." ); - break; - case TAB_PASSIVE: - msg = _( "No passive bionics installed." ); - break; + if( current_bionic_list->empty() ) { + std::string msg; + switch( tab_mode ) { + case TAB_ACTIVE: + msg = _( "No activatable bionics installed." ); + break; + case TAB_PASSIVE: + msg = _( "No passive bionics installed." ); + break; + } + fold_and_print( wBio, point( 2, list_start_y ), pos_x - 1, c_light_gray, msg ); + } else { + for( size_t i = scroll_position; i < current_bionic_list->size(); i++ ) { + if( list_start_y + static_cast( i ) - scroll_position == HEIGHT - 1 ) { + break; } - fold_and_print( wBio, point( 2, list_start_y ), pos_x - 1, c_light_gray, msg ); - } else { - for( size_t i = scroll_position; i < current_bionic_list->size(); i++ ) { - if( list_start_y + static_cast( i ) - scroll_position == HEIGHT - 1 ) { - break; + const bool is_highlighted = cursor == static_cast( i ); + const nc_color col = get_bionic_text_color( *( *current_bionic_list )[i], + is_highlighted ); + const std::string desc = string_format( "%c %s", ( *current_bionic_list )[i]->invlet, + build_bionic_powerdesc_string( + *( *current_bionic_list )[i] ).c_str() ); + trim_and_print( wBio, point( 2, list_start_y + i - scroll_position ), WIDTH - 3, col, + desc ); + if( is_highlighted && menu_mode != EXAMINING && get_option < bool >( "CBM_SLOTS_ENABLED" ) ) { + const bionic_id bio_id = ( *current_bionic_list )[i]->id; + draw_connectors( wBio, list_start_y + i - scroll_position, utf8_width( desc ) + 3, + pos_x - 2, bio_id ); + + // redraw highlighted (occupied) body parts + for( auto &elem : bio_id->occupied_bodyparts ) { + const int i = static_cast( elem.first ); + mvwprintz( wBio, point( pos_x, i + list_start_y ), c_yellow, bps[i] ); } - const bool is_highlighted = cursor == static_cast( i ); - const nc_color col = get_bionic_text_color( *( *current_bionic_list )[i], - is_highlighted ); - const std::string desc = string_format( "%c %s", ( *current_bionic_list )[i]->invlet, - build_bionic_powerdesc_string( - *( *current_bionic_list )[i] ).c_str() ); - trim_and_print( wBio, point( 2, list_start_y + i - scroll_position ), WIDTH - 3, col, - desc ); - if( is_highlighted && menu_mode != EXAMINING && get_option < bool >( "CBM_SLOTS_ENABLED" ) ) { - const bionic_id bio_id = ( *current_bionic_list )[i]->id; - draw_connectors( wBio, list_start_y + i - scroll_position, utf8_width( desc ) + 3, - pos_x - 2, bio_id ); - - // redraw highlighted (occupied) body parts - for( auto &elem : bio_id->occupied_bodyparts ) { - const int i = static_cast( elem.first ); - mvwprintz( wBio, point( pos_x, i + list_start_y ), c_yellow, bps[i] ); - } - } - } - } - - draw_scrollbar( wBio, cursor, LIST_HEIGHT, current_bionic_list->size(), point( 0, list_start_y ) ); -#if defined(__ANDROID__) - ctxt.get_registered_manual_keys().clear(); - for( size_t i = 0; i < current_bionic_list->size(); i++ ) { - ctxt.register_manual_key( ( *current_bionic_list )[i]->invlet, - build_bionic_powerdesc_string( *( *current_bionic_list )[i] ).c_str() ); } -#endif - } + + draw_scrollbar( wBio, cursor, LIST_HEIGHT, current_bionic_list->size(), point( 0, list_start_y ) ); + wrefresh( wBio ); draw_bionics_tabs( w_tabs, active.size(), passive.size(), tab_mode ); @@ -598,6 +574,24 @@ void player::power_bionics() if( menu_mode == EXAMINING && !current_bionic_list->empty() ) { draw_description( w_description, *( *current_bionic_list )[cursor] ); } + } ); + + for( ;; ) { + ui_manager::redraw(); + + //track which list we are looking at + std::vector *current_bionic_list = ( tab_mode == TAB_ACTIVE ? &active : &passive ); + max_scroll_position = std::max( 0, static_cast( current_bionic_list->size() ) - LIST_HEIGHT ); + scroll_position = clamp( scroll_position, 0, max_scroll_position ); + cursor = clamp( cursor, 0, current_bionic_list->size() ); + +#if defined(__ANDROID__) + ctxt.get_registered_manual_keys().clear(); + for( size_t i = 0; i < current_bionic_list->size(); i++ ) { + ctxt.register_manual_key( ( *current_bionic_list )[i]->invlet, + build_bionic_powerdesc_string( *( *current_bionic_list )[i] ).c_str() ); + } +#endif const std::string action = ctxt.handle_input(); const int ch = ctxt.get_raw_input().get_first_input(); @@ -607,7 +601,6 @@ void player::power_bionics() bool toggle_auto_start = false; if( action == "DOWN" ) { - redraw = true; if( static_cast( cursor ) < current_bionic_list->size() - 1 ) { cursor++; } else { @@ -621,7 +614,6 @@ void player::power_bionics() scroll_position = std::max( cursor - half_list_view_location, 0 ); } } else if( action == "UP" ) { - redraw = true; if( cursor > 0 ) { cursor--; } else { @@ -650,7 +642,6 @@ void player::power_bionics() // Selected an non-existing bionic (or Escape, or ...) continue; } - redraw = true; const int newch = popup_getkey( _( "%s; enter new letter. Space to clear. Esc to cancel." ), tmp->id->name ); wrefresh( wBio ); @@ -674,7 +665,6 @@ void player::power_bionics() } // TODO: show a message like when reassigning a key to an item? } else if( action == "NEXT_TAB" ) { - redraw = true; scroll_position = 0; cursor = 0; if( tab_mode == TAB_ACTIVE ) { @@ -683,7 +673,6 @@ void player::power_bionics() tab_mode = TAB_ACTIVE; } } else if( action == "PREV_TAB" ) { - redraw = true; scroll_position = 0; cursor = 0; if( tab_mode == TAB_PASSIVE ) { @@ -696,16 +685,11 @@ void player::power_bionics() } else if( action == "TOGGLE_EXAMINE" ) { // switches between activation and examination menu_mode = menu_mode == ACTIVATING ? EXAMINING : ACTIVATING; - redraw = true; } else if( action == "TOGGLE_SAFE_FUEL" ) { toggle_safe_fuel = true; } else if( action == "TOGGLE_AUTO_START" ) { toggle_auto_start = true; - } else if( action == "HELP_KEYBINDINGS" ) { - redraw = true; - } else if( action == "CONFIRM" ) { - confirmCheck = true; - } else { + } else if( action == "CONFIRM" || action == "QUIT" ) { confirmCheck = true; } @@ -715,8 +699,7 @@ void player::power_bionics() tmp = bio_list[cursor]; if( !tmp->info().fuel_opts.empty() || tmp->info().is_remote_fueled ) { tmp->toggle_safe_fuel_mod(); - g->refresh_all(); - redraw = true; + g->invalidate_main_ui_adaptor(); } else { popup( _( "You can't toggle fuel saving mode on a non-fueled CBM." ) ); } @@ -730,8 +713,7 @@ void player::power_bionics() tmp = bio_list[cursor]; if( !tmp->info().fuel_opts.empty() || tmp->info().is_remote_fueled ) { tmp->toggle_auto_start_mod(); - g->refresh_all(); - redraw = true; + g->invalidate_main_ui_adaptor(); } else { popup( _( "You can't toggle auto start mode on a non-fueled CBM." ) ); } @@ -786,9 +768,7 @@ void player::power_bionics() break; } } - // update message log and the menu - g->refresh_all(); - redraw = true; + g->invalidate_main_ui_adaptor(); if( moves < 0 ) { return; } @@ -797,11 +777,9 @@ void player::power_bionics() popup( _( "You can not activate %s!\n" "To read a description of %s, press '%s', then '%c'." ), bio_data.name, bio_data.name, ctxt.get_desc( "TOGGLE_EXAMINE" ), tmp->invlet ); - redraw = true; } } else if( menu_mode == EXAMINING ) { // Describing bionics, allow user to jump to description key - redraw = true; if( action != "CONFIRM" ) { for( size_t i = 0; i < active.size(); i++ ) { if( active[i] == tmp ) { diff --git a/src/game.cpp b/src/game.cpp index 2ba4ebb564305..1fd63428e8991 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3191,6 +3191,30 @@ static void draw_footsteps( const catacurses::window &window, const tripoint &of } } +shared_ptr_fast game::create_or_get_main_ui_adaptor() +{ + shared_ptr_fast ui = main_ui_adaptor.lock(); + if( !ui ) { + main_ui_adaptor = ui = make_shared_fast(); + ui->position_from_window( catacurses::stdscr ); + ui->on_redraw( []( const ui_adaptor & ) { + g->draw(); + } ); + ui->on_screen_resize( []( ui_adaptor & ui ) { + ui.position_from_window( catacurses::stdscr ); + } ); + } + return ui; +} + +void game::invalidate_main_ui_adaptor() const +{ + shared_ptr_fast ui = main_ui_adaptor.lock(); + if( ui ) { + ui->invalidate_ui(); + } +} + void game::draw() { if( test_mode ) { diff --git a/src/game.h b/src/game.h index 3a184978cf352..7ad7fb6336a55 100644 --- a/src/game.h +++ b/src/game.h @@ -120,6 +120,8 @@ struct visibility_variables; class scent_map; class loading_ui; +class ui_adaptor; + using item_filter = std::function; enum peek_act : int { @@ -221,6 +223,8 @@ class game void start_calendar(); /** MAIN GAME LOOP. Returns true if game is over (death, saved, quit, etc.). */ bool do_turn(); + shared_ptr_fast create_or_get_main_ui_adaptor(); + void invalidate_main_ui_adaptor() const; void draw(); void draw_ter( bool draw_sounds = true ); void draw_ter( const tripoint ¢er, bool looking = false, bool draw_sounds = true ); @@ -1047,6 +1051,8 @@ class game tripoint last_mouse_edge_scroll_vector_overmap; std::pair mouse_edge_scrolling( input_context &ctxt, int speed, const tripoint &last, bool iso ); + + weak_ptr_fast main_ui_adaptor; public: /** Used to implement mouse "edge scrolling". Returns a * tripoint which is a vector of the resulting "move", i.e. diff --git a/src/main.cpp b/src/main.cpp index 83cc8957e678e..a9ab1d0e762df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -689,14 +689,7 @@ int main( int argc, char *argv[] ) } } - ui_adaptor main_ui; - main_ui.position_from_window( catacurses::stdscr ); - main_ui.on_redraw( []( const ui_adaptor & ) { - g->draw(); - } ); - main_ui.on_screen_resize( []( ui_adaptor & ui ) { - ui.position_from_window( catacurses::stdscr ); - } ); + shared_ptr_fast ui = g->create_or_get_main_ui_adaptor(); while( !g->do_turn() ); } diff --git a/src/ui_manager.cpp b/src/ui_manager.cpp index 3fada42175a19..acd2eedfffa91 100644 --- a/src/ui_manager.cpp +++ b/src/ui_manager.cpp @@ -6,7 +6,9 @@ #include "point.h" #include "sdltiles.h" -static std::vector> ui_stack; +using ui_stack_t = std::vector>; + +static ui_stack_t ui_stack; ui_adaptor::ui_adaptor() : disabling_uis_below( false ), invalidated( false ), deferred_resize( false ) @@ -79,18 +81,17 @@ static bool overlap( const rectangle &lhs, const rectangle &rhs ) rhs.p_min.x < lhs.p_max.x && rhs.p_min.y < lhs.p_max.y; } -void ui_adaptor::invalidate( const rectangle &rect ) +// This function does two things: +// 1. Ensure that any UI that would be overwritten by redrawing a lower invalidated +// UI also gets redrawn. +// 2. Optimize the invalidated flag so completely occluded UIs will not be redrawn. +// +// The current implementation may still invalidate UIs that in fact do not need to +// be redrawn, but all UIs that need to be redrawn are guaranteed be invalidated. +void ui_adaptor::invalidation_consistency_and_optimization() { - if( rect.p_min.x >= rect.p_max.x || rect.p_min.y >= rect.p_max.y ) { - return; - } - // TODO avoid invalidating portions that do not need to be redrawn for( auto it_upper = ui_stack.cbegin(); it_upper < ui_stack.cend(); ++it_upper ) { const ui_adaptor &ui_upper = it_upper->get(); - if( !ui_upper.invalidated && overlap( ui_upper.dimensions, rect ) ) { - // invalidated by `rect` - ui_upper.invalidated = true; - } for( auto it_lower = ui_stack.cbegin(); it_lower < it_upper; ++it_lower ) { const ui_adaptor &ui_lower = it_lower->get(); if( !ui_upper.invalidated && ui_lower.invalidated && @@ -113,6 +114,45 @@ void ui_adaptor::invalidate( const rectangle &rect ) } } +void ui_adaptor::invalidate_ui() const +{ + if( invalidated ) { + return; + } + auto it = ui_stack.cbegin(); + for( ; it < ui_stack.cend(); ++it ) { + if( &it->get() == this ) { + break; + } + } + if( it == ui_stack.end() ) { + return; + } + // If an upper UI occludes this UI then nothing gets redrawn + for( auto it_upper = std::next( it ); it_upper < ui_stack.cend(); ++it_upper ) { + if( contains( it_upper->get().dimensions, dimensions ) ) { + return; + } + } + invalidated = true; + invalidation_consistency_and_optimization(); +} + +void ui_adaptor::invalidate( const rectangle &rect ) +{ + if( rect.p_min.x >= rect.p_max.x || rect.p_min.y >= rect.p_max.y ) { + return; + } + for( auto it_upper = ui_stack.cbegin(); it_upper < ui_stack.cend(); ++it_upper ) { + const ui_adaptor &ui_upper = it_upper->get(); + if( !ui_upper.invalidated && overlap( ui_upper.dimensions, rect ) ) { + // invalidated by `rect` + ui_upper.invalidated = true; + } + } + invalidation_consistency_and_optimization(); +} + void ui_adaptor::redraw() { // apply deferred resizing diff --git a/src/ui_manager.h b/src/ui_manager.h index e3947734004dd..9ada2fba5e28e 100644 --- a/src/ui_manager.h +++ b/src/ui_manager.h @@ -52,10 +52,17 @@ class ui_adaptor // initialization is not possible or wanted. void mark_resize() const; + // Invalidate this UI so it gets redrawn the next time screen is refreshed, + // unless an upper UI completely occludes this UI. May also cause upper UIs + // to redraw. + void invalidate_ui() const; + static void invalidate( const rectangle &rect ); static void redraw(); static void screen_resized(); private: + static void invalidation_consistency_and_optimization(); + // pixel dimensions in tiles, console cell dimensions in curses rectangle dimensions; redraw_callback_t redraw_cb;