diff --git a/src/input.cpp b/src/input.cpp index 83cfd0a56c336..926c55c5ce0df 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1551,36 +1551,24 @@ cata::optional input_context::get_coordinates( const catacurses::windo } #endif -std::pair input_context::get_coordinates_text( const catacurses::window +cata::optional input_context::get_coordinates_text( const catacurses::window &capture_win ) const { #if !defined( TILES ) ( void ) capture_win; - return std::make_pair( point(), false ); + return cata::nullopt; #else if( !coordinate_input_received ) { - return std::make_pair( point(), false ); + return cata::nullopt; } - const window_dimensions dim = get_window_dimensions( capture_win ); - const int &fw = dim.scaled_font_size.x; const int &fh = dim.scaled_font_size.y; const point &win_min = dim.window_pos_pixel; - const point &win_size = dim.window_size_pixel; - const point win_max = win_min + win_size; - - const half_open_rectangle win_bounds( win_min, win_max ); - const point screen_pos = coordinate - win_min; const point selected( divide_round_down( screen_pos.x, fw ), divide_round_down( screen_pos.y, fh ) ); - - if( !win_bounds.contains( coordinate ) ) { - return std::make_pair( selected, false ); - } - - return std::make_pair( selected, true ); + return selected; #endif } diff --git a/src/input.h b/src/input.h index 8a468042a5eba..c47b61df801ac 100644 --- a/src/input.h +++ b/src/input.h @@ -712,7 +712,10 @@ class input_context */ input_event get_raw_input(); - std::pair get_coordinates_text( const catacurses::window &capture_win ) const; + /** + * Get coordinate of text level from mouse input, difference between this and get_coordinates is that one is getting pixel level coordinate. + */ + cata::optional get_coordinates_text( const catacurses::window &capture_win ) const; /** * Get the human-readable name for an action. diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index bcc15e72458bf..680c045c534aa 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -26,6 +26,7 @@ #include "vehicle_selector.h" #include "visitable.h" #include "vpart_position.h" +#include "sdltiles.h" #if defined(__ANDROID__) #include @@ -919,12 +920,12 @@ static int num_parents( const item_location &loc ) return 2 + num_parents( loc.parent_item() ); } -void inventory_column::draw( const catacurses::window &win, const point &p ) +void inventory_column::draw( const catacurses::window &win, const point &p, + std::vector, inventory_entry *>> &rect_entry_map ) { if( !visible() ) { return; } - const auto available_cell_width = [ this ]( size_t index, size_t cell_index ) { const size_t displayed_width = cells[cell_index].current_width; const size_t real_width = get_entry_cell_width( index, cell_index ); @@ -933,6 +934,7 @@ void inventory_column::draw( const catacurses::window &win, const point &p ) }; // Do the actual drawing + rect_entry_map.clear(); for( size_t index = page_offset, line = 0; index < entries.size() && line < entries_per_page; ++index, ++line ) { inventory_entry &entry = entries[index]; @@ -954,10 +956,11 @@ void inventory_column::draw( const catacurses::window &win, const point &p ) const bool selected = active && is_selected( entry ); - entry.drawn_info.text_x_start = x1; const int hx_max = p.x + get_width() + contained_offset; - entry.drawn_info.text_x_end = hx_max; - entry.drawn_info.y = yy; + inclusive_rectangle rect = inclusive_rectangle( point( x1, yy ), + point( hx_max - 1, yy ) ); + rect_entry_map.push_back( std::pair, inventory_entry *>( rect, + &entry ) ); if( selected && visible_cells() > 1 ) { for( int hx = x1; hx < hx_max; ++hx ) { @@ -1418,20 +1421,11 @@ inventory_entry *inventory_selector::find_entry_by_invlet( int invlet ) const return nullptr; } -inventory_entry *inventory_selector::find_entry_by_coordinate( point coordinate ) const +inventory_entry *inventory_selector::find_entry_by_coordinate( const point coordinate ) const { - std::vector columns = get_visible_columns(); - const auto filter_to_selected = [&]( const inventory_entry & entry ) { - return entry.is_selectable(); - }; - for( inventory_column *column : columns ) { - std::vector entries = column->get_entries( filter_to_selected ); - for( inventory_entry *entry : entries ) { - if( entry->drawn_info.text_x_start <= coordinate.x && - coordinate.x <= entry->drawn_info.text_x_end && - entry->drawn_info.y == coordinate.y ) { - return entry; - } + for( auto pair : rect_entry_map ) { + if( pair.first.contains( coordinate ) ) { + return pair.second; } } return nullptr; @@ -1765,7 +1759,7 @@ void inventory_selector::draw_columns( const catacurses::window &w ) } if( !is_active_column( *elem ) ) { - elem->draw( w, point( x, y ) ); + elem->draw( w, point( x, y ), rect_entry_map ); } else { active_x = x; } @@ -1773,7 +1767,7 @@ void inventory_selector::draw_columns( const catacurses::window &w ) x += elem->get_width() + gap; } - get_active_column().draw( w, point( active_x, y ) ); + get_active_column().draw( w, point( active_x, y ), rect_entry_map ); if( empty() ) { center_print( w, getmaxy( w ) / 2, c_dark_gray, _( "Your inventory is empty." ) ); } @@ -1887,12 +1881,14 @@ inventory_input inventory_selector::get_input() res.action = ctxt.handle_input(); res.ch = ctxt.get_raw_input().get_first_input(); - std::pair ct_pair = ctxt.get_coordinates_text( w_inv ); - if( ct_pair.second ) { - point p = ct_pair.first; - res.entry = find_entry_by_coordinate( p ); - if( res.entry != nullptr ) { - return res; + cata::optional o_p = ctxt.get_coordinates_text( w_inv ); + if( o_p ) { + point p = o_p.value(); + if( window_contains_point_relative( w_inv, p ) ) { + res.entry = find_entry_by_coordinate( p ); + if( res.entry != nullptr && res.entry->is_selectable() ) { + return res; + } } } diff --git a/src/inventory_ui.h b/src/inventory_ui.h index e169959cb5948..b2c453d9be117 100644 --- a/src/inventory_ui.h +++ b/src/inventory_ui.h @@ -24,6 +24,7 @@ #include "pimpl.h" #include "units.h" #include "item_category.h" +#include "ui.h" class Character; class item; @@ -47,12 +48,6 @@ struct inventory_input; using drop_location = std::pair; using drop_locations = std::list; -struct inventory_entry_drawn_info { - int text_x_start; - int text_x_end; - int y; -}; - class inventory_entry { public: @@ -133,8 +128,6 @@ class inventory_entry nc_color get_invlet_color() const; void update_cache(); - inventory_entry_drawn_info drawn_info; - private: const item_category *custom_category = nullptr; bool enabled = true; @@ -308,7 +301,8 @@ class inventory_column inventory_entry *find_by_invlet( int invlet ) const; - void draw( const catacurses::window &win, const point & ); + void draw( const catacurses::window &win, const point &p, + std::vector< std::pair, inventory_entry *>> &rect_entry_map ); void add_entry( const inventory_entry &entry ); void move_entries_to( inventory_column &dest ); @@ -594,6 +588,8 @@ class inventory_selector } std::vector get_visible_columns() const; + std::vector< std::pair, inventory_entry *>> rect_entry_map; + private: // These functions are called from resizing/redraw callbacks of ui_adaptor // and should not be made protected or public. diff --git a/src/pickup.cpp b/src/pickup.cpp index a26f3fbb86503..285040b679a8e 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -51,6 +51,7 @@ #include "vehicle.h" #include "vehicle_selector.h" #include "vpart_position.h" +#include "sdltiles.h" using ItemCount = std::pair; using PickupMap = std::map; @@ -624,6 +625,7 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) ctxt.register_action( "ANY_INPUT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "FILTER" ); + ctxt.register_action( "SELECT" ); #if defined(__ANDROID__) ctxt.allow_text_entry = true; // allow user to specify pickup amount #endif @@ -670,6 +672,7 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) const std::string pickup_chars = ctxt.get_available_single_char_hotkeys( all_pickup_chars ); werase( w_pickup ); + pickup_rect::list.clear(); for( int cur_it = start; cur_it < start + maxitems; cur_it++ ) { if( cur_it < static_cast( matches.size() ) ) { int true_it = matches[cur_it]; @@ -740,8 +743,11 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) item_name = string_format( "! %s", item_name ); } - trim_and_print( w_pickup, point( 6, 1 + ( cur_it % maxitems ) ), pickupW - 4, icolor, - item_name ); + int y = 1 + ( cur_it % maxitems ); + trim_and_print( w_pickup, point( 6, y ), pickupW - 4, icolor, item_name ); + pickup_rect rect = pickup_rect( point( 6, y ), point( 6 + pickupW - 4 - 1, y ) ); + rect.cur_it = cur_it; + pickup_rect::list.push_back( rect ); } } @@ -795,6 +801,19 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) if( itemcount < 0 ) { itemcount = 0; } + } else if( action == "SELECT" ) { + cata::optional pos = ctxt.get_coordinates_text( w_pickup ); + if( pos ) { + if( window_contains_point_relative( w_pickup, pos.value() ) ) { + pickup_rect *rect = pickup_rect::find_by_coordinate( pos.value() ); + if( rect != nullptr ) { + selected = rect->cur_it; + iScrollPos = 0; + idx = selected; + } + } + } + } else if( action == "SCROLL_UP" ) { iScrollPos--; } else if( action == "SCROLL_DOWN" ) { @@ -1083,3 +1102,15 @@ int Pickup::cost_to_move_item( const Character &who, const item &it ) // Keep it sane - it's not a long activity return std::min( 400, ret ); } + +std::vector Pickup::pickup_rect::list; + +Pickup::pickup_rect *Pickup::pickup_rect::find_by_coordinate( const point &p ) +{ + for( pickup_rect &rect : pickup_rect::list ) { + if( rect.contains( p ) ) { + return ▭ + } + } + return nullptr; +} diff --git a/src/pickup.h b/src/pickup.h index 18601d9f20749..7c396481bba98 100644 --- a/src/pickup.h +++ b/src/pickup.h @@ -3,6 +3,8 @@ #define CATA_SRC_PICKUP_H #include +#include "point.h" +#include "ui.h" class item; class item_location; @@ -42,6 +44,14 @@ int cost_to_move_item( const Character &who, const item &it ); * @param m map they are on */ bool handle_spillable_contents( Character &c, item &it, map &m ); -} // namespace Pickup +struct pickup_rect : inclusive_rectangle { + pickup_rect() = default; + pickup_rect( const point &P_MIN, const point &P_MAX ) : inclusive_rectangle( P_MIN, P_MAX ) {} + int cur_it; + static std::vector list; + static pickup_rect *find_by_coordinate( const point &p ); +}; + +} // namespace Pickup #endif // CATA_SRC_PICKUP_H diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index ed2423eb7e8dd..a55409fd21a5e 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1,7 +1,8 @@ -#if defined(TILES) - #include "cursesdef.h" // IWYU pragma: associated #include "sdltiles.h" // IWYU pragma: associated +#include "cuboid_rectangle.h" + +#if defined(TILES) #include #include @@ -4114,3 +4115,11 @@ HWND getWindowHandle() #endif #endif // TILES + +bool window_contains_point_relative( const catacurses::window &win, const point &p ) +{ + const int x = catacurses::getmaxx( win ); + const int y = catacurses::getmaxy( win ); + const half_open_rectangle win_bounds( point_zero, point( x, y ) ); + return win_bounds.contains( p ); +} diff --git a/src/sdltiles.h b/src/sdltiles.h index 603953912416a..ebe3d5018eae2 100644 --- a/src/sdltiles.h +++ b/src/sdltiles.h @@ -3,13 +3,19 @@ #define CATA_SRC_SDLTILES_H #include +#include "point.h" + +namespace catacurses +{ +class window; +} // namespace catacurses + #if defined(TILES) #include #include #include "color_loader.h" -#include "point.h" #include "sdl_wrappers.h" class cata_tiles; @@ -41,4 +47,6 @@ window_dimensions get_window_dimensions( const point &pos, const point &size ); #endif // TILES +// Text level, valid only for a point relative to the window, not a point in overall space. +bool window_contains_point_relative( const catacurses::window &win, const point &p ); #endif // CATA_SRC_SDLTILES_H diff --git a/src/ui.cpp b/src/ui.cpp index e245da9aa371f..7163f55e7146c 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -19,6 +19,7 @@ #include "string_input_popup.h" #include "translations.h" #include "ui_manager.h" +#include "sdltiles.h" #if defined(__ANDROID__) #include @@ -611,8 +612,11 @@ void uilist::show() // to be used. const auto entry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].txt ) : entries[ ei ].txt ); - trim_and_print( window, point( pad_left + 4, estart + si ), - max_entry_len, co, "%s", entry.c_str() ); + int x = pad_left + 4; + int y = estart + si; + entries[ei].drawn_rect.p_min = point( x, y ); + entries[ei].drawn_rect.p_max = point( x + max_entry_len - 1, y ); + trim_and_print( window, point( x, y ), max_entry_len, co, "%s", entry.c_str() ); if( max_column_len && !entries[ ei ].ctxt.empty() ) { const auto centry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].ctxt ) : @@ -788,6 +792,7 @@ void uilist::query( bool loop, int timeout ) if( allow_cancel ) { ctxt.register_action( "QUIT" ); } + ctxt.register_action( "SELECT" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "FILTER" ); ctxt.register_action( "ANY_INPUT" ); @@ -829,6 +834,18 @@ void uilist::query( bool loop, int timeout ) if( callback != nullptr ) { callback->select( this ); } + } else if( !fentries.empty() && ret_act == "SELECT" ) { + cata::optional p = ctxt.get_coordinates_text( window ); + if( p ) { + if( window_contains_point_relative( window, p.value() ) ) { + uilist_entry *entry = find_entry_by_coordinate( p.value() ); + if( entry != nullptr ) { + if( entry->enabled ) { + ret = entry->retval; + } + } + } + } } else if( !fentries.empty() && ret_act == "CONFIRM" ) { if( entries[ selected ].enabled ) { ret = entries[ selected ].retval; // valid @@ -859,6 +876,17 @@ void uilist::query( bool loop, int timeout ) } while( loop && ret == UILIST_WAIT_INPUT ); } +uilist_entry *uilist::find_entry_by_coordinate( const point &p ) +{ + for( int i : fentries ) { + uilist_entry &entry = entries[i]; + if( entry.drawn_rect.contains( p ) ) { + return &entry; + } + } + return nullptr; +} + ///@} /** * cleanup @@ -958,4 +986,3 @@ void pointmenu_cb::select( uilist *const menu ) { impl->select( menu ); } - diff --git a/src/ui.h b/src/ui.h index 91c7b5dcc5c87..0eb6805f5be1e 100644 --- a/src/ui.h +++ b/src/ui.h @@ -11,11 +11,13 @@ #include #include "color.h" +#include "cuboid_rectangle.h" #include "cursesdef.h" #include "memory_fast.h" #include "pimpl.h" #include "point.h" #include "string_formatter.h" +#include "input.h" class translation; @@ -96,6 +98,8 @@ struct uilist_entry { uilist_entry( Enum e, Args && ... args ) : uilist_entry( static_cast( e ), std::forward( args )... ) {} + + inclusive_rectangle drawn_rect; }; /** @@ -338,13 +342,14 @@ class uilist // NOLINT(cata-xy) bool started = false; + uilist_entry *find_entry_by_coordinate( const point &p ); + public: // Results // TODO change to getters std::string ret_act; int ret = 0; int keypress = 0; - int selected = 0; };