RigsofRods
Soft-body Physics Simulation
GUI_GameControls.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2020 tritonas00
4  Copyright 2021 Petr Ohlidal
5 
6  For more information, see http://www.rigsofrods.org/
7 
8  Rigs of Rods is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License version 3, as
10  published by the Free Software Foundation.
11 
12  Rigs of Rods 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 General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 
22 #include "GUI_GameControls.h"
23 
24 #include "Actor.h"
25 #include "Application.h"
26 #include "Language.h"
27 #include "OgreImGui.h"
28 #include "GUI_LoadingWindow.h"
29 #include "GUIManager.h"
30 #include "InputEngine.h"
31 
32 #include <fmt/format.h>
33 
34 using namespace RoR;
35 using namespace GUI;
36 
38 {
40  {
41  // Interactive keybind mode - controls window remains 'visible', but box is drawn instead.
42 
44  Ogre::String keys_pressed;
45  int num_nonmodifier_keys = App::GetInputEngine()->getCurrentKeyCombo(&keys_pressed);
46 
47  if (num_nonmodifier_keys > 0)
48  {
50  {
51  m_active_buffer << "EXPL+" << keys_pressed;
52  }
53  else
54  {
55  m_active_buffer = keys_pressed;
56  }
57  this->ApplyChanges();
58  App::GetInputEngine()->resetKeys(); // Do not leak the pressed keys to gameplay.
59  }
60  else
61  {
62  ImGui::SetNextWindowPosCenter();
63  ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize;
64  bool open = true;
65  ImGui::Begin(_LC("GameControls", "Press a new key"), &open, flags);
66  // Title and description
67  ImGui::TextColored(theme.value_blue_text_color, "%s", App::GetInputEngine()->eventIDToName(m_active_event).c_str());
68  ImGui::TextColored(GRAY_HINT_TEXT, "%s", App::GetInputEngine()->eventIDToDescription(m_active_event).c_str());
69  // Keys preview
70  ImGui::NewLine();
71  ImGui::Text(keys_pressed.c_str());
72  // EXPL checkbox + tooltip
73  ImGui::NewLine();
74  ImGui::Checkbox(_LC("GameControls", "EXPL"), &m_interactive_keybinding_expl);
75  const bool checkbox_hovered = ImGui::IsItemHovered();
76  ImGui::SameLine();
77  ImGui::TextDisabled("(?)");
78  const bool hint_hovered = ImGui::IsItemHovered();
79  if (checkbox_hovered || hint_hovered)
80  {
81  ImGui::BeginTooltip();
82  ImGui::Text("%s", _LC("GameControls",
83  "With EXPL tag, only exactly matching key combos will be triggered.\n"
84  "Without it, partial matches will trigger, too."));
85  ImGui::Separator();
86  ImGui::Text("%s", _LC("GameControls",
87  "Example: Pressing CTRL+F1 will trigger COMMANDS_03 and COMMANDS_01\n"
88  "but not COMMANDS_02 which has EXPL tag."));
89  ImGui::TextDisabled(" COMMANDS_01 Keyboard F1");
90  ImGui::TextDisabled(" COMMANDS_02 Keyboard EXPL+F1");
91  ImGui::TextDisabled(" COMMANDS_03 Keyboard CTRL+F1");
92  ImGui::EndTooltip();
93  }
94  ImGui::End();
95  if (!open)
96  {
97  this->CancelChanges();
98  }
99  }
100  }
101  else
102  {
103  // regular window display
104 
105  ImGui::SetNextWindowPosCenter(ImGuiCond_FirstUseEver);
106  ImGui::SetNextWindowSize(ImVec2(800.f, 600.f), ImGuiCond_FirstUseEver);
107  bool keep_open = true;
108  ImGui::Begin(_LC("GameControls", "Game Controls"), &keep_open);
109 
111 
112  // Toolbar
113 
114  if (m_unsaved_changes)
115  {
116  if (ImGui::Button(_LC("GameControls", "Save changes")))
117  {
118  this->SaveMapFile();
119  }
120  ImGui::SameLine();
121  if (ImGui::Button(_LC("GameControls", "Reset changes")))
122  {
123  this->ReloadMapFile();
124  }
125  }
126 
127  // Tabs
128 
129  ImGui::BeginTabBar("GameSettingsTabs");
130 
131  this->DrawControlsTabItem("Airplane", "AIRPLANE");
132  this->DrawControlsTabItem("Boat", "BOAT");
133  this->DrawControlsTabItem("Camera", "CAMERA");
134  this->DrawControlsTabItem("Sky", "SKY");
135  this->DrawControlsTabItem("Character", "CHARACTER");
136  this->DrawControlsTabItem("Commands", "COMMANDS");
137  this->DrawControlsTabItem("Common", "COMMON");
138  this->DrawControlsTabItem("Grass", "GRASS");
139  this->DrawControlsTabItem("Map", "SURVEY_MAP");
140  this->DrawControlsTabItem("Menu", "MENU");
141  this->DrawControlsTabItem("Truck", "TRUCK");
142  this->DrawControlsTabItem("Road editor", "ROAD_EDITOR");
143 
144  ImGui::EndTabBar(); // GameSettingsTabs
145 
146  m_is_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
147 
148  ImGui::End();
149  if (!keep_open)
150  {
151  this->SetVisible(false);
152  }
153  }
154 }
155 
157 {
158  // Var
159  InputEngine::TriggerVec& triggers = App::GetInputEngine()->getEvents()[ev_code];
161  float cursor_x = ImGui::GetCursorPosX();
162 
163  // Check if we have anything to show
164  int display_count = 0;
165  for (event_trigger_t& trig : triggers)
166  {
167  display_count += (int)this->ShouldDisplay(trig);
168  }
169  if (display_count == 0)
170  {
171  return;
172  }
173 
174  // Set up
175  ImGui::PushID((int)ev_code);
176 
177  // Name column
178  ImGui::TextColored(theme.value_blue_text_color, "%s", App::GetInputEngine()->eventIDToName(ev_code).c_str());
179 
180  ImGui::NextColumn();
181 
182  // Command column
183 
184  int num_visible_commands = 0; // Count visible commands ahead of time
185  for (event_trigger_t& trig : triggers)
186  {
187  num_visible_commands += this->ShouldDisplay(trig);
188  }
189 
190  int num_drawn_commands = 0;
191  for (event_trigger_t& trig: triggers)
192  {
193  if (!this->ShouldDisplay(trig)) continue;
194 
195  ImGui::PushID(&trig);
196 
197  ImVec2 cursor_before_command = ImGui::GetCursorScreenPos();
198 
199  if (ImGui::Button(App::GetInputEngine()->getTriggerCommand(trig).c_str(), ImVec2(ImGui::GetColumnWidth() - 2*ImGui::GetStyle().ItemSpacing.x, 0)))
200  {
201  // Begin interactive keybind
202  m_active_event = ev_code;
203  m_active_trigger = &trig;
207  m_interactive_keybinding_expl = trig.explicite;
208  }
209 
210  // If there's more than 1 commands, add numbering at the left side of the buttons
211  num_drawn_commands++;
212  if (num_visible_commands > 1)
213  {
214  ImVec2 text_pos = cursor_before_command + ImGui::GetStyle().FramePadding;
215  ImU32 text_color = ImColor(ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
216  ImGui::GetWindowDrawList()->AddText(text_pos, text_color, fmt::format("{}.", num_drawn_commands).c_str());
217  }
218 
219  ImGui::PopID(); // &trig
220  }
221  ImGui::NextColumn();
222 
223  // Description column
224  ImGui::TextColored(GRAY_HINT_TEXT, "%s", App::GetInputEngine()->eventIDToDescription(ev_code).c_str());
225  ImGui::NextColumn();
226 
227  // Clean up.
228  ImGui::PopID(); // ev_code
229 }
230 
232 {
233  // Device type selector
234  // - General BeginCombo() API, you have full control over your selection data and display type.
235  if (ImGui::BeginCombo(_LC("GameSettings", "EventType"), InputEngine::getEventTypeName(m_selected_evtype))) // The second parameter is the label previewed before opening the combo.
236  {
237  for (int i = ET_Keyboard; i < ET_END; i++)
238  {
239  switch (i)
240  {
241  case ET_MouseButton:
242  case ET_MouseAxisX:
243  case ET_MouseAxisY:
244  case ET_MouseAxisZ:
245  case ET_JoystickAxisRel: // Configured as "JoystickAxis RELATIVE".
246  case ET_JoystickSliderX: // X/Y is determined by special parameter.
247  continue; // Not available
248  default:
249  break;
250  }
251  const bool is_selected = (m_selected_evtype == i);
252  if (ImGui::Selectable(InputEngine::getEventTypeName((eventtypes)i), is_selected))
253  {
255  }
256  if (is_selected)
257  {
258  ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
259  }
260  }
261  ImGui::EndCombo();
262  }
263 
264  // Combo text input
265  int flags = ImGuiInputTextFlags_EnterReturnsTrue;
267  {
268  flags |= ImGuiInputTextFlags_CharsNoBlank;
269  }
271  {
272  flags |= ImGuiInputTextFlags_CharsUppercase; // POV options are case-sensitive.
273  }
274  if (ImGui::InputText("", m_active_buffer.GetBuffer(), m_active_buffer.GetCapacity(), flags))
275  {
276  this->ApplyChanges();
277  }
278 
279  // Buttons
280  if (ImGui::Button(_LC("GameSettings", "OK")))
281  {
282  this->ApplyChanges();
283  }
284  ImGui::SameLine();
285  if (ImGui::Button(_LC("GameSettings", "Cancel")))
286  {
287  this->CancelChanges();
288  }
289  ImGui::SameLine();
290  if (ImGui::Button(_LC("GameSettings", "Delete")))
291  {
293  this->CancelChanges();
294  }
295 }
296 
297 void GameControls::DrawControlsTab(const char* prefix)
298 {
299  ImGui::Columns(3, /*id=*/nullptr, /*border=*/true);
300 
301  for (auto& ev_pair: App::GetInputEngine()->getEvents())
302  {
303  // Retrieve data
304  const RoR::events ev_code = RoR::events(ev_pair.first);
305  const std::string ev_name = App::GetInputEngine()->eventIDToName(ev_code);
306 
307  // Filter event list by prefix
308  if (ev_name.find(prefix) == 0)
309  {
310  this->DrawEvent(ev_code);
311  }
312  }
313 
314  m_colum_widths[0] = ImGui::GetColumnWidth(0);
315  m_colum_widths[1] = ImGui::GetColumnWidth(1);
316  m_colum_widths[2] = ImGui::GetColumnWidth(2);
317 
318  ImGui::Columns(1);
319 }
320 
321 void GameControls::DrawControlsTabItem(const char* name, const char* prefix)
322 {
323  if (ImGui::BeginTabItem(_LC("GameSettings", name)))
324  {
325  ImGui::PushID(prefix);
326 
327  // Table header
328  ImGui::Columns(3, /*id=*/nullptr, /*border=*/true);
329  ImGui::SetColumnWidth(0, m_colum_widths[0] + ImGui::GetStyle().FramePadding.x + 1);
330  ImGui::SetColumnWidth(1, m_colum_widths[1]);
331  ImGui::SetColumnWidth(2, m_colum_widths[2]);
332  ImGui::TextColored(GRAY_HINT_TEXT, "Name");
333  ImGui::NextColumn();
334  ImGui::TextColored(GRAY_HINT_TEXT, "Shortcut");
335  ImGui::NextColumn();
336  ImGui::TextColored(GRAY_HINT_TEXT, "Description");
337  ImGui::NextColumn();
338  ImGui::Separator();
339  ImGui::Columns(1); // Cannot cross with child window.
340 
341  // Scroll region
342  ImGui::BeginChild("scroll");
343 
344  // Actual controls table
345  this->DrawControlsTab(prefix);
346 
347  // Cleanup
348  ImGui::EndChild(); // "scroll"
349  ImGui::PopID(); // `prefix`
350  ImGui::EndTabItem(); // `name`
351  }
352 }
353 
355 {
356  // Validate input (the '_CharsNoBlank' flag ensures there is no whitespace)
357  if (m_active_buffer.GetLength() == 0)
358  {
359  this->CancelChanges();
360  return;
361  }
362 
363  // Erase the old trigger.
365 
366  // Format a '.map' format line with new config
367  std::string format_string;
369  {
370  format_string = "{} {} {}"; // Name, Type, Binding
371  }
372  else // Joystick
373  {
374  format_string = "{} {} 0 {}"; // Name, Type, DeviceNumber (unused), Binding
375  }
376  std::string line = fmt::format(format_string,
377  App::GetInputEngine()->eventIDToName(m_active_event),
380 
381  // Parse the line - this creates new trigger.
383 
384  // Reset editing context.
386  m_active_trigger = nullptr;
389  m_unsaved_changes = true;
390 }
391 
393 {
394  // Reset editing context.
396  m_active_trigger = nullptr;
399 }
400 
402 {
403  this->CancelChanges();
405  m_unsaved_changes = false;
406 }
407 
409 {
410  this->CancelChanges();
413  m_unsaved_changes = false;
414 }
415 
417 {
418  m_is_visible = vis;
419  m_is_hovered = false;
420  if (!vis)
421  {
422  this->CancelChanges();
424  }
425 }
426 
428 {
429  // display only keyboard items from "input.map" or defaults
430  return trig.eventtype == eventtypes::ET_Keyboard &&
433 }
434 
RoR::InputEngine::BUILTIN_MAPPING_DEVICEID
static const int BUILTIN_MAPPING_DEVICEID
virtual device ID for builtin defaults
Definition: InputEngine.h:465
RoR::ET_END
@ ET_END
Definition: InputEngine.h:64
RoR::InputEngine::processLine
bool processLine(const char *line, int deviceID=-1)
Definition: InputEngine.cpp:1289
RoR::InputEngine::getEventTypeName
static const char * getEventTypeName(eventtypes type)
Enum to string helper.
Definition: InputEngine.cpp:1177
RoR::GUI::GameControls::m_colum_widths
float m_colum_widths[3]
body->header width sync
Definition: GUI_GameControls.h:57
RoR::InputEngine::getCurrentKeyCombo
int getCurrentKeyCombo(Ogre::String *combo)
Returns number of non-modifier keys pressed (or modifier count as negative number).
Definition: InputEngine.cpp:1701
RoR::InputEngine::resetKeys
void resetKeys()
Definition: InputEngine.cpp:696
RoR::InputEngine::loadConfigFile
bool loadConfigFile(int deviceID=-1)
Loads config file specific to a device and OS (or default config if deviceID is -1).
Definition: InputEngine.cpp:1851
RoR::GUI::GameControls::m_active_buffer
Str< 1000 > m_active_buffer
Definition: GUI_GameControls.h:67
RoR::Str::GetBuffer
char * GetBuffer()
Definition: Str.h:48
RoR::GUIManager::GuiTheme
Definition: GUIManager.h:70
RoR::InputEngine::saveConfigFile
bool saveConfigFile(int deviceID=-1)
Wites events with matching deviceID to loaded file with matching deviceID (or default file if deviceI...
Definition: InputEngine.cpp:1895
RoR::App::GetGuiManager
GUIManager * GetGuiManager()
Definition: Application.cpp:269
RoR::GUIManager::GuiTheme::value_blue_text_color
ImVec4 value_blue_text_color
Definition: GUIManager.h:77
RoR::InputEngine::clearEventsByDevice
void clearEventsByDevice(int deviceID)
Clears all bindings with given deviceID (-1 is no exception).
Definition: InputEngine.cpp:1265
RoR::GUI::GameControls::m_selected_evtype
eventtypes m_selected_evtype
Definition: GUI_GameControls.h:66
format
Truck file format(technical spec)
RoR::GUI::GameControls::m_active_mapping_file
int m_active_mapping_file
Definition: GUI_GameControls.h:60
RoR::ET_JoystickSliderX
@ ET_JoystickSliderX
Definition: InputEngine.h:62
RoR::event_trigger_t::eventtype
enum eventtypes eventtype
Definition: InputEngine.h:418
RoR::Str::GetCapacity
size_t GetCapacity() const
Definition: Str.h:49
RoR::ET_JoystickPov
@ ET_JoystickPov
Definition: InputEngine.h:61
RoR::InputEngine::TriggerVec
std::vector< event_trigger_t > TriggerVec
Definition: InputEngine.h:460
RoR::ET_MouseAxisX
@ ET_MouseAxisX
Definition: InputEngine.h:55
RoR::ET_Keyboard
@ ET_Keyboard
Definition: InputEngine.h:53
RoR::GUI::GameControls::m_interactive_keybinding_expl
bool m_interactive_keybinding_expl
Definition: GUI_GameControls.h:69
Language.h
RoR::Str::GetLength
size_t GetLength() const
Definition: Str.h:51
OgreImGui.h
GUIManager.h
RoR::ET_MouseButton
@ ET_MouseButton
Definition: InputEngine.h:54
Actor.h
RoR::GUI::GameControls::DrawControlsTab
void DrawControlsTab(const char *prefix)
Draws table with events matching prefix.
Definition: GUI_GameControls.cpp:297
RoR::event_trigger_t
Definition: InputEngine.h:415
RoR::GUI::GameControls::m_active_event
RoR::events m_active_event
Definition: GUI_GameControls.h:64
RoR::GUI::GameControls::m_active_trigger
event_trigger_t * m_active_trigger
Definition: GUI_GameControls.h:65
RoR::ET_MouseAxisY
@ ET_MouseAxisY
Definition: InputEngine.h:56
RoR::ET_MouseAxisZ
@ ET_MouseAxisZ
Definition: InputEngine.h:57
RoR::GUI::GameControls::DrawControlsTabItem
void DrawControlsTabItem(const char *name, const char *prefix)
Wraps DrawControlsTab() with scrollbar and tabs-bar logic.
Definition: GUI_GameControls.cpp:321
RoR::GUIManager::GetTheme
GuiTheme & GetTheme()
Definition: GUIManager.h:158
RoR::EV_MODE_LAST
@ EV_MODE_LAST
Definition: InputEngine.h:404
RoR::GUI::GameControls::DrawEvent
void DrawEvent(RoR::events ev_code)
One line in table.
Definition: GUI_GameControls.cpp:156
RoR::event_trigger_t::configDeviceID
int configDeviceID
For which device (which config file) was this binding defined?
Definition: InputEngine.h:419
GUI_LoadingWindow.h
RoR::Str::ToCStr
const char * ToCStr() const
Definition: Str.h:46
RoR::InputEngine::eventIDToName
static Ogre::String eventIDToName(int eventID)
Definition: InputEngine.cpp:2013
Application.h
Central state/object manager and communications hub.
RoR::GUI::GameControls::Draw
void Draw()
Definition: GUI_GameControls.cpp:37
RoR::ET_JoystickAxisRel
@ ET_JoystickAxisRel
Definition: InputEngine.h:60
RoR::InputEngine::DEFAULT_MAPFILE_DEVICEID
static const int DEFAULT_MAPFILE_DEVICEID
virtual device ID for "input.map" entries
Definition: InputEngine.h:464
RoR::GUI::GameControls::GRAY_HINT_TEXT
const ImVec4 GRAY_HINT_TEXT
Definition: GUI_GameControls.h:31
RoR::GUI::GameControls::m_is_visible
bool m_is_visible
Definition: GUI_GameControls.h:55
RoR::GUI::GameControls::CancelChanges
void CancelChanges()
Definition: GUI_GameControls.cpp:392
RoR::InputEngine::getEvents
EventMap & getEvents()
Definition: InputEngine.h:507
RoR::eventtypes
eventtypes
Definition: InputEngine.h:50
RoR::GUI::GameControls::ReloadMapFile
void ReloadMapFile()
Definition: GUI_GameControls.cpp:408
RoR::GUI::GameMainMenu::SetVisible
void SetVisible(bool v)
Definition: GUI_GameMainMenu.h:43
_LC
#define _LC(ctx, str)
Definition: Language.h:42
RoR::GUI::GameControls::m_is_hovered
bool m_is_hovered
Definition: GUI_GameControls.h:56
RoR::GUI::GameControls::ShouldDisplay
bool ShouldDisplay(event_trigger_t &trig)
Definition: GUI_GameControls.cpp:427
RoR::ET_JoystickButton
@ ET_JoystickButton
Definition: InputEngine.h:58
RoR::App::GetInputEngine
InputEngine * GetInputEngine()
Definition: Application.cpp:271
RoR::GUI::GameControls::ApplyChanges
void ApplyChanges()
Definition: GUI_GameControls.cpp:354
InputEngine.h
Handles controller inputs from player. Defines input events and binding mechanism,...
RoR::InputEngine::eraseEvent
void eraseEvent(int eventID, const event_trigger_t *t)
Definition: InputEngine.cpp:1241
RoR::GUI::GameControls::SetVisible
void SetVisible(bool visible)
Definition: GUI_GameControls.cpp:416
RoR::GUI::GameControls::DrawEventEditBox
void DrawEventEditBox()
Only the editing UI, embeddable.
Definition: GUI_GameControls.cpp:231
GUI_GameControls.h
RoR::events
events
Definition: InputEngine.h:74
RoR
Definition: AppContext.h:36
RoR::GUI::GameControls::SaveMapFile
void SaveMapFile()
Definition: GUI_GameControls.cpp:401
RoR::Str::Clear
Str & Clear()
Definition: Str.h:54
RoR::GUIManager::GameMainMenu
GUI::GameMainMenu GameMainMenu
Definition: GUIManager.h:106
RoR::GUI::GameControls::m_unsaved_changes
bool m_unsaved_changes
Definition: GUI_GameControls.h:61
RoR::GUI::GameControls::m_interactive_keybinding_active
bool m_interactive_keybinding_active
Definition: GUI_GameControls.h:68