1 #pragma once
2 #ifndef CATA_SRC_STRING_INPUT_POPUP_H
3 #define CATA_SRC_STRING_INPUT_POPUP_H
4 
5 #include <cstddef>
6 #include <cstdint>
7 #include <functional>
8 #include <iosfwd>
9 #include <map>
10 #include <memory>
11 #include <vector>
12 
13 #include "color.h"
14 #include "cursesdef.h"
15 
16 class input_context;
17 class utf8_wrapper;
18 struct point;
19 
20 /**
21  * Shows a window querying the user for input.
22  *
23  * Returns the input that was entered. If the user cancels the input (e.g. by pressing Escape),
24  * an empty string is returned. An empty string may also be returned when the user does not enter
25  * any text and confirms the input (by pressing ENTER).
26  *
27  * Examples:
28  * \code
29     input = string_input_popup().title("Enter something").query();
30     // shows the input field in window w at coordinates (10,20) up to (30,20).
31     input = string_input_popup().window(w, 10, 20, 30).query();
32  * \endcode
33  *
34  * @param title The displayed title, describing what to enter. @ref color_tags can be used.
35  * @param width Width of the input area where the user input appears.
36  * @param input The initially display input. The user can change this.
37  * @param desc An optional text (e.h. help or formatting information) which is displayed
38  * above the input. Color tags can be used.
39  * @param identifier If not empty, this is used to store and retrieve previously entered
40  * text. All calls with the same `identifier` share this history, the history is also stored
41  * when saving the game (see @ref uistate).
42  * @param max_length The maximal length of the text the user can input. More input is simply
43  * ignored and the returned string is never longer than this.
44  * @param only_digits Whether to only allow digits in the string.
45  */
46 class string_input_popup // NOLINT(cata-xy)
47 {
48     private:
49         std::string _title;
50         std::string _text;
51         std::string _description;
52         std::string _identifier;
53         std::string _session_str_entered;
54         nc_color _title_color = c_light_red;
55         nc_color _desc_color = c_green;
56         nc_color _string_color = c_magenta;
57         nc_color _cursor_color = h_light_gray;
58         nc_color _underscore_color = c_light_gray;
59         int _width = 0;
60         int _max_length = -1;
61         bool _only_digits = false;
62         bool _hist_use_uilist = true;
63         bool _ignore_custom_actions = true;
64         int _startx = 0;
65         int _starty = 0;
66         int _endx = 0;
67         int _position = -1;
68         // in output (console) cells, not characters of the string!
69         int shift = 0;
70         int _hist_str_ind = 0;
71         //Counts only when @_hist_use_uilist is false
72         const size_t _hist_max_size = 100;
73 
74         // Cache when using the default window
75         int w_width = 0;
76         int w_height = 0;
77         std::vector<std::string> descformatted;
78         std::vector<std::string> title_split;
79         int titlesize = 0;
80 
81         bool custom_window = false;
82         catacurses::window w;
83 
84         std::unique_ptr<input_context> ctxt_ptr;
85         input_context *ctxt = nullptr;
86 
87         bool _canceled = false;
88         bool _confirmed = false;
89         bool _handled = false;
90 
91         void create_window();
92         void create_context();
93 
94         void show_history( utf8_wrapper &ret );
95         void add_to_history( const std::string &value ) const;
96         void update_input_history( utf8_wrapper &ret, bool up );
97         void draw( const utf8_wrapper &ret, const utf8_wrapper &edit ) const;
98 
99     public:
100         string_input_popup();
101         ~string_input_popup();
102         /**
103          * The title: short string before the actual input field.
104          * It's optional, default is an empty string.
105          */
title(const std::string & value)106         string_input_popup &title( const std::string &value ) {
107             _title = value;
108             return *this;
109         }
110         /**
111          * Set / get the text that can be modified by the user.
112          * Note that canceling the query makes this an empty string.
113          * It's optional default is an empty string.
114          */
115         /**@{*/
116         string_input_popup &text( const std::string &value );
text()117         const std::string &text() const {
118             return _text;
119         }
120         /**@}*/
121         /**
122          * Additional help text, shown below the input box.
123          * It's optional, default is an empty text.
124          */
description(const std::string & value)125         string_input_popup &description( const std::string &value ) {
126             _description = value;
127             return *this;
128         }
129         /**
130          * An identifier to be used to store / get the input
131          * history. If empty (the default), no history will be
132          * available, otherwise the history associated with
133          * the identifier will be available.
134          * If the input is not canceled, the new input is
135          * added to the history.
136          */
identifier(const std::string & value)137         string_input_popup &identifier( const std::string &value ) {
138             _identifier = value;
139             return *this;
140         }
141         /**
142          * Width (in console cells) of the input field itself.
143          */
width(int value)144         string_input_popup &width( int value ) {
145             _width = value;
146             return *this;
147         }
148         /**
149          * Maximal amount of Unicode characters that can be
150          * given by the user. The default is something like 1000.
151          */
max_length(int value)152         string_input_popup &max_length( int value ) {
153             _max_length = value;
154             return *this;
155         }
156         /**
157          * If true, any non-digit input cancels the input. Default is false.
158          */
only_digits(bool value)159         string_input_popup &only_digits( bool value ) {
160             _only_digits = value;
161             return *this;
162         }
163         /**
164          * Make any difference only if @identifier is used.
165          * If true, create UiList window with query history, otherwise use arrow keys at string input to move through history.
166          * Default is true.
167          */
hist_use_uilist(bool value)168         string_input_popup &hist_use_uilist( bool value ) {
169             _hist_use_uilist = value;
170             return *this;
171         }
172         /**
173          * If true and the custom input context returns an input action, the
174          * action is not handled at all and left to be handled by the caller.
175          * Otherwise the action is always handled as an input event to the popup.
176          * The caller can use @ref handled to check whether the last input is handled.
177          */
ignore_custom_actions(const bool value)178         string_input_popup &ignore_custom_actions( const bool value ) {
179             _ignore_custom_actions = value;
180             return *this;
181         }
182         /**
183          * Set the window area where to display the input text. If this is set,
184          * the class will not create a separate window and *only* the editable
185          * text will be printed at the given part of the given window.
186          * Integer parameters define the area (one line) where the editable
187          * text is printed.
188          *
189          * This method only has effect before the default window is initialized.
190          * After that calls to this method are just ignored.
191          */
192         string_input_popup &window( const catacurses::window &w, const point &start, int endx );
193         /**
194          * Set / get the input context that is used to gather user input.
195          * The class will create its own context if none is set here.
196          */
197         /**@{*/
198         string_input_popup &context( input_context &ctxt );
context()199         input_context &context() const {
200             return *ctxt;
201         }
202         /**
203          * Set / get the foreground color of the title.
204          * Optional, default value is c_light_red.
205          */
title_color(const nc_color & color)206         string_input_popup &title_color( const nc_color &color ) {
207             _title_color = color;
208             return *this;
209         }
210         /**
211          * Set / get the foreground color of the description.
212          * Optional, default value is c_green.
213          */
desc_color(const nc_color & color)214         string_input_popup &desc_color( const nc_color &color ) {
215             _desc_color = color;
216             return *this;
217         }
218         /**
219          * Set / get the foreground color of the input string.
220          * Optional, default value is c_magenta.
221          */
string_color(const nc_color & color)222         string_input_popup &string_color( const nc_color &color ) {
223             _string_color = color;
224             return *this;
225         }
226         /**
227          * Set / get the foreground color of the caret.
228          * Optional, default value is h_light_gray.
229          */
cursor_color(const nc_color & color)230         string_input_popup &cursor_color( const nc_color &color ) {
231             _cursor_color = color;
232             return *this;
233         }
234         /**
235          * Set / get the foreground color of the dashed line.
236          * Optional, default value is c_light_gray.
237          */
underscore_color(const nc_color & color)238         string_input_popup &underscore_color( const nc_color &color ) {
239             _underscore_color = color;
240             return *this;
241         }
242         /**@}*/
243         /**
244          * Draws the input box, waits for input (if \p loop is true).
245          * @return @ref text()
246          */
247         /**@{*/
248         void query( bool loop = true, bool draw_only = false );
249         int query_int( bool loop = true, bool draw_only = false );
250         int64_t query_int64_t( bool loop = true, bool draw_only = false );
251         const std::string &query_string( bool loop = true, bool draw_only = false );
252         /**@}*/
253         /**
254          * Whether the input box was canceled via the ESCAPE key (or similar)
255          * If the input was finished via the ENTER key (or similar), this will
256          * return `false`.
257          */
canceled()258         bool canceled() const {
259             return _canceled;
260         }
261         /**
262          * Returns true if query was finished via the ENTER key.
263          */
confirmed()264         bool confirmed() const {
265             return _confirmed;
266         }
267         /**
268          * Returns false if the last input was unhandled. Useful to avoid handling
269          * input already handled by the popup itself.
270          */
handled()271         bool handled() const {
272             return _handled;
273         }
274         /**
275          * Edit values in place. This combines: calls to @ref text to set the
276          * current value, @ref query to get user input and setting the
277          * value back into the parameter object (when the popup was not
278          * canceled). Canceling the popup keeps the value unmodified.
279          */
280         /**@{*/
281         void edit( std::string &value );
282         // Acceptable to use long as part of overload set
283         // NOLINTNEXTLINE(cata-no-long)
284         void edit( long &value );
285         void edit( int &value );
286         /**@}*/
287 
288         std::map<long, std::function<bool()>> callbacks;
289 };
290 
291 #endif // CATA_SRC_STRING_INPUT_POPUP_H
292