Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
GUI_MainSelector.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5 Copyright 2013-2019 Petr Ohlidal
6
7 For more information, see http://www.rigsofrods.org/
8
9 Rigs of Rods is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3, as
11 published by the Free Software Foundation.
12
13 Rigs of Rods is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "GUI_MainSelector.h"
23
24#include "Application.h"
25#include "ActorManager.h"
26#include "CacheSystem.h"
27#include "ContentManager.h"
28#include "GameContext.h"
29#include "GUIManager.h"
30#include "GUIUtils.h"
31#include "GUI_LoadingWindow.h"
32#include "InputEngine.h"
33#include "Language.h"
34#include "ScriptEngine.h"
35#include "Utils.h"
36
37#include <MyGUI.h>
38#include <imgui.h>
39#include <imgui_internal.h>
40
41using namespace RoR;
42using namespace GUI;
43
44// MAIN SELECTOR WINDOW
45// --------------------
46// KEY CONTROLS
47// tab: toggle search box focus (FIXME: currently, only focusing works)
48// arrow left/right: if search box is not in focus, select previous(left) or next(right) category in "categories" combobox, wrap around when at either end.
49// arrow up/down: select prev/next entry. When already on top/bottom item, wrap to other end of the list.
50// enter: activate highlighted entry
51// SEARCHING
52// The result list is sorted descending by 'score'.
53// Syntax 'abcdef': searches fulltext (ingoring case) in: name, filename, description, author name/mail (in this order, with descending rank) and returns rank+string pos as score
54// Syntax 'AREA:abcdef': searches (ignoring case) in AREA: 'guid'(guid string), 'author' (name/email), 'wheels' (string "WHEELCOUNTxPROPWHEELCOUNT"), 'file' (filename); returns string pos as score
55
57{
58 // Constructs `CacheEntryPtr` - doesn't compile without `#include CacheSystem.h` - not pretty if in header (even if auto-generated by C++).
59}
60
62{
63 // Destructs `CacheEntryPtr` - doesn't compile without `#include CacheSystem.h` - not pretty if in header (even if auto-generated by C++).
64}
65
87
89{
91
92 ImGuiWindowFlags win_flags = ImGuiWindowFlags_NoCollapse;
93 ImGui::SetNextWindowPosCenter(ImGuiCond_FirstUseEver);
94 ImGui::SetNextWindowSize(ImVec2((ImGui::GetIO().DisplaySize.x / 1.4), (ImGui::GetIO().DisplaySize.y / 1.2)), ImGuiCond_FirstUseEver);
95 bool keep_open = true;
96 if (!ImGui::Begin(_LC("MainSelector", "Loader"), &keep_open, win_flags))
97 {
98 return;
99 }
100
102
103 // category keyboard control
104 const int num_categories = static_cast<int>(m_display_categories.size());
106 {
107 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
108 {
109 m_selected_category = (m_selected_category + 1) % num_categories; // select next item and wrap around at bottom.
113 this->UpdateDisplayLists();
114 }
115 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
116 {
117 m_selected_category = (m_selected_category > 0) ? (m_selected_category - 1) : (num_categories - 1); // select prev. item and wrap around on top
121 this->UpdateDisplayLists();
122 }
123 }
124
125 // category combobox
126 ImGui::PushItemWidth(LEFT_PANE_WIDTH);
127 if (ImGui::Combo(
128 "##SelectorCategory", &m_selected_category,
130 {
134 this->UpdateDisplayLists();
135 }
136 ImGui::PopItemWidth();
137 ImGui::SameLine();
138
139 // search box
140 const ImVec2 searchbox_cursor = ImGui::GetCursorPos();
141 const float searchbox_width = ImGui::GetWindowWidth() -
142 (LEFT_PANE_WIDTH + 2 * (ImGui::GetStyle().WindowPadding.x) + ImGui::GetStyle().ItemSpacing.x);
143
144 if (m_kb_focused == true)
145 {
146 ImGui::SetKeyboardFocusHere();
147 m_kb_focused = false;
148 }
149
150 if (!m_searchbox_was_active && (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)) || ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Slash))))
151 {
152 ImGui::SetKeyboardFocusHere();
153 }
154 ImGui::PushItemWidth(searchbox_width);
155 if (ImGui::InputText("##SelectorSearch", m_search_input.GetBuffer(), m_search_input.GetCapacity()))
156 {
157 m_selected_category = 0; // 'All'
159 this->UpdateSearchParams();
160 this->UpdateDisplayLists();
161 }
162 ImGui::PopItemWidth();
163 m_searchbox_was_active = ImGui::IsItemActive();
164 const ImVec2 separator_cursor = ImGui::GetCursorPos()
165 + ImVec2(0, ImGui::GetStyle().WindowPadding.y - ImGui::GetStyle().ItemSpacing.y);
166
167 // advanced search hint
168 const char* searchbox_hint = "(?)";
169 ImGui::SetCursorPos(searchbox_cursor + ImVec2(searchbox_width - (ImGui::CalcTextSize(searchbox_hint).x + ImGui::GetStyle().FramePadding.x), ImGui::GetStyle().FramePadding.y));
170 ImGui::TextDisabled(searchbox_hint);
171 if (ImGui::IsItemHovered())
172 {
173 ImGui::BeginTooltip();
174 ImGui::TextDisabled("Fulltext search:");
175 ImGui::Text("~ partial name, filename, description, author name or e-mail");
176 ImGui::TextDisabled("Advanced search:");
177 ImGui::Text("guid: ~ partial GUID");
178 ImGui::Text("author: ~ partial author name or e-mail");
179 ImGui::Text("wheels: ~ wheel configuration (i.e. 4x4)");
180 ImGui::Text("file: ~ partial file name");
181 ImGui::EndTooltip();
182 }
183
184 ImGui::SetCursorPos(separator_cursor);
185 ImGui::Separator();
186
187 // left
188 ImGui::BeginChild("left pane", ImVec2(LEFT_PANE_WIDTH, 0), true);
189 const int num_entries = static_cast<int>(m_display_entries.size());
190 bool scroll_to_selected = false;
191 // Entry list: handle keyboard
192 if (m_selected_entry != -1) // -1 indicates empty entry-list
193 {
194 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
195 {
196 m_selected_entry = (m_selected_entry + 1) % num_entries; // select next item and wrap around at bottom.
198 scroll_to_selected = true;
199 }
200 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
201 {
202 m_selected_entry = (m_selected_entry > 0) ? (m_selected_entry - 1) : (num_entries - 1); // select prev. item and wrap around on top
204 scroll_to_selected = true;
205 }
206 }
207 // Entry list: display
208 ImDrawList* drawlist = ImGui::GetWindowDrawList();
209 drawlist->ChannelsSplit(2); // 0=background selection indicator, 1=text
210 bool do_apply = false;
211 for (int i = 0; i < num_entries; ++i)
212 {
213 DisplayEntry& d_entry = m_display_entries[i];
214 bool is_selected = (i == m_selected_entry);
215
216 // Draw text manually to enable coloring.
217 drawlist->ChannelsSetCurrent(1);
218 ImVec2 size = RoR::DrawColorMarkedText(drawlist, ImGui::GetCursorScreenPos(),
219 ImGui::GetStyle().Colors[ImGuiCol_Text],
220 /*override_alpha=*/1.f, /*wrap_width=*/-1.f,
221 d_entry.sde_entry->dname.c_str());
222
223 ImGui::PushID(i);
224 drawlist->ChannelsSetCurrent(0);
225 ImVec2 mouse_area(LEFT_PANE_WIDTH, size.y); // Monitor the whole line (excess width gets clipped)
226 if (ImGui::Selectable("##dummy", &is_selected, 0, mouse_area)) // Use invisible label + size parameter.
227 {
231 }
232 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) // Left doubleclick
233 {
237 do_apply = true;
238 }
239 if (is_selected && scroll_to_selected)
240 {
241 ImGui::SetScrollHere();
242 }
243 ImGui::PopID();
244 }
245
246 if (do_apply)
247 {
248 this->Apply();
249 }
250 drawlist->ChannelsMerge();
251 ImGui::EndChild();
252 ImGui::SameLine();
253
254 // right
255 ImGui::BeginGroup();
256
257 ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing())); // Leave room for 1 line below us
258
259 if (m_selected_entry != -1)
260 {
262
263 // Preview image
264 if (!sd_entry.sde_entry->filecachename.empty())
265 {
266 ImVec2 cursor_pos = ImGui::GetCursorPos();
267 try
268 {
269 Ogre::TexturePtr preview_tex =
270 Ogre::TextureManager::getSingleton().load(
271 sd_entry.sde_entry->filecachename, Ogre::RGN_DEFAULT);
272 if (preview_tex)
273 {
274 // Scale the image (for dashboards only shrink but don't enlarge so players see actual in-game size).
275 ImVec2 max_size = (ImGui::GetWindowSize() * PREVIEW_SIZE_RATIO);
276 ImVec2 size(preview_tex->getWidth(), preview_tex->getHeight());
277 if (m_loader_type != LT_DashBoard || size.x > max_size.x)
278 {
279 size *= max_size.x / size.x; // Fit size along X
280 if (size.y > max_size.y) // Reduce size along Y if needed
281 {
282 size *= max_size.y / size.y;
283 }
284 }
285 // Draw the image
286 ImGui::SetCursorPos((cursor_pos + ImGui::GetWindowSize()) - size);
287 ImGui::Image(reinterpret_cast<ImTextureID>(preview_tex->getHandle()), size);
288 ImGui::SetCursorPos(cursor_pos);
289 }
290 }
291 catch(...)
292 {
293 // Invalid texture file - OGRE exception already logged
294 }
295 }
296
297 // Title and description
299 ImGui::TextWrapped("%s", sd_entry.sde_entry->description.c_str());
300 ImGui::Separator();
301
302 // Details
303 for (AuthorInfo const& author: sd_entry.sde_entry->authors)
304 {
305 ImGui::TextDisabled("%s", _LC("MainSelector", "Author(s): "));
306 ImGui::SameLine();
307 ImGui::TextColored(theme.value_blue_text_color, "%s [%s]", author.name.c_str(), author.type.c_str());
308 }
309 this->DrawAttrInt(_LC("MainSelector", "Version: "), sd_entry.sde_entry->version);
310 if (sd_entry.sde_entry->wheelcount > 0)
311 {
312 ImGui::Text("%s", _LC("MainSelector", "Wheels: "));
313 ImGui::SameLine();
314 ImGui::TextColored(theme.value_blue_text_color, "%dx%d", sd_entry.sde_entry->wheelcount, sd_entry.sde_entry->propwheelcount);
315 }
316 if (sd_entry.sde_entry->truckmass > 0)
317 {
318 ImGui::Text("%s", _LC("MainSelector", "Mass: "));
319 ImGui::SameLine();
320 ImGui::TextColored(theme.value_blue_text_color, "%.2f t", Round(sd_entry.sde_entry->truckmass / 1000.0f, 3));
321 }
322
323 if (m_show_details)
324 {
325 if (sd_entry.sde_entry->loadmass > 0)
326 {
327 ImGui::Text("%s", _LC("MainSelector", "Load Mass: "));
328 ImGui::SameLine();
329 ImGui::TextColored(theme.value_blue_text_color, "%f.2 t", Round(sd_entry.sde_entry->loadmass / 1000.0f, 3));
330 }
331 this->DrawAttrInt(_LC("MainSelector", "Nodes: "), sd_entry.sde_entry->nodecount);
332 this->DrawAttrInt(_LC("MainSelector", "Beams: "), sd_entry.sde_entry->beamcount);
333 this->DrawAttrInt(_LC("MainSelector", "Shocks: "), sd_entry.sde_entry->shockcount);
334 this->DrawAttrInt(_LC("MainSelector", "Hydros: "), sd_entry.sde_entry->hydroscount);
335 this->DrawAttrInt(_LC("MainSelector", "SoundSources: "), sd_entry.sde_entry->soundsourcescount);
336 this->DrawAttrInt(_LC("MainSelector", "Commands: "), sd_entry.sde_entry->commandscount);
337 this->DrawAttrInt(_LC("MainSelector", "Rotators: "), sd_entry.sde_entry->rotatorscount);
338 this->DrawAttrInt(_LC("MainSelector", "Exhausts: "), sd_entry.sde_entry->exhaustscount);
339 this->DrawAttrInt(_LC("MainSelector", "Flares: "), sd_entry.sde_entry->flarescount);
340 this->DrawAttrInt(_LC("MainSelector", "Flexbodies: "), sd_entry.sde_entry->flexbodiescount);
341 this->DrawAttrInt(_LC("MainSelector", "Props: "), sd_entry.sde_entry->propscount);
342 this->DrawAttrInt(_LC("MainSelector", "Wings: "), sd_entry.sde_entry->wingscount);
343 if (sd_entry.sde_entry->hasSubmeshs)
344 {
345 ImGui::Text("%s", _LC("MainSelector", "Using Submeshs: "));
346 ImGui::SameLine();
347 ImGui::TextColored(theme.value_blue_text_color, "%s", sd_entry.sde_entry->hasSubmeshs ?_LC("MainSelector", "Yes") : _LC("MainSelector", "No"));
348 }
349 if (sd_entry.sde_entry->default_skin != "")
350 {
351 ImGui::Text("%s", _LC("MainSelector", "Default skin: "));
352 ImGui::SameLine();
353 ImGui::TextColored(theme.value_blue_text_color, "%s", sd_entry.sde_entry->default_skin.c_str());
354 }
355 this->DrawAttrFloat(_LC("MainSelector", "Torque: "), sd_entry.sde_entry->torque);
356 this->DrawAttrInt(_LC("MainSelector", "Transmission Gear Count: "), sd_entry.sde_entry->numgears);
357 if (sd_entry.sde_entry->minrpm > 0)
358 {
359 ImGui::Text("%s", _LC("MainSelector", "Engine RPM: "));
360 ImGui::SameLine();
361 ImGui::TextColored(theme.value_blue_text_color, "%f - %f", sd_entry.sde_entry->minrpm, sd_entry.sde_entry->maxrpm);
362 }
363 this->DrawAttrStr(_LC("MainSelector", "Unique ID: "), sd_entry.sde_entry->uniqueid);
364 this->DrawAttrStr(_LC("MainSelector", "GUID: "), sd_entry.sde_entry->guid);
365 this->DrawAttrInt(_LC("MainSelector", "Times used: "), sd_entry.sde_entry->usagecounter);
366 this->DrawAttrStr(_LC("MainSelector", "Date and Time modified: "), sd_entry.sde_filetime_str.ToCStr());
367 this->DrawAttrStr(_LC("MainSelector", "Date and Time installed: "), sd_entry.sde_addtime_str.ToCStr());
369 {
370 this->DrawAttrStr(_LC("MainSelector", "Vehicle Type: "), sd_entry.sde_driveable_str.ToCStr());
371 }
372
373 this->DrawAttrSpecial(sd_entry.sde_entry->forwardcommands, _LC("MainSelector", "[forwards commands]"));
374 this->DrawAttrSpecial(sd_entry.sde_entry->importcommands, _LC("MainSelector", "[imports commands]"));
375 this->DrawAttrSpecial(sd_entry.sde_entry->rescuer, _LC("MainSelector", "[is rescuer]"));
376 this->DrawAttrSpecial(sd_entry.sde_entry->custom_particles, _LC("MainSelector", "[uses custom particles]"));
377 this->DrawAttrSpecial(sd_entry.sde_entry->fixescount > 0, _LC("MainSelector", "[has fixes]"));
378 // Engine type 't' (truck) is the default, do not display it
379 this->DrawAttrSpecial(sd_entry.sde_entry->enginetype == 'c', _LC("MainSelector", "[car engine]"));
380 this->DrawAttrSpecial(sd_entry.sde_entry->resource_bundle_type == "Zip", _LC("MainSelector", "[zip archive]"));
381 this->DrawAttrSpecial(sd_entry.sde_entry->resource_bundle_type == "FileSystem", _LC("MainSelector", "[unpacked in directory]"));
382
383 if (!sd_entry.sde_entry->resource_bundle_path.empty())
384 {
385 ImGui::Text("%s", _LC("MainSelector", "Source: "));
386 ImGui::SameLine();
387 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color,
388 "%s", sd_entry.sde_entry->resource_bundle_path.c_str());
389 }
390 if (!sd_entry.sde_entry->fname.empty())
391 {
392 ImGui::Text("%s", _LC("MainSelector", "Filename: "));
393 ImGui::SameLine();
394 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color,
395 "%s", sd_entry.sde_entry->fname.c_str());
396 }
397 }
398 ImGui::Checkbox(_LC("MainSelector", "Show details"), &m_show_details);
399 }
400 ImGui::EndChild();
401
402 ImGui::BeginChild("buttons");
403 if (m_selected_entry != -1)
404 {
406 if (sd_entry.sde_entry->sectionconfigs.size() > 0)
407 {
408 ImGui::PushItemWidth(ImGui::GetWindowWidth() / 2);
409 ImGui::Combo(_LC("MainSelector", "Configuration"), &m_selected_sectionconfig,
411 static_cast<int>(sd_entry.sde_entry->sectionconfigs.size()));
412 ImGui::SameLine();
413 }
414 ImGui::SameLine(ImGui::GetWindowWidth()-280);
415 if (ImGui::Button(_LC("MainSelector", "OK"), ImVec2(120.f, 0.0f)) ||
416 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
417 {
418 this->Apply();
419 }
420 }
421 ImGui::SameLine(ImGui::GetWindowWidth()-150);
422 if (ImGui::Button(_LC("MainSelector", "Cancel"), ImVec2(120.f, 0.0f)) || ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
423 {
424 this->Cancel();
425 }
426 ImGui::EndChild();
427
428 ImGui::EndGroup();
429
430 m_is_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
431
432 ImGui::End();
433 if (!keep_open)
434 {
435 this->Cancel();
436 }
437};
438
439template <typename T1, typename T2>
441{
442 bool operator ()(std::pair<int, Ogre::String> const& a, std::pair<int, Ogre::String> const& b) const
443 {
444 if (a.first == CID_All)
445 return true;
446 if (b.first == CID_All)
447 return false;
448 if (a.first == CID_Fresh)
449 return true;
450 if (b.first == CID_Fresh)
451 return false;
452 return a.second < b.second;
453 }
454};
455
456
458{
459 m_display_categories.clear();
460 m_display_entries.clear();
461
463 {
466 }
467
468 // Find all relevant entries
469 CacheQuery query;
475 if (m_loader_type == LT_DashBoard) // HACK for dashboards
476 {
478 }
479
480 App::GetCacheSystem()->Query(query);
481
482 m_selected_entry = -1;
483 for (CacheQueryResult const& res: query.cqy_results)
484 {
485 if (res.cqr_entry != m_advertised_entry)
486 {
487 m_display_entries.push_back(res.cqr_entry);
489 }
490 }
491
492 // Sort categories alphabetically
494 std::vector<std::pair<int, Ogre::String>> sorted_cats(cats.begin(), cats.end());
495 std::sort(sorted_cats.begin(), sorted_cats.end(), sort_cats<int, Ogre::String>());
496
497 // Display used categories
498 for (auto itor: sorted_cats)
499 {
500 size_t usage = query.cqy_res_category_usage[itor.first];
501 if (usage > 0 || itor.first == CacheCategoryId::CID_Fresh) // HACK: Always include the "fresh" category
502 {
503 m_display_categories.emplace_back(itor.first, itor.second, usage);
504 }
505 }
506}
507
509{
510 std::string input = m_search_input.ToCStr();
511
512 if (input.find(":") == std::string::npos)
513 {
515 m_search_string = input;
516 }
517 else
518 {
519 Ogre::StringVector v = Ogre::StringUtil::split(input, ":");
520 if (v.size() < 2)
521 {
523 m_search_string = "";
524 }
525 else if (v[0] == "guid")
526 {
528 m_search_string = v[1];
529 }
530 else if (v[0] == "author")
531 {
533 m_search_string = v[1];
534 }
535 else if (v[0] == "wheels")
536 {
538 m_search_string = v[1];
539 }
540 else if (v[0] == "file")
541 {
543 m_search_string = v[1];
544 }
545 else
546 {
548 m_search_string = "";
549 }
550 }
551}
552
553// Static helper
554bool MainSelector::ScComboItemGetter(void* data, int idx, const char** out_text)
555{
556 CacheEntryPtr* entry_ptr = static_cast<CacheEntryPtr*>(data);
557 if (out_text)
558 *out_text = (*entry_ptr)->sectionconfigs.at(idx).c_str();
559 return true;
560}
561
562void MainSelector::DrawAttrInt(const char* title, int val) const
563{
564 if (val > 0)
565 {
566 ImGui::Text("%s", title);
567 ImGui::SameLine();
568 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color, "%d", val);
569 }
570}
571
572void MainSelector::DrawAttrFloat(const char* title, float val) const
573{
574 if (val > 0)
575 {
576 ImGui::Text("%s", title);
577 ImGui::SameLine();
578 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color, "%f", val);
579 }
580}
581
582void MainSelector::DrawAttrSpecial(bool val, const char* label) const
583{
584 if (val)
585 {
586 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_red_text_color, "%s", label);
587 }
588}
589
590void MainSelector::DrawAttrStr(const char* desc, std::string const& str) const
591{
592 if (!str.empty())
593 {
594 ImGui::Text("%s", desc);
595 ImGui::SameLine();
596 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color, "%s", str.c_str());
597 }
598}
599
601{
602 m_selected_entry = -1;
605 m_loader_type = LT_None; // Hide window
606 m_kb_focused = true;
607 m_is_hovered = false;
608}
609
611{
612 this->Close();
613
614 if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
615 {
616 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
617 {
619 }
621 }
622 else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
623 {
625 }
626}
627
629{
630 ROR_ASSERT(m_selected_entry > -1); // Programmer error
632
634 {
635 auto request = new LoadScriptRequest();
636 request->lsr_filename = sd_entry.sde_entry->fname;
637 request->lsr_category = ScriptCategory::GADGET;
639 this->Close();
640 }
641 else if (m_loader_type == LT_Terrain &&
642 App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
643 {
645 this->Close();
646 }
647 else if (m_loader_type == LT_DashBoard &&
648 App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
649 {
651 {
654 break;
657 break;
658 default:
659 LOG(fmt::format("[RoR|GameSettings] INTERNAL ERROR - Unrecognized dashboard type being selected ({})!",
661 break;
662 }
664 this->Close();
665 }
666 else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
667 {
669 std::string sectionconfig;
670 if (sd_entry.sde_entry->sectionconfigs.size() > 0)
671 {
672 sectionconfig = sd_entry.sde_entry->sectionconfigs[m_selected_sectionconfig];
673 }
674 this->Close();
675
676 App::GetGameContext()->OnLoaderGuiApply(type, sd_entry.sde_entry, sectionconfig);
677 }
678}
679
680// Static helper
681bool MainSelector::CatComboItemGetter(void* data, int idx, const char** out_text)
682{
683 DisplayCategoryVec* dc_vec = static_cast<DisplayCategoryVec*>(data);
684 if (out_text)
685 *out_text = dc_vec->at(idx).sdc_title.ToCStr();
686 return true;
687}
688
690 sde_entry(entry)
691{
692 // Pre-format strings
693 if (sde_entry->filetime > 0)
694 {
695 sde_filetime_str = asctime(gmtime(&sde_entry->filetime));
696 }
697 if (sde_entry->addtimestamp > 0)
698 {
699 sde_addtime_str = asctime(gmtime(&sde_entry->addtimestamp));
700 }
701
703}
704
705MainSelector::DisplayCategory::DisplayCategory(int id, std::string const& name, size_t usage)
706 : sdc_category_id(id)
707{
708 sdc_title << "(" << usage << ") " << _LC("ModCategory", name.c_str());
709}
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
void LOG(const char *msg)
Legacy alias - formerly a macro.
A database of user-installed content alias 'mods' (vehicles, terrains...)
Game state manager and message-queue provider.
Handles controller inputs from player.
#define _LC(ctx, str)
Definition Language.h:38
void setStr(std::string const &str)
Definition CVar.h:83
float minrpm
float truckmass
int wheelcount
bool custom_particles
std::vector< Ogre::String > sectionconfigs
Ogre::String fname
filename
Definition CacheSystem.h:67
int version
file's version
Definition CacheSystem.h:78
Ogre::String description
Ogre::String dname
name parsed from the file
Definition CacheSystem.h:70
int commandscount
int propscount
int exhaustscount
std::vector< AuthorInfo > authors
authors
Definition CacheSystem.h:86
bool hasSubmeshs
int flexbodiescount
bool importcommands
std::time_t addtimestamp
timestamp when this file was added to the cache
Definition CacheSystem.h:75
bool forwardcommands
int numgears
bool rescuer
int hydroscount
int usagecounter
how much it was used already
Definition CacheSystem.h:85
float torque
std::string resource_bundle_type
Archive type recognized by OGRE resource system: 'FileSystem' or 'Zip'.
Definition CacheSystem.h:80
std::string resource_bundle_path
Path of ZIP or directory which contains the media. Shared between CacheEntries, loaded only once.
Definition CacheSystem.h:81
int nodecount
int beamcount
int propwheelcount
float loadmass
int soundsourcescount
int fixescount
int shockcount
char enginetype
std::string default_skin
ActorType driveable
int flarescount
float maxrpm
Ogre::String filecachename
preview image filename
Definition CacheSystem.h:87
int rotatorscount
int wingscount
std::time_t filetime
filetime
Definition CacheSystem.h:83
Ogre::String uniqueid
file's unique id
Definition CacheSystem.h:76
Ogre::String guid
global unique id; Type "addonpart" leaves this empty and uses addonpart_guids; Always lowercase.
Definition CacheSystem.h:77
const CategoryIdNameMap & GetCategories() const
std::string ActorTypeToName(ActorType driveable)
size_t Query(CacheQuery &query)
std::map< int, Ogre::String > CategoryIdNameMap
CacheCategoryId default_dash_being_selected
std::map< LoaderType, int > m_last_selected_category
Last category-combobox position for each loader type.
std::string m_filter_guid
Used for skins.
std::map< LoaderType, int > m_last_selected_entry
Stores the last manually selected entry index for each loader type.
static bool CatComboItemGetter(void *data, int idx, const char **out_text)
CacheEntryPtr m_advertised_entry
Always shown on top, even if not existing in modcache (i.e. dummy default skin)
DisplayEntryVec m_display_entries
CacheSearchMethod m_search_method
int m_selected_cid
Category ID.
DisplayCategoryVec m_display_categories
void DrawAttrInt(const char *desc, int val) const
void DrawAttrStr(const char *desc, std::string const &str) const
void DrawAttrFloat(const char *desc, float val) const
std::map< LoaderType, int > m_last_selected_cid
Last selected category-ID for each loader type.
static bool ScComboItemGetter(void *data, int idx, const char **out_text)
void DrawAttrSpecial(bool val, const char *label) const
void Show(LoaderType type, std::string const &filter_guid="", CacheEntryPtr advertised_entry=nullptr)
int m_selected_category
Combobox position (uses display list)
std::vector< DisplayCategory > DisplayCategoryVec
GUI::GameSettings GameSettings
Definition GUIManager.h:119
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
GUI::GameMainMenu GameMainMenu
Definition GUIManager.h:117
GuiTheme & GetTheme()
Definition GUIManager.h:168
void OnLoaderGuiApply(RoR::LoaderType type, CacheEntryPtr entry, std::string sectionconfig)
GUI callback.
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
void OnLoaderGuiCancel()
GUI callback.
const char * ToCStr() const
Definition Str.h:46
Str & Clear()
Definition Str.h:54
char * GetBuffer()
Definition Str.h:48
size_t GetCapacity() const
Definition Str.h:49
@ MSG_NET_DISCONNECT_REQUESTED
@ MSG_APP_LOAD_SCRIPT_REQUESTED
Payload = RoR::LoadScriptRequest* (owner)
Definition Application.h:93
@ MSG_SIM_LOAD_TERRN_REQUESTED
@ GADGET
Associated with a .gadget mod file, launched via UI or any method given below for CUSTOM scripts (use...
GUIManager * GetGuiManager()
GameContext * GetGameContext()
CVar * app_state
CVar * mp_state
CVar * ui_default_boat_dash
string; name of the '.dashboard' file in modcache.
CVar * ui_default_truck_dash
string; name of the '.dashboard' file in modcache.
CacheSystem * GetCacheSystem()
LoaderType
< Search mode for ModCache::Query() & Operation mode for GUI::MainSelector
@ LT_DashBoard
@ LT_Skin
@ LT_Gadget
@ LT_Terrain
@ LT_None
ImVec2 DrawColorMarkedText(ImDrawList *drawlist, ImVec2 text_cursor, ImVec4 default_color, float override_alpha, float wrap_width, std::string const &line)
Draw multiline text with '#rrggbb' color markers. Returns total text size.
Definition GUIUtils.cpp:230
@ AUTHORS
Partial match in: author name/email.
@ FILENAME
Partial match in file name.
@ WHEELS
Wheel configuration, i.e. 4x4.
@ GUID
Partial match in: guid.
@ NONE
Ignore the search string and find all.
@ FULLTEXT
Partial match in: name, filename, description, author name/mail.
void ImTextWrappedColorMarked(std::string const &text)
Prints multiline text with '#rrggbb' color markers. Behaves like ImGui::Text* functions.
Definition GUIUtils.cpp:272
@ CID_None
@ CID_DashboardsTruck
@ CID_Fresh
@ CID_DashboardsBoat
@ CID_All
Ogre::Real Round(Ogre::Real value, unsigned short ndigits=0)
Definition Utils.cpp:101
std::string cqy_filter_guid
Exact match (case-insensitive); leave empty to disable.
std::map< int, size_t > cqy_res_category_usage
Total usage (ignores search params + category filter)
std::string cqy_search_string
CacheSearchMethod cqy_search_method
std::vector< CacheQueryResult > cqy_results
RoR::LoaderType cqy_filter_type
DisplayCategory(int id, std::string const &name, size_t usage)
Str< 50 > sde_addtime_str
CacheEntryPtr sde_entry
Str< 50 > sde_filetime_str
DisplayEntry(CacheEntryPtr entry)
Str< 50 > sde_driveable_str
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52