39af1e7c223a7202d552230ef1e675642deb88d5
[oweals/minetest.git] / src / gui / guiFormSpecMenu.h
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #pragma once
21
22 #include <utility>
23 #include <stack>
24 #include <unordered_set>
25
26 #include "irrlichttypes_extrabloated.h"
27 #include "inventorymanager.h"
28 #include "modalMenu.h"
29 #include "guiTable.h"
30 #include "network/networkprotocol.h"
31 #include "client/joystick_controller.h"
32 #include "util/string.h"
33 #include "util/enriched_string.h"
34 #include "StyleSpec.h"
35
36 class InventoryManager;
37 class ISimpleTextureSource;
38 class Client;
39
40 typedef enum {
41         f_Button,
42         f_Table,
43         f_TabHeader,
44         f_CheckBox,
45         f_DropDown,
46         f_ScrollBar,
47         f_Unknown
48 } FormspecFieldType;
49
50 typedef enum {
51         quit_mode_no,
52         quit_mode_accept,
53         quit_mode_cancel
54 } FormspecQuitMode;
55
56 struct TextDest
57 {
58         virtual ~TextDest() = default;
59
60         // This is deprecated I guess? -celeron55
61         virtual void gotText(const std::wstring &text) {}
62         virtual void gotText(const StringMap &fields) = 0;
63
64         std::string m_formname;
65 };
66
67 class IFormSource
68 {
69 public:
70         virtual ~IFormSource() = default;
71         virtual const std::string &getForm() const = 0;
72         // Fill in variables in field text
73         virtual std::string resolveText(const std::string &str) { return str; }
74 };
75
76 class GUIFormSpecMenu : public GUIModalMenu
77 {
78         struct ItemSpec
79         {
80                 ItemSpec() = default;
81
82                 ItemSpec(const InventoryLocation &a_inventoryloc,
83                                 const std::string &a_listname,
84                                 s32 a_i) :
85                         inventoryloc(a_inventoryloc),
86                         listname(a_listname),
87                         i(a_i)
88                 {
89                 }
90
91                 bool isValid() const { return i != -1; }
92
93                 InventoryLocation inventoryloc;
94                 std::string listname;
95                 s32 i = -1;
96         };
97
98         struct ListDrawSpec
99         {
100                 ListDrawSpec() = default;
101
102                 ListDrawSpec(const InventoryLocation &a_inventoryloc,
103                                 const std::string &a_listname,
104                                 v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i,
105                                 bool a_real_coordinates):
106                         inventoryloc(a_inventoryloc),
107                         listname(a_listname),
108                         pos(a_pos),
109                         geom(a_geom),
110                         start_item_i(a_start_item_i),
111                         real_coordinates(a_real_coordinates)
112                 {
113                 }
114
115                 InventoryLocation inventoryloc;
116                 std::string listname;
117                 v2s32 pos;
118                 v2s32 geom;
119                 s32 start_item_i;
120                 bool real_coordinates;
121         };
122
123         struct ListRingSpec
124         {
125                 ListRingSpec() = default;
126
127                 ListRingSpec(const InventoryLocation &a_inventoryloc,
128                                 const std::string &a_listname):
129                         inventoryloc(a_inventoryloc),
130                         listname(a_listname)
131                 {
132                 }
133
134                 InventoryLocation inventoryloc;
135                 std::string listname;
136         };
137
138         struct ImageDrawSpec
139         {
140                 ImageDrawSpec():
141                         parent_button(NULL),
142                         clip(false)
143                 {
144                 }
145
146                 ImageDrawSpec(const std::string &a_name,
147                                 const std::string &a_item_name,
148                                 gui::IGUIButton *a_parent_button,
149                                 const v2s32 &a_pos, const v2s32 &a_geom):
150                         name(a_name),
151                         item_name(a_item_name),
152                         parent_button(a_parent_button),
153                         pos(a_pos),
154                         geom(a_geom),
155                         scale(true),
156                         clip(false)
157                 {
158                 }
159
160                 ImageDrawSpec(const std::string &a_name,
161                                 const std::string &a_item_name,
162                                 const v2s32 &a_pos, const v2s32 &a_geom):
163                         name(a_name),
164                         item_name(a_item_name),
165                         parent_button(NULL),
166                         pos(a_pos),
167                         geom(a_geom),
168                         scale(true),
169                         clip(false)
170                 {
171                 }
172
173                 ImageDrawSpec(const std::string &a_name,
174                                 const v2s32 &a_pos, const v2s32 &a_geom, bool clip=false):
175                         name(a_name),
176                         parent_button(NULL),
177                         pos(a_pos),
178                         geom(a_geom),
179                         scale(true),
180                         clip(clip)
181                 {
182                 }
183
184                 ImageDrawSpec(const std::string &a_name,
185                                 const v2s32 &a_pos, const v2s32 &a_geom, const core::rect<s32> &middle, bool clip=false):
186                                 name(a_name),
187                                 parent_button(NULL),
188                                 pos(a_pos),
189                                 geom(a_geom),
190                                 middle(middle),
191                                 scale(true),
192                                 clip(clip)
193                 {
194                 }
195
196                 ImageDrawSpec(const std::string &a_name,
197                                 const v2s32 &a_pos):
198                         name(a_name),
199                         parent_button(NULL),
200                         pos(a_pos),
201                         scale(false),
202                         clip(false)
203                 {
204                 }
205
206                 std::string name;
207                 std::string item_name;
208                 gui::IGUIButton *parent_button;
209                 v2s32 pos;
210                 v2s32 geom;
211                 core::rect<s32> middle;
212                 bool scale;
213                 bool clip;
214         };
215
216         struct FieldSpec
217         {
218                 FieldSpec() = default;
219
220                 FieldSpec(const std::string &name, const std::wstring &label,
221                                 const std::wstring &default_text, int id) :
222                         fname(name),
223                         flabel(label),
224                         fdefault(unescape_enriched(translate_string(default_text))),
225                         fid(id),
226                         send(false),
227                         ftype(f_Unknown),
228                         is_exit(false)
229                 {
230                 }
231
232                 std::string fname;
233                 std::wstring flabel;
234                 std::wstring fdefault;
235                 int fid;
236                 bool send;
237                 FormspecFieldType ftype;
238                 bool is_exit;
239                 core::rect<s32> rect;
240         };
241
242         struct BoxDrawSpec
243         {
244                 BoxDrawSpec(v2s32 a_pos, v2s32 a_geom, irr::video::SColor a_color):
245                         pos(a_pos),
246                         geom(a_geom),
247                         color(a_color)
248                 {
249                 }
250                 v2s32 pos;
251                 v2s32 geom;
252                 irr::video::SColor color;
253         };
254
255         struct TooltipSpec
256         {
257                 TooltipSpec() = default;
258                 TooltipSpec(const std::wstring &a_tooltip, irr::video::SColor a_bgcolor,
259                                 irr::video::SColor a_color):
260                         tooltip(translate_string(a_tooltip)),
261                         bgcolor(a_bgcolor),
262                         color(a_color)
263                 {
264                 }
265
266                 std::wstring tooltip;
267                 irr::video::SColor bgcolor;
268                 irr::video::SColor color;
269         };
270
271         struct StaticTextSpec
272         {
273                 StaticTextSpec():
274                         parent_button(NULL)
275                 {
276                 }
277
278                 StaticTextSpec(const std::wstring &a_text,
279                                 const core::rect<s32> &a_rect):
280                         text(a_text),
281                         rect(a_rect),
282                         parent_button(NULL)
283                 {
284                 }
285
286                 StaticTextSpec(const std::wstring &a_text,
287                                 const core::rect<s32> &a_rect,
288                                 gui::IGUIButton *a_parent_button):
289                         text(a_text),
290                         rect(a_rect),
291                         parent_button(a_parent_button)
292                 {
293                 }
294
295                 std::wstring text;
296                 core::rect<s32> rect;
297                 gui::IGUIButton *parent_button;
298         };
299
300 public:
301         GUIFormSpecMenu(JoystickController *joystick,
302                         gui::IGUIElement* parent, s32 id,
303                         IMenuManager *menumgr,
304                         Client *client,
305                         ISimpleTextureSource *tsrc,
306                         IFormSource* fs_src,
307                         TextDest* txt_dst,
308                         const std::string &formspecPrepend,
309                         bool remap_dbl_click = true);
310
311         ~GUIFormSpecMenu();
312
313         static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
314                 JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
315                 const std::string &formspecPrepend);
316
317         void setFormSpec(const std::string &formspec_string,
318                         const InventoryLocation &current_inventory_location)
319         {
320                 m_formspec_string = formspec_string;
321                 m_current_inventory_location = current_inventory_location;
322                 regenerateGui(m_screensize_old);
323         }
324
325         const InventoryLocation &getFormspecLocation()
326         {
327                 return m_current_inventory_location;
328         }
329
330         void setFormspecPrepend(const std::string &formspecPrepend)
331         {
332                 m_formspec_prepend = formspecPrepend;
333         }
334
335         // form_src is deleted by this GUIFormSpecMenu
336         void setFormSource(IFormSource *form_src)
337         {
338                 delete m_form_src;
339                 m_form_src = form_src;
340         }
341
342         // text_dst is deleted by this GUIFormSpecMenu
343         void setTextDest(TextDest *text_dst)
344         {
345                 delete m_text_dst;
346                 m_text_dst = text_dst;
347         }
348
349         void allowClose(bool value)
350         {
351                 m_allowclose = value;
352         }
353
354         void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0))
355         {
356                 m_lock = lock;
357                 m_lockscreensize = basescreensize;
358         }
359
360         void removeChildren();
361         void setInitialFocus();
362
363         void setFocus(const std::string &elementname)
364         {
365                 m_focused_element = elementname;
366         }
367
368         /*
369                 Remove and re-add (or reposition) stuff
370         */
371         void regenerateGui(v2u32 screensize);
372
373         ItemSpec getItemAtPos(v2s32 p) const;
374         void drawList(const ListDrawSpec &s, int layer, bool &item_hovered);
375         void drawSelectedItem();
376         void drawMenu();
377         void updateSelectedItem();
378         ItemStack verifySelectedItem();
379
380         void acceptInput(FormspecQuitMode quitmode);
381         bool preprocessEvent(const SEvent& event);
382         bool OnEvent(const SEvent& event);
383         bool doPause;
384         bool pausesGame() { return doPause; }
385
386         GUITable* getTable(const std::string &tablename);
387         std::vector<std::string>* getDropDownValues(const std::string &name);
388
389 #ifdef __ANDROID__
390         bool getAndroidUIInput();
391 #endif
392
393 protected:
394         v2s32 getBasePos() const
395         {
396                         return padding + offset + AbsoluteRect.UpperLeftCorner;
397         }
398         std::wstring getLabelByID(s32 id);
399         std::string getNameByID(s32 id);
400         v2s32 getElementBasePos(bool absolute,
401                         const std::vector<std::string> *v_pos);
402         v2s32 getRealCoordinateBasePos(bool absolute,
403                         const std::vector<std::string> &v_pos);
404         v2s32 getRealCoordinateGeometry(const std::vector<std::string> &v_geom);
405
406         std::unordered_map<std::string, StyleSpec> theme_by_type;
407         std::unordered_map<std::string, StyleSpec> theme_by_name;
408         std::unordered_set<std::string> property_warned;
409
410         StyleSpec getStyleForElement(const std::string &type,
411                         const std::string &name="", const std::string &parent_type="");
412
413         v2s32 padding;
414         v2f32 spacing;
415         v2s32 imgsize;
416         v2s32 offset;
417         v2f32 pos_offset;
418         std::stack<v2f32> container_stack;
419
420         InventoryManager *m_invmgr;
421         ISimpleTextureSource *m_tsrc;
422         Client *m_client;
423
424         std::string m_formspec_string;
425         std::string m_formspec_prepend;
426         InventoryLocation m_current_inventory_location;
427
428         std::vector<ListDrawSpec> m_inventorylists;
429         std::vector<ListRingSpec> m_inventory_rings;
430         std::vector<ImageDrawSpec> m_backgrounds;
431         std::vector<ImageDrawSpec> m_images;
432         std::vector<ImageDrawSpec> m_itemimages;
433         std::vector<BoxDrawSpec> m_boxes;
434         std::unordered_map<std::string, bool> field_close_on_enter;
435         std::vector<FieldSpec> m_fields;
436         std::vector<StaticTextSpec> m_static_texts;
437         std::vector<std::pair<FieldSpec,GUITable*> > m_tables;
438         std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
439         std::map<std::string, TooltipSpec> m_tooltips;
440         std::vector<std::pair<irr::core::rect<s32>, TooltipSpec>> m_tooltip_rects;
441         std::vector<std::pair<FieldSpec,gui::IGUIScrollBar*> > m_scrollbars;
442         std::vector<std::pair<FieldSpec, std::vector<std::string> > > m_dropdowns;
443
444         ItemSpec *m_selected_item = nullptr;
445         u16 m_selected_amount = 0;
446         bool m_selected_dragging = false;
447         ItemStack m_selected_swap;
448
449         gui::IGUIStaticText *m_tooltip_element = nullptr;
450
451         u64 m_tooltip_show_delay;
452         bool m_tooltip_append_itemname;
453         u64 m_hovered_time = 0;
454         s32 m_old_tooltip_id = -1;
455
456         bool m_auto_place = false;
457
458         bool m_allowclose = true;
459         bool m_lock = false;
460         v2u32 m_lockscreensize;
461
462         bool m_bgfullscreen;
463         bool m_slotborder;
464         video::SColor m_bgcolor;
465         video::SColor m_fullscreen_bgcolor;
466         video::SColor m_slotbg_n;
467         video::SColor m_slotbg_h;
468         video::SColor m_slotbordercolor;
469         video::SColor m_default_tooltip_bgcolor;
470         video::SColor m_default_tooltip_color;
471
472         
473 private:
474         IFormSource        *m_form_src;
475         TextDest           *m_text_dst;
476         u16                 m_formspec_version = 1;
477         std::string         m_focused_element = "";
478         JoystickController *m_joystick;
479
480         typedef struct {
481                 bool explicit_size;
482                 bool real_coordinates;
483                 v2f invsize;
484                 v2s32 size;
485                 v2f32 offset;
486                 v2f32 anchor;
487                 core::rect<s32> rect;
488                 v2s32 basepos;
489                 v2u32 screensize;
490                 std::string focused_fieldname;
491                 GUITable::TableOptions table_options;
492                 GUITable::TableColumns table_columns;
493                 // used to restore table selection/scroll/treeview state
494                 std::unordered_map<std::string, GUITable::DynamicData> table_dyndata;
495         } parserData;
496
497         typedef struct {
498                 bool key_up;
499                 bool key_down;
500                 bool key_enter;
501                 bool key_escape;
502         } fs_key_pendig;
503
504         fs_key_pendig current_keys_pending;
505         std::string current_field_enter_pending = "";
506
507         void parseElement(parserData* data, const std::string &element);
508
509         void parseSize(parserData* data, const std::string &element);
510         void parseContainer(parserData* data, const std::string &element);
511         void parseContainerEnd(parserData* data);
512         void parseList(parserData* data, const std::string &element);
513         void parseListRing(parserData* data, const std::string &element);
514         void parseCheckbox(parserData* data, const std::string &element);
515         void parseImage(parserData* data, const std::string &element);
516         void parseItemImage(parserData* data, const std::string &element);
517         void parseButton(parserData* data, const std::string &element,
518                         const std::string &typ);
519         void parseBackground(parserData* data, const std::string &element);
520         void parseTableOptions(parserData* data, const std::string &element);
521         void parseTableColumns(parserData* data, const std::string &element);
522         void parseTable(parserData* data, const std::string &element);
523         void parseTextList(parserData* data, const std::string &element);
524         void parseDropDown(parserData* data, const std::string &element);
525         void parseFieldCloseOnEnter(parserData *data, const std::string &element);
526         void parsePwdField(parserData* data, const std::string &element);
527         void parseField(parserData* data, const std::string &element, const std::string &type);
528         void createTextField(parserData *data, FieldSpec &spec,
529                 core::rect<s32> &rect, bool is_multiline);
530         void parseSimpleField(parserData* data,std::vector<std::string> &parts);
531         void parseTextArea(parserData* data,std::vector<std::string>& parts,
532                         const std::string &type);
533         void parseHyperText(parserData *data, const std::string &element);
534         void parseLabel(parserData* data, const std::string &element);
535         void parseVertLabel(parserData* data, const std::string &element);
536         void parseImageButton(parserData* data, const std::string &element,
537                         const std::string &type);
538         void parseItemImageButton(parserData* data, const std::string &element);
539         void parseTabHeader(parserData* data, const std::string &element);
540         void parseBox(parserData* data, const std::string &element);
541         void parseBackgroundColor(parserData* data, const std::string &element);
542         void parseListColors(parserData* data, const std::string &element);
543         void parseTooltip(parserData* data, const std::string &element);
544         bool parseVersionDirect(const std::string &data);
545         bool parseSizeDirect(parserData* data, const std::string &element);
546         void parseScrollBar(parserData* data, const std::string &element);
547         bool parsePositionDirect(parserData *data, const std::string &element);
548         void parsePosition(parserData *data, const std::string &element);
549         bool parseAnchorDirect(parserData *data, const std::string &element);
550         void parseAnchor(parserData *data, const std::string &element);
551         bool parseStyle(parserData *data, const std::string &element, bool style_type);
552
553         void tryClose();
554
555         void showTooltip(const std::wstring &text, const irr::video::SColor &color,
556                 const irr::video::SColor &bgcolor);
557
558         /**
559          * check if event is part of a double click
560          * @param event event to evaluate
561          * @return true/false if a doubleclick was detected
562          */
563         bool DoubleClickDetection(const SEvent event);
564
565         struct clickpos
566         {
567                 v2s32 pos;
568                 s64 time;
569         };
570         clickpos m_doubleclickdetect[2];
571
572         int m_btn_height;
573         gui::IGUIFont *m_font = nullptr;
574
575         /* If true, remap a double-click (or double-tap) action to ESC. This is so
576          * that, for example, Android users can double-tap to close a formspec.
577         *
578          * This value can (currently) only be set by the class constructor
579          * and the default value for the setting is true.
580          */
581         bool m_remap_dbl_click;
582 };
583
584 class FormspecFormSource: public IFormSource
585 {
586 public:
587         FormspecFormSource(const std::string &formspec):
588                 m_formspec(formspec)
589         {
590         }
591
592         ~FormspecFormSource() = default;
593
594         void setForm(const std::string &formspec)
595         {
596                 m_formspec = formspec;
597         }
598
599         const std::string &getForm() const
600         {
601                 return m_formspec;
602         }
603
604         std::string m_formspec;
605 };