1 /*
2    Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #include "gui/widgets/text_box_base.hpp"
18 
19 namespace gui2
20 {
21 namespace implementation
22 {
23 struct builder_text_box;
24 }
25 
26 // ------------ WIDGET -----------{
27 
28 /**
29  * Class for text input history.
30  *
31  * The history of text items can be stored in the preferences. This class
32  * handles that. Every item needs an id by which the history is loaded and
33  * saved.
34  */
35 class text_history
36 {
37 public:
38 	/**
39 	 * Gets history that matches id.
40 	 *
41 	 * @param id                  The id of the history to look for.
42 	 * @param enabled             The enabled state of the history.
43 	 *
44 	 * @returns                   The history object.
45 	 */
46 	static text_history get_history(const std::string& id, const bool enabled);
47 
text_history()48 	text_history() : history_(0), pos_(0), enabled_(false)
49 	{
50 	}
51 
52 	/**
53 	 * Push string into the history.
54 	 *
55 	 * If the string is empty or the same as the last item in the history this
56 	 * function is a nop.
57 	 *
58 	 * @param text                   The text to push in the history.
59 	 */
60 	void push(const std::string& text);
61 
62 	/**
63 	 * One step up in the history.
64 	 *
65 	 * Pushes text to the history if at the end.
66 	 *
67 	 * @param text                The text to push in the history.
68 	 *
69 	 * @returns                   The current value of the history.
70 	 */
71 	std::string up(const std::string& text = "");
72 
73 	/**
74 	 * One step down in the history.
75 	 *
76 	 * Pushes text to the history if at the end.
77 	 *
78 	 * @param text                The text to push in the history.
79 	 *
80 	 * @returns                   The current value of the history.
81 	 */
82 	std::string down(const std::string& text = "");
83 
84 	/**
85 	 * Gets the current history value.
86 	 *
87 	 * @returns                   If enabled return the current history
88 	 *                            position, otherwise an empty string is
89 	 *                            returned.
90 	 */
91 	std::string get_value() const;
92 
93 	/***** ***** ***** setters / getters for members ***** ****** *****/
94 
set_enabled(bool enabled=true)95 	void set_enabled(bool enabled = true)
96 	{
97 		enabled_ = enabled;
98 	}
get_enabled() const99 	bool get_enabled() const
100 	{
101 		return enabled_;
102 	}
103 
104 private:
text_history(std::vector<std::string> * history,const bool enabled)105 	text_history(std::vector<std::string>* history, const bool enabled)
106 		: history_(history), pos_(history->size()), enabled_(enabled)
107 	{
108 	}
109 
110 	/** The items in the history. */
111 	std::vector<std::string>* history_;
112 
113 	/** The current position in the history. */
114 	unsigned pos_;
115 
116 	/** Is the history enabled. */
117 	bool enabled_;
118 };
119 
120 /** Class for a single line text area. */
121 class text_box : public text_box_base
122 {
123 	friend struct implementation::builder_text_box;
124 
125 public:
126 	explicit text_box(const implementation::builder_styled_widget& builder);
127 
128 	/** Saves the text in the widget to the history. */
save_to_history()129 	void save_to_history()
130 	{
131 		history_.push(get_value());
132 	}
133 
134 	/***** ***** ***** setters / getters for members ***** ****** *****/
135 
set_history(const std::string & id)136 	void set_history(const std::string& id)
137 	{
138 		history_ = text_history::get_history(id, true);
139 	}
140 
set_max_input_length(const size_t length)141 	void set_max_input_length(const size_t length)
142 	{
143 		max_input_length_ = length;
144 	}
145 
set_hint_data(const std::string & text,const std::string & image)146 	void set_hint_data(const std::string& text, const std::string& image)
147 	{
148 		hint_text_ = text;
149 		hint_image_ = image;
150 
151 		update_canvas();
152 	}
153 
clear()154 	void clear()
155 	{
156 		set_value("");
157 	}
158 
159 protected:
160 	/***** ***** ***** ***** layout functions ***** ***** ***** *****/
161 
162 	/** See @ref widget::place. */
163 	virtual void place(const point& origin, const point& size) override;
164 
165 	/***** ***** ***** ***** Inherited ***** ***** ***** *****/
166 
167 	/** See @ref styled_widget::update_canvas. */
168 	virtual void update_canvas() override;
169 
170 	/** Inherited from text_box_base. */
goto_end_of_line(const bool select=false)171 	void goto_end_of_line(const bool select = false) override
172 	{
173 		goto_end_of_data(select);
174 	}
175 
176 	/** Inherited from text_box_base. */
goto_start_of_line(const bool select=false)177 	void goto_start_of_line(const bool select = false) override
178 	{
179 		goto_start_of_data(select);
180 	}
181 
182 	/** Inherited from text_box_base. */
183 	void delete_char(const bool before_cursor) override;
184 
185 	/** Inherited from text_box_base. */
186 	void delete_selection() override;
187 
188 	void handle_mouse_selection(point mouse, const bool start_selection);
189 
190 private:
191 	/** The history text for this widget. */
192 	text_history history_;
193 
194 	/** The maximum length of the text input. */
195 	size_t max_input_length_;
196 
197 	/**
198 	 * The x offset in the widget where the text starts.
199 	 *
200 	 * This value is needed to translate a location in the widget to a location
201 	 * in the text.
202 	 */
203 	unsigned text_x_offset_;
204 
205 	/**
206 	 * The y offset in the widget where the text starts.
207 	 *
208 	 * Needed to determine whether a click is on the text.
209 	 */
210 	unsigned text_y_offset_;
211 
212 	/**
213 	 * The height of the text itself.
214 	 *
215 	 * Needed to determine whether a click is on the text.
216 	 */
217 	unsigned text_height_;
218 
219 	/** Updates text_x_offset_ and text_y_offset_. */
220 	void update_offsets();
221 
222 	/** Is the mouse in dragging mode, this affects selection in mouse move */
223 	bool dragging_;
224 
225 	/** Helper text to display (such as "Search") if the text box is empty. */
226 	std::string hint_text_;
227 
228 	/** Image (such as a magnifying glass) that accompanies the help text. */
229 	std::string hint_image_;
230 
231 	/**
232 	 * Inherited from text_box_base.
233 	 *
234 	 * Unmodified                 Unhandled.
235 	 * Control                    Ignored.
236 	 * Shift                      Ignored.
237 	 * Alt                        Ignored.
238 	 */
handle_key_up_arrow(SDL_Keymod,bool &)239 	void handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& /*handled*/) override
240 	{
241 	}
242 
243 	/**
244 	 * Inherited from text_box_base.
245 	 *
246 	 * Unmodified                 Unhandled.
247 	 * Control                    Ignored.
248 	 * Shift                      Ignored.
249 	 * Alt                        Ignored.
250 	 */
handle_key_down_arrow(SDL_Keymod,bool &)251 	void handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& /*handled*/) override
252 	{
253 	}
254 
255 	/**
256 	 * Goes one item up in the history.
257 	 *
258 	 * @returns                   True if there's a history, false otherwise.
259 	 */
260 	bool history_up();
261 
262 	/**
263 	 * Goes one item down in the history.
264 	 *
265 	 * @returns                   True if there's a history, false otherwise.
266 	 */
267 	bool history_down();
268 
269 	/** Inherited from text_box_base. */
270 	void handle_key_tab(SDL_Keymod modifier, bool& handled) override;
271 
272 	/** Inherited from text_box_base. */
273 	void handle_key_clear_line(SDL_Keymod modifier, bool& handled) override;
274 
275 public:
276 	/** Static type getter that does not rely on the widget being constructed. */
277 	static const std::string& type();
278 
279 private:
280 	/** Inherited from styled_widget, implemented by REGISTER_WIDGET. */
281 	virtual const std::string& get_control_type() const override;
282 
283 	/***** ***** ***** signal handlers ***** ****** *****/
284 
285 	void signal_handler_mouse_motion(const event::ui_event event,
286 									 bool& handled,
287 									 const point& coordinate);
288 
289 	void signal_handler_left_button_down(const event::ui_event event,
290 										 bool& handled);
291 
292 	void signal_handler_left_button_up(const event::ui_event event,
293 									   bool& handled);
294 
295 	void signal_handler_left_button_double_click(const event::ui_event event,
296 												 bool& handled);
297 };
298 
299 // }---------- DEFINITION ---------{
300 
301 struct text_box_definition : public styled_widget_definition
302 {
303 	explicit text_box_definition(const config& cfg);
304 
305 	struct resolution : public resolution_definition
306 	{
307 		explicit resolution(const config& cfg);
308 
309 		typed_formula<unsigned> text_x_offset;
310 		typed_formula<unsigned> text_y_offset;
311 	};
312 };
313 
314 // }---------- BUILDER -----------{
315 
316 namespace implementation
317 {
318 
319 struct builder_text_box : public builder_styled_widget
320 {
321 public:
322 	explicit builder_text_box(const config& cfg);
323 
324 	using builder_styled_widget::build;
325 
326 	widget* build() const;
327 
328 	std::string history;
329 
330 	size_t max_input_length;
331 
332 	t_string hint_text;
333 	std::string hint_image;
334 };
335 
336 } // namespace implementation
337 
338 // }------------ END --------------
339 
340 } // namespace gui2
341