]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiKeyChangeMenu.cpp
Add more visual feedback for button states (#8916)
[dragonfireclient.git] / src / gui / guiKeyChangeMenu.cpp
1 /*
2  Minetest
3  Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4  Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
5  Copyright (C) 2013 teddydestodes <derkomtur@schattengang.net>
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation; either version 2.1 of the License, or
10  (at your option) any later version.
11
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU Lesser General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License along
18  with this program; if not, write to the Free Software Foundation, Inc.,
19  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "guiKeyChangeMenu.h"
23 #include "debug.h"
24 #include "guiButton.h"
25 #include "serialization.h"
26 #include <string>
27 #include <IGUICheckBox.h>
28 #include <IGUIEditBox.h>
29 #include <IGUIButton.h>
30 #include <IGUIStaticText.h>
31 #include <IGUIFont.h>
32 #include "settings.h"
33 #include <algorithm>
34
35 #include "mainmenumanager.h"  // for g_gamecallback
36
37 #define KMaxButtonPerColumns 12
38
39 extern MainGameCallback *g_gamecallback;
40
41 enum
42 {
43         GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
44         // buttons
45         GUI_ID_KEY_FORWARD_BUTTON,
46         GUI_ID_KEY_BACKWARD_BUTTON,
47         GUI_ID_KEY_LEFT_BUTTON,
48         GUI_ID_KEY_RIGHT_BUTTON,
49         GUI_ID_KEY_USE_BUTTON,
50         GUI_ID_KEY_FLY_BUTTON,
51         GUI_ID_KEY_FAST_BUTTON,
52         GUI_ID_KEY_JUMP_BUTTON,
53         GUI_ID_KEY_NOCLIP_BUTTON,
54         GUI_ID_KEY_PITCH_MOVE,
55         GUI_ID_KEY_CHAT_BUTTON,
56         GUI_ID_KEY_CMD_BUTTON,
57         GUI_ID_KEY_CMD_LOCAL_BUTTON,
58         GUI_ID_KEY_CONSOLE_BUTTON,
59         GUI_ID_KEY_SNEAK_BUTTON,
60         GUI_ID_KEY_DROP_BUTTON,
61         GUI_ID_KEY_INVENTORY_BUTTON,
62         GUI_ID_KEY_HOTBAR_PREV_BUTTON,
63         GUI_ID_KEY_HOTBAR_NEXT_BUTTON,
64         GUI_ID_KEY_MUTE_BUTTON,
65         GUI_ID_KEY_DEC_VOLUME_BUTTON,
66         GUI_ID_KEY_INC_VOLUME_BUTTON,
67         GUI_ID_KEY_RANGE_BUTTON,
68         GUI_ID_KEY_ZOOM_BUTTON,
69         GUI_ID_KEY_CAMERA_BUTTON,
70         GUI_ID_KEY_MINIMAP_BUTTON,
71         GUI_ID_KEY_SCREENSHOT_BUTTON,
72         GUI_ID_KEY_CHATLOG_BUTTON,
73         GUI_ID_KEY_HUD_BUTTON,
74         GUI_ID_KEY_FOG_BUTTON,
75         GUI_ID_KEY_DEC_RANGE_BUTTON,
76         GUI_ID_KEY_INC_RANGE_BUTTON,
77         GUI_ID_KEY_AUTOFWD_BUTTON,
78         // other
79         GUI_ID_CB_AUX1_DESCENDS,
80         GUI_ID_CB_DOUBLETAP_JUMP,
81         GUI_ID_CB_AUTOJUMP,
82 };
83
84 GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
85                                 gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
86 GUIModalMenu(env, parent, id, menumgr)
87 {
88         init_keys();
89         for (key_setting *ks : key_settings)
90                 key_used.push_back(ks->key);
91 }
92
93 GUIKeyChangeMenu::~GUIKeyChangeMenu()
94 {
95         removeChildren();
96
97         for (key_setting *ks : key_settings) {
98                 delete[] ks->button_name;
99                 delete ks;
100         }
101         key_settings.clear();
102 }
103
104 void GUIKeyChangeMenu::removeChildren()
105 {
106         const core::list<gui::IGUIElement*> &children = getChildren();
107         core::list<gui::IGUIElement*> children_copy;
108         for (gui::IGUIElement*i : children) {
109                 children_copy.push_back(i);
110         }
111
112         for (gui::IGUIElement *i : children_copy) {
113                 i->remove();
114         }
115 }
116
117 void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
118 {
119         removeChildren();
120
121         const float s = m_gui_scale;
122         DesiredRect = core::rect<s32>(
123                 screensize.X / 2 - 835 * s / 2,
124                 screensize.Y / 2 - 430 * s / 2,
125                 screensize.X / 2 + 835 * s / 2,
126                 screensize.Y / 2 + 430 * s / 2
127         );
128         recalculateAbsolutePosition(false);
129
130         v2s32 size = DesiredRect.getSize();
131         v2s32 topleft(0, 0);
132
133         {
134                 core::rect<s32> rect(0, 0, 600 * s, 40 * s);
135                 rect += topleft + v2s32(25 * s, 3 * s);
136                 //gui::IGUIStaticText *t =
137                 const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
138                 Environment->addStaticText(text,
139                                                                    rect, false, true, this, -1);
140                 delete[] text;
141                 //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
142         }
143
144         // Build buttons
145
146         v2s32 offset(25 * s, 60 * s);
147
148         for(size_t i = 0; i < key_settings.size(); i++)
149         {
150                 key_setting *k = key_settings.at(i);
151                 {
152                         core::rect<s32> rect(0, 0, 150 * s, 20 * s);
153                         rect += topleft + v2s32(offset.X, offset.Y);
154                         Environment->addStaticText(k->button_name, rect, false, true, this, -1);
155                 }
156
157                 {
158                         core::rect<s32> rect(0, 0, 100 * s, 30 * s);
159                         rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s);
160                         const wchar_t *text = wgettext(k->key.name());
161                         k->button = GUIButton::addButton(Environment, rect, this, k->id, text);
162                         delete[] text;
163                 }
164                 if ((i + 1) % KMaxButtonPerColumns == 0) {
165                         offset.X += 260 * s;
166                         offset.Y = 60 * s;
167                 } else {
168                         offset += v2s32(0, 25 * s);
169                 }
170         }
171
172         {
173                 s32 option_x = offset.X;
174                 s32 option_y = offset.Y + 5 * s;
175                 u32 option_w = 180 * s;
176                 {
177                         core::rect<s32> rect(0, 0, option_w, 30 * s);
178                         rect += topleft + v2s32(option_x, option_y);
179                         const wchar_t *text = wgettext("\"Special\" = climb down");
180                         Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
181                                         GUI_ID_CB_AUX1_DESCENDS, text);
182                         delete[] text;
183                 }
184                 offset += v2s32(0, 25 * s);
185         }
186
187         {
188                 s32 option_x = offset.X;
189                 s32 option_y = offset.Y + 5 * s;
190                 u32 option_w = 280 * s;
191                 {
192                         core::rect<s32> rect(0, 0, option_w, 30 * s);
193                         rect += topleft + v2s32(option_x, option_y);
194                         const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
195                         Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
196                                         GUI_ID_CB_DOUBLETAP_JUMP, text);
197                         delete[] text;
198                 }
199                 offset += v2s32(0, 25 * s);
200         }
201
202         {
203                 s32 option_x = offset.X;
204                 s32 option_y = offset.Y + 5 * s;
205                 u32 option_w = 280;
206                 {
207                         core::rect<s32> rect(0, 0, option_w, 30 * s);
208                         rect += topleft + v2s32(option_x, option_y);
209                         const wchar_t *text = wgettext("Automatic jumping");
210                         Environment->addCheckBox(g_settings->getBool("autojump"), rect, this,
211                                         GUI_ID_CB_AUTOJUMP, text);
212                         delete[] text;
213                 }
214                 offset += v2s32(0, 25);
215         }
216
217         {
218                 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
219                 rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s);
220                 const wchar_t *text =  wgettext("Save");
221                 GUIButton::addButton(Environment, rect, this, GUI_ID_BACK_BUTTON, text);
222                 delete[] text;
223         }
224         {
225                 core::rect<s32> rect(0, 0, 100 * s, 30 * s);
226                 rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s);
227                 const wchar_t *text = wgettext("Cancel");
228                 GUIButton::addButton(Environment, rect, this, GUI_ID_ABORT_BUTTON, text);
229                 delete[] text;
230         }
231 }
232
233 void GUIKeyChangeMenu::drawMenu()
234 {
235         gui::IGUISkin* skin = Environment->getSkin();
236         if (!skin)
237                 return;
238         video::IVideoDriver* driver = Environment->getVideoDriver();
239
240         video::SColor bgcolor(140, 0, 0, 0);
241         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
242
243         gui::IGUIElement::draw();
244 }
245
246 bool GUIKeyChangeMenu::acceptInput()
247 {
248         for (key_setting *k : key_settings) {
249                 g_settings->set(k->setting_name, k->key.sym());
250         }
251
252         {
253                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
254                 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
255                         g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
256         }
257         {
258                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
259                 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
260                         g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
261         }
262         {
263                 gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUTOJUMP);
264                 if(e && e->getType() == gui::EGUIET_CHECK_BOX)
265                         g_settings->setBool("autojump", ((gui::IGUICheckBox*)e)->isChecked());
266         }
267
268         clearKeyCache();
269
270         g_gamecallback->signalKeyConfigChange();
271
272         return true;
273 }
274
275 bool GUIKeyChangeMenu::resetMenu()
276 {
277         if (activeKey >= 0)
278         {
279                 for (key_setting *k : key_settings) {
280                         if (k->id == activeKey) {
281                                 const wchar_t *text = wgettext(k->key.name());
282                                 k->button->setText(text);
283                                 delete[] text;
284                                 break;
285                         }
286                 }
287                 activeKey = -1;
288                 return false;
289         }
290         return true;
291 }
292 bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
293 {
294         if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
295                         && event.KeyInput.PressedDown) {
296
297                 bool prefer_character = shift_down;
298                 KeyPress kp(event.KeyInput, prefer_character);
299
300                 bool shift_went_down = false;
301                 if(!shift_down &&
302                                 (event.KeyInput.Key == irr::KEY_SHIFT ||
303                                 event.KeyInput.Key == irr::KEY_LSHIFT ||
304                                 event.KeyInput.Key == irr::KEY_RSHIFT))
305                         shift_went_down = true;
306
307                 // Remove Key already in use message
308                 if(this->key_used_text)
309                 {
310                         this->key_used_text->remove();
311                         this->key_used_text = NULL;
312                 }
313                 // Display Key already in use message
314                 if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
315                 {
316                         core::rect < s32 > rect(0, 0, 600, 40);
317                         rect += v2s32(0, 0) + v2s32(25, 30);
318                         const wchar_t *text = wgettext("Key already in use");
319                         this->key_used_text = Environment->addStaticText(text,
320                                         rect, false, true, this, -1);
321                         delete[] text;
322                         //infostream << "Key already in use" << std::endl;
323                 }
324
325                 // But go on
326                 {
327                         key_setting *k = NULL;
328                         for (key_setting *ks : key_settings) {
329                                 if (ks->id == activeKey) {
330                                         k = ks;
331                                         break;
332                                 }
333                         }
334                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
335                         k->key = kp;
336                         const wchar_t *text = wgettext(k->key.name());
337                         k->button->setText(text);
338                         delete[] text;
339
340                         this->key_used.push_back(kp);
341
342                         // Allow characters made with shift
343                         if(shift_went_down){
344                                 shift_down = true;
345                                 return false;
346                         }
347
348                         activeKey = -1;
349                         return true;
350                 }
351         } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
352                         && event.KeyInput.PressedDown
353                         && event.KeyInput.Key == irr::KEY_ESCAPE) {
354                 quitMenu();
355                 return true;
356         } else if (event.EventType == EET_GUI_EVENT) {
357                 if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
358                         && isVisible())
359                 {
360                         if (!canTakeFocus(event.GUIEvent.Element))
361                         {
362                                 dstream << "GUIMainMenu: Not allowing focus change."
363                                 << std::endl;
364                                 // Returning true disables focus change
365                                 return true;
366                         }
367                 }
368                 if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
369                 {
370                         switch (event.GUIEvent.Caller->getID())
371                         {
372                                 case GUI_ID_BACK_BUTTON: //back
373                                         acceptInput();
374                                         quitMenu();
375                                         return true;
376                                 case GUI_ID_ABORT_BUTTON: //abort
377                                         quitMenu();
378                                         return true;
379                                 default:
380                                         key_setting *k = NULL;
381
382                                         for (key_setting *ks : key_settings) {
383                                                 if (ks->id == event.GUIEvent.Caller->getID()) {
384                                                         k = ks;
385                                                         break;
386                                                 }
387                                         }
388                                         FATAL_ERROR_IF(k == NULL, "Key setting not found");
389
390                                         resetMenu();
391                                         shift_down = false;
392                                         activeKey = event.GUIEvent.Caller->getID();
393                                         const wchar_t *text = wgettext("press key");
394                                         k->button->setText(text);
395                                         delete[] text;
396                                         this->key_used.erase(std::remove(this->key_used.begin(),
397                                                         this->key_used.end(), k->key), this->key_used.end());
398                                         break;
399                         }
400                         Environment->setFocus(this);
401                 }
402         }
403         return Parent ? Parent->OnEvent(event) : false;
404 }
405
406 void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
407 {
408         key_setting *k = new key_setting;
409         k->id = id;
410
411         k->button_name = button_name;
412         k->setting_name = setting_name;
413         k->key = getKeySetting(k->setting_name.c_str());
414         key_settings.push_back(k);
415 }
416
417 void GUIKeyChangeMenu::init_keys()
418 {
419         this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
420         this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
421         this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
422         this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
423         this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Special"),          "keymap_special1");
424         this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
425         this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
426         this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
427         this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
428         this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"),      "keymap_hotbar_previous");
429         this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"),       "keymap_hotbar_next");
430         this->add_key(GUI_ID_KEY_ZOOM_BUTTON,      wgettext("Zoom"),             "keymap_zoom");
431         this->add_key(GUI_ID_KEY_CAMERA_BUTTON,    wgettext("Change camera"),    "keymap_camera_mode");
432         this->add_key(GUI_ID_KEY_MINIMAP_BUTTON,   wgettext("Toggle minimap"),   "keymap_minimap");
433         this->add_key(GUI_ID_KEY_FLY_BUTTON,       wgettext("Toggle fly"),       "keymap_freemove");
434         this->add_key(GUI_ID_KEY_PITCH_MOVE,       wgettext("Toggle pitchmove"), "keymap_pitchmove");
435         this->add_key(GUI_ID_KEY_FAST_BUTTON,      wgettext("Toggle fast"),      "keymap_fastmove");
436         this->add_key(GUI_ID_KEY_NOCLIP_BUTTON,    wgettext("Toggle noclip"),    "keymap_noclip");
437         this->add_key(GUI_ID_KEY_MUTE_BUTTON,      wgettext("Mute"),             "keymap_mute");
438         this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"),      "keymap_decrease_volume");
439         this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"),      "keymap_increase_volume");
440         this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON,   wgettext("Autoforward"),      "keymap_autoforward");
441         this->add_key(GUI_ID_KEY_CHAT_BUTTON,      wgettext("Chat"),             "keymap_chat");
442         this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON,wgettext("Screenshot"),       "keymap_screenshot");
443         this->add_key(GUI_ID_KEY_RANGE_BUTTON,     wgettext("Range select"),     "keymap_rangeselect");
444         this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"),       "keymap_decrease_viewing_range_min");
445         this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"),       "keymap_increase_viewing_range_min");
446         this->add_key(GUI_ID_KEY_CONSOLE_BUTTON,   wgettext("Console"),          "keymap_console");
447         this->add_key(GUI_ID_KEY_CMD_BUTTON,       wgettext("Command"),          "keymap_cmd");
448         this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"),    "keymap_cmd_local");
449         this->add_key(GUI_ID_KEY_HUD_BUTTON,       wgettext("Toggle HUD"),       "keymap_toggle_hud");
450         this->add_key(GUI_ID_KEY_CHATLOG_BUTTON,   wgettext("Toggle chat log"),  "keymap_toggle_chat");
451         this->add_key(GUI_ID_KEY_FOG_BUTTON,       wgettext("Toggle fog"),       "keymap_toggle_fog");
452 }