1 #pragma once 2 #ifndef CATA_SRC_UI_H 3 #define CATA_SRC_UI_H 4 5 #include <functional> 6 #include <initializer_list> 7 #include <iosfwd> 8 #include <map> 9 #include <memory> 10 #include <new> 11 #include <string> 12 #include <type_traits> 13 #include <utility> 14 #include <vector> 15 16 #include "color.h" 17 #include "cuboid_rectangle.h" 18 #include "cursesdef.h" 19 #include "input.h" 20 #include "memory_fast.h" 21 #include "optional.h" 22 #include "pimpl.h" 23 #include "point.h" 24 #include "string_formatter.h" 25 26 class translation; 27 28 //////////////////////////////////////////////////////////////////////////////////// 29 /** 30 * uilist constants 31 */ 32 const int UILIST_ERROR = -1024; 33 const int UILIST_WAIT_INPUT = -1025; 34 const int UILIST_UNBOUND = -1026; 35 const int UILIST_CANCEL = -1027; 36 const int UILIST_TIMEOUT = -1028; 37 const int UILIST_ADDITIONAL = -1029; 38 const int MENU_AUTOASSIGN = -1; 39 40 class string_input_popup; 41 class ui_adaptor; 42 43 catacurses::window new_centered_win( int nlines, int ncols ); 44 45 /** 46 * mvwzstr: line of text with horizontal offset and color 47 */ 48 49 struct mvwzstr { 50 int left = 0; 51 nc_color color = c_unset; 52 std::string txt; 53 int sym = 0; 54 }; 55 56 /** 57 * uilist_entry: entry line for uilist 58 */ 59 struct uilist_entry { 60 int retval; // return this int 61 bool enabled; // darken, and forbid scrolling if hilight_disabled is false 62 bool force_color = false; // Never darken this option 63 // cata::nullopt: automatically assign an unassigned hotkey 64 // input_event(): disable hotkey 65 cata::optional<input_event> hotkey; 66 std::string txt; // what it says on the tin 67 std::string desc; // optional, possibly longer, description 68 std::string ctxt; // second column text 69 nc_color hotkey_color; 70 nc_color text_color; 71 mvwzstr extratxt; 72 73 // In the following constructors, int K only support letters (a-z, A-Z) and 74 // digits (0-9), MENU_AUTOASSIGN, and 0 or ' ' (disable hotkey). Other 75 // values may not work under keycode mode. 76 explicit uilist_entry( const std::string &T ); 77 uilist_entry( const std::string &T, const std::string &D ); 78 uilist_entry( const std::string &T, int K ); 79 uilist_entry( const std::string &T, const cata::optional<input_event> &K ); 80 uilist_entry( int R, bool E, int K, const std::string &T ); 81 uilist_entry( int R, bool E, const cata::optional<input_event> &K, 82 const std::string &T ); 83 uilist_entry( int R, bool E, int K, const std::string &T, const std::string &D ); 84 uilist_entry( int R, bool E, int K, const std::string &T, const std::string &D, 85 const std::string &C ); 86 uilist_entry( int R, bool E, const cata::optional<input_event> &K, 87 const std::string &T, const std::string &D, 88 const std::string &C ); 89 uilist_entry( int R, bool E, int K, const std::string &T, 90 const nc_color &H, const nc_color &C ); 91 template<typename Enum, typename... Args, 92 typename = std::enable_if_t<std::is_enum<Enum>::value>> uilist_entryuilist_entry93 explicit uilist_entry( Enum e, Args && ... args ) : 94 uilist_entry( static_cast<int>( e ), std::forward<Args>( args )... ) 95 {} 96 97 inclusive_rectangle<point> drawn_rect; 98 }; 99 100 /** 101 * Generic multi-function callback for highlighted items, key presses, and window control. Example: 102 * 103 * class monmenu_cb: public uilist_callback { 104 * public: 105 * bool key(int ch, int num, uilist * menu) { 106 * if ( ch == 'k' && num > 0 ) { 107 * std::vector<monster> * game_z=static_cast<std::vector<monster>*>(myptr); 108 * game_z[num]->dead = true; 109 * } 110 * } 111 * void refresh( uilist *menu ) { 112 * if( menu->selected >= 0 && static_cast<size_t>( menu->selected ) < game_z.size() ) { 113 * mvwprintz( menu->window, 0, 0, c_red, "( %s )",game_z[menu->selected]->name() ); 114 * wnoutrefresh( menu->window ); 115 * } 116 * } 117 * } 118 * uilist monmenu; 119 * for( size_t i = 0; i < z.size(); ++i ) { 120 * monmenu.addentry( z[i].name ); 121 * } 122 * monmenu_cb * cb; 123 * cb->setptr( &g->z ); 124 * monmenu.callback = cb 125 * monmenu.query(); 126 * 127 */ 128 class uilist; 129 130 /** 131 * uilist::query() handles most input events first, 132 * and then passes the event to the callback if it can't handle it. 133 * 134 * The callback returninig a boolean false signifies that the callback can't "handle the 135 * event completely". This is unchanged before or after the PR. 136 * @{ 137 */ 138 class uilist_callback 139 { 140 public: 141 142 /** 143 * After a new item is selected, call this once 144 */ select(uilist *)145 virtual void select( uilist * ) {} key(const input_context &,const input_event &,int,uilist *)146 virtual bool key( const input_context &, const input_event &/*key*/, int /*entnum*/, 147 uilist * ) { 148 return false; 149 } refresh(uilist *)150 virtual void refresh( uilist * ) {} 151 virtual ~uilist_callback() = default; 152 }; 153 /*@}*/ 154 /** 155 * uilist: scrolling vertical list menu 156 */ 157 158 class uilist // NOLINT(cata-xy) 159 { 160 public: 161 class size_scalar 162 { 163 public: 164 struct auto_assign { 165 }; 166 167 size_scalar &operator=( auto_assign ); 168 size_scalar &operator=( int val ); 169 size_scalar &operator=( const std::function<int()> &fun ); 170 171 friend class uilist; 172 173 private: 174 std::function<int()> fun; 175 }; 176 177 class pos_scalar 178 { 179 public: 180 struct auto_assign { 181 }; 182 183 pos_scalar &operator=( auto_assign ); 184 pos_scalar &operator=( int val ); 185 // the parameter to the function is the corresponding size vector element 186 // (width for x, height for y) 187 pos_scalar &operator=( const std::function<int( int )> &fun ); 188 189 friend class uilist; 190 191 private: 192 std::function<int( int )> fun; 193 }; 194 195 uilist(); 196 // query() will be called at the end of these convenience constructors 197 uilist( const std::string &msg, const std::vector<uilist_entry> &opts ); 198 uilist( const std::string &msg, const std::vector<std::string> &opts ); 199 uilist( const std::string &msg, std::initializer_list<const char *const> opts ); 200 201 ~uilist(); 202 203 // whether to report invalid color tag with debug message. 204 void color_error( bool report ); 205 206 void init(); 207 void setup(); 208 // initialize the window or reposition it after screen size change. 209 void reposition( ui_adaptor &ui ); 210 void show(); 211 bool scrollby( int scrollby ); 212 void query( bool loop = true, int timeout = -1 ); 213 void filterlist(); 214 // In add_entry/add_entry_desc/add_entry_col, int k only support letters 215 // (a-z, A-Z) and digits (0-9), MENU_AUTOASSIGN, and 0 or ' ' (disable 216 // hotkey). Other values may not work under keycode mode. 217 void addentry( const std::string &str ); 218 void addentry( int r, bool e, int k, const std::string &str ); 219 void addentry( int r, bool e, const cata::optional<input_event> &k, 220 const std::string &str ); 221 template<typename K, typename ...Args> addentry(const int r,const bool e,K && k,const char * const format,Args &&...args)222 void addentry( const int r, const bool e, K &&k, const char *const format, Args &&... args ) { 223 return addentry( r, e, std::forward<K>( k ), 224 string_format( format, std::forward<Args>( args )... ) ); 225 } 226 void addentry_desc( const std::string &str, const std::string &desc ); 227 void addentry_desc( int r, bool e, int k, const std::string &str, const std::string &desc ); 228 void addentry_col( int r, bool e, int k, const std::string &str, const std::string &column, 229 const std::string &desc = "" ); 230 void addentry_col( int r, bool e, const cata::optional<input_event> &k, 231 const std::string &str, const std::string &column, 232 const std::string &desc = std::string() ); 233 void settext( const std::string &str ); 234 235 void reset(); 236 237 // Can be called before `uilist::query` to keep the uilist on UI stack after 238 // `uilist::query` returns. The returned `ui_adaptor` is cleared when the 239 // `uilist` is deconstructed. 240 // 241 // Example: 242 // shared_ptr_fast<ui_adaptor> ui = menu.create_or_get_ui_adaptor(); 243 // menu.query() 244 // // before `ui` or `menu` is deconstructed, the menu will always be 245 // // displayed on screen. 246 shared_ptr_fast<ui_adaptor> create_or_get_ui_adaptor(); 247 // NOLINTNEXTLINE(google-explicit-constructor) 248 operator int() const; 249 250 private: 251 int scroll_amount_from_action( const std::string &action ); 252 void apply_scrollbar(); 253 // This function assumes it's being called from `query` and should 254 // not be made public. 255 void inputfilter(); 256 257 public: 258 // Parameters 259 // TODO change to setters 260 std::string title; 261 std::string text; 262 // basically the same as desc, except it doesn't change based on selection 263 std::string footer_text; 264 std::vector<uilist_entry> entries; 265 266 std::string input_category; 267 std::vector<std::pair<std::string, translation>> additional_actions; 268 269 nc_color border_color; 270 nc_color text_color; 271 nc_color title_color; 272 nc_color hilight_color; 273 nc_color hotkey_color; 274 nc_color disabled_color; 275 276 uilist_callback *callback; 277 278 pos_scalar w_x_setup; 279 pos_scalar w_y_setup; 280 size_scalar w_width_setup; 281 size_scalar w_height_setup; 282 283 int textwidth = 0; 284 285 size_scalar pad_left_setup; 286 size_scalar pad_right_setup; 287 288 // Maximum number of lines to be allocated for displaying descriptions. 289 // This only serves as a hint, not a hard limit, so the number of lines 290 // may still exceed this value when for example the description text is 291 // long enough. 292 int desc_lines_hint = 0; 293 bool desc_enabled = false; 294 295 bool filtering = false; 296 bool filtering_nocase = false; 297 298 // return on selecting disabled entry, default false 299 bool allow_disabled = false; 300 // return UILIST_UNBOUND on keys unbound & unhandled by callback, default false 301 bool allow_anykey = false; 302 // return UILIST_CANCEL on "QUIT" action, default true 303 bool allow_cancel = true; 304 // return UILIST_ADDITIONAL if the input action is inside `additional_actions` 305 // and unhandled by callback, default false. 306 bool allow_additional = false; 307 bool hilight_disabled = false; 308 309 private: 310 report_color_error _color_error = report_color_error::yes; 311 312 public: 313 // Iternal states 314 // TODO make private 315 std::vector<std::string> textformatted; 316 317 catacurses::window window; 318 int w_x = 0; 319 int w_y = 0; 320 int w_width = 0; 321 int w_height = 0; 322 323 int pad_left = 0; 324 int pad_right = 0; 325 326 int vshift = 0; 327 328 int fselected = 0; 329 330 private: 331 std::vector<int> fentries; 332 std::map<input_event, int, std::function<bool( const input_event &, const input_event & )>> 333 keymap { input_event::compare_type_mod_code }; 334 335 weak_ptr_fast<ui_adaptor> ui; 336 337 std::unique_ptr<string_input_popup> filter_popup; 338 std::string filter; 339 340 int max_entry_len = 0; 341 int max_column_len = 0; 342 343 int vmax = 0; 344 345 int desc_lines = 0; 346 347 bool started = false; 348 349 uilist_entry *find_entry_by_coordinate( const point &p ); 350 351 public: 352 // Results 353 // TODO change to getters 354 std::string ret_act; 355 input_event ret_evt; 356 int ret = 0; 357 int selected = 0; 358 }; 359 360 /** 361 * Callback for uilist that pairs menu entries with points 362 * When an entry is selected, view will be centered on the paired point 363 */ 364 class pointmenu_cb : public uilist_callback 365 { 366 private: 367 struct impl_t; 368 369 pimpl<impl_t> impl; 370 public: 371 explicit pointmenu_cb( const std::vector< tripoint > &pts ); 372 ~pointmenu_cb() override; 373 void select( uilist *menu ) override; 374 }; 375 376 #endif // CATA_SRC_UI_H 377