Rigs of Rods 2026.01
Soft-body Physics Simulation
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
66void MainSelector::Show(LoaderType type, std::string const& filter_guid, CacheEntryPtr advertised_entry)
67{
68 m_loader_type = type;
70 if (!App::ui_keep_search->getBool())
71 {
73 m_search_string.clear();
74 m_kept_searchstring.clear();
75 }
76 else
77 {
78 auto current_search = m_kept_searchstring.find(type);
79 if (current_search != m_kept_searchstring.end())
80 {
81 m_search_input = current_search->second.c_str();
82 }
83 else
84 {
86 }
87 this->UpdateSearchParams();
88 }
89 m_filter_guid = filter_guid;
90 m_advertised_entry = advertised_entry;
92 if (App::ui_keep_search->getBool())
93 {
96 }
97 else
98 {
100 if (m_selected_cid == 0)
102 }
103 this->UpdateDisplayLists();
104 if (!App::ui_keep_search->getBool())
105 {
107 {
109 }
110 }
112 {
114 }
115}
116
118{
120
121 ImGuiWindowFlags win_flags = ImGuiWindowFlags_NoCollapse;
122 ImGui::SetNextWindowPosCenter(ImGuiCond_FirstUseEver);
123 ImGui::SetNextWindowSize(ImVec2((ImGui::GetIO().DisplaySize.x / 1.4), (ImGui::GetIO().DisplaySize.y / 1.2)), ImGuiCond_FirstUseEver);
124 bool keep_open = true;
125 if (!ImGui::Begin(_LC("MainSelector", "Loader"), &keep_open, win_flags))
126 {
127 return;
128 }
129
131
132 // category keyboard control
133 const int num_categories = static_cast<int>(m_display_categories.size());
135 {
136 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
137 {
138 m_selected_category = (m_selected_category + 1) % num_categories; // select next item and wrap around at bottom.
142 this->UpdateDisplayLists();
143 }
144 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
145 {
146 m_selected_category = (m_selected_category > 0) ? (m_selected_category - 1) : (num_categories - 1); // select prev. item and wrap around on top
150 this->UpdateDisplayLists();
151 }
152 }
153
154 // category combobox
155 ImGui::PushItemWidth(LEFT_PANE_WIDTH * ImGui::GetFontSize());
156 if (ImGui::Combo(
157 "##SelectorCategory", &m_selected_category,
159 {
163 this->UpdateDisplayLists();
164 }
165 ImGui::PopItemWidth();
166 ImGui::SameLine();
167
168 // search box
169 const ImVec2 searchbox_cursor = ImGui::GetCursorPos();
170 const float searchbox_width = ImGui::GetWindowWidth() -
171 (LEFT_PANE_WIDTH * ImGui::GetFontSize() + 2 * (ImGui::GetStyle().WindowPadding.x) + ImGui::GetStyle().ItemSpacing.x);
172
173 if (m_kb_focused == true)
174 {
175 ImGui::SetKeyboardFocusHere();
176 m_kb_focused = false;
177 }
178
179 if (!m_searchbox_was_active && (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)) || ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Slash))))
180 {
181 ImGui::SetKeyboardFocusHere();
182 }
183 ImGui::PushItemWidth(searchbox_width);
184 if (ImGui::InputText("##SelectorSearch", m_search_input.GetBuffer(), m_search_input.GetCapacity()))
185 {
186 if (App::ui_keep_search->getBool())
187 {
189 }
190 m_selected_category = 0; // 'All'
192 this->UpdateSearchParams();
193 this->UpdateDisplayLists();
194 }
195 ImGui::PopItemWidth();
196 m_searchbox_was_active = ImGui::IsItemActive();
197 const ImVec2 separator_cursor = ImGui::GetCursorPos()
198 + ImVec2(0, ImGui::GetStyle().WindowPadding.y - ImGui::GetStyle().ItemSpacing.y);
199
200 if (!m_settings_icon)
201 {
202 try
203 {
205 m_settings_icon = Ogre::TextureManager::getSingleton().load(
206 "cog.png", ContentManager::ResourcePack::FAMICONS.resource_group_name);
207 }
208 catch (...) {} // Logged by OGRE
209 }
210
211 // search settings
212 ImGui::SetItemAllowOverlap();
213 ImVec2 p_min = ImGui::GetItemRectMin();
214 ImVec2 p_max = ImGui::GetItemRectMax();
215 ImGui::SetCursorScreenPos(ImVec2(
216 p_max.x - ImGui::GetFontSize() - ImGui::GetStyle().FramePadding.x,
217 p_min.y + ImGui::GetStyle().FramePadding.y - 3.0f));
218 if (m_settings_icon)
219 {
220 if (ImGui::ImageButton(reinterpret_cast<ImTextureID>(m_settings_icon->getHandle()), ImVec2(ImGui::GetFontSize(), ImGui::GetFontSize())))
221 {
222 ImGui::OpenPopup("##SelectorSearchSettings");
223 }
224 }
225
226 ImGui::SetNextWindowPos(ImGui::GetItemRectMin(), ImGuiCond_Always);
227 if (ImGui::BeginPopup("##SelectorSearchSettings", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove))
228 {
229 ImGui::TextDisabled("Search settings:");
230 DrawGCheckbox(App::ui_keep_search, _LC("SelectorSearchSettings", "Keep search"));
231 ImGui::Separator();
232 ImGui::TextDisabled("Fulltext search:");
233 ImGui::Text("~ partial name, filename, description, author name or e-mail");
234 ImGui::TextDisabled("Advanced search:");
235 ImGui::Text("guid: ~ partial GUID");
236 ImGui::Text("author: ~ partial author name or e-mail");
237 ImGui::Text("wheels: ~ wheel configuration (i.e. 4x4)");
238 ImGui::Text("file: ~ partial file name");
239 ImGui::EndPopup();
240 }
241
242 ImGui::SetCursorPos(separator_cursor);
243 ImGui::Separator();
244
245 // left
246 ImGui::BeginChild("left pane", ImVec2(LEFT_PANE_WIDTH * ImGui::GetFontSize(), 0), true);
247 const int num_entries = static_cast<int>(m_display_entries.size());
248 bool scroll_to_selected = false;
249 // Entry list: handle keyboard
250 if (m_selected_entry != -1) // -1 indicates empty entry-list
251 {
252 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
253 {
254 m_selected_entry = (m_selected_entry + 1) % num_entries; // select next item and wrap around at bottom.
256 scroll_to_selected = true;
257 }
258 else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
259 {
260 m_selected_entry = (m_selected_entry > 0) ? (m_selected_entry - 1) : (num_entries - 1); // select prev. item and wrap around on top
262 scroll_to_selected = true;
263 }
264 }
265 // Entry list: display
266 ImDrawList* drawlist = ImGui::GetWindowDrawList();
267 drawlist->ChannelsSplit(2); // 0=background selection indicator, 1=text
268 bool do_apply = false;
269 for (int i = 0; i < num_entries; ++i)
270 {
271 DisplayEntry& d_entry = m_display_entries[i];
272 bool is_selected = (i == m_selected_entry);
273
274 // Draw text manually to enable coloring.
275 drawlist->ChannelsSetCurrent(1);
276 ImVec2 size = RoR::DrawColorMarkedText(drawlist, ImGui::GetCursorScreenPos(),
277 ImGui::GetStyle().Colors[ImGuiCol_Text],
278 /*override_alpha=*/1.f, /*wrap_width=*/-1.f,
279 d_entry.sde_entry->dname.c_str());
280
281 ImGui::PushID(i);
282 drawlist->ChannelsSetCurrent(0);
283 ImVec2 mouse_area(LEFT_PANE_WIDTH * ImGui::GetFontSize(), size.y); // Monitor the whole line (excess width gets clipped)
284 if (ImGui::Selectable("##dummy", &is_selected, 0, mouse_area)) // Use invisible label + size parameter.
285 {
289 }
290 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) // Left doubleclick
291 {
295 do_apply = true;
296 }
297 if (is_selected && scroll_to_selected)
298 {
299 ImGui::SetScrollHere();
300 }
301 ImGui::PopID();
302 }
303
304 if (do_apply)
305 {
306 this->Apply();
307 }
308 drawlist->ChannelsMerge();
309 ImGui::EndChild();
310 ImGui::SameLine();
311
312 // right
313 ImGui::BeginGroup();
314
315 ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing())); // Leave room for 1 line below us
316
317 if (m_selected_entry != -1)
318 {
320
321 // Preview image
322 if (!sd_entry.sde_entry->filecachename.empty())
323 {
324 ImVec2 cursor_pos = ImGui::GetCursorPos();
325 try
326 {
327 Ogre::TexturePtr preview_tex =
328 Ogre::TextureManager::getSingleton().load(
329 sd_entry.sde_entry->filecachename, Ogre::RGN_DEFAULT);
330 if (preview_tex)
331 {
332 // Scale the image (for dashboards only shrink but don't enlarge so players see actual in-game size).
333 ImVec2 max_size = (ImGui::GetWindowSize() * PREVIEW_SIZE_RATIO);
334 ImVec2 size(preview_tex->getWidth(), preview_tex->getHeight());
335 if (m_loader_type != LT_DashBoard || size.x > max_size.x)
336 {
337 size *= max_size.x / size.x; // Fit size along X
338 if (size.y > max_size.y) // Reduce size along Y if needed
339 {
340 size *= max_size.y / size.y;
341 }
342 }
343 // Draw the image
344 ImGui::SetCursorPos((cursor_pos + ImGui::GetWindowSize()) - size);
345 ImGui::Image(reinterpret_cast<ImTextureID>(preview_tex->getHandle()), size);
346 ImGui::SetCursorPos(cursor_pos);
347 }
348 }
349 catch(...)
350 {
351 // Invalid texture file - OGRE exception already logged
352 }
353 }
354
355 // Title and description
357 ImGui::TextWrapped("%s", sd_entry.sde_entry->description.c_str());
358 ImGui::Separator();
359
360 // Details
361 for (AuthorInfo const& author: sd_entry.sde_entry->authors)
362 {
363 ImGui::TextDisabled("%s", _LC("MainSelector", "Author(s): "));
364 ImGui::SameLine();
365 ImGui::TextColored(theme.value_blue_text_color, "%s [%s]", author.name.c_str(), author.type.c_str());
366 }
367 this->DrawAttrInt(_LC("MainSelector", "Version: "), sd_entry.sde_entry->version);
368 if (sd_entry.sde_entry->wheelcount > 0)
369 {
370 ImGui::Text("%s", _LC("MainSelector", "Wheels: "));
371 ImGui::SameLine();
372 ImGui::TextColored(theme.value_blue_text_color, "%dx%d", sd_entry.sde_entry->wheelcount, sd_entry.sde_entry->propwheelcount);
373 }
374 if (sd_entry.sde_entry->truckmass > 0)
375 {
376 ImGui::Text("%s", _LC("MainSelector", "Mass: "));
377 ImGui::SameLine();
378 ImGui::TextColored(theme.value_blue_text_color, "%.2f t", Round(sd_entry.sde_entry->truckmass / 1000.0f, 3));
379 }
380
381 if (m_show_details)
382 {
383 if (sd_entry.sde_entry->loadmass > 0)
384 {
385 ImGui::Text("%s", _LC("MainSelector", "Load Mass: "));
386 ImGui::SameLine();
387 ImGui::TextColored(theme.value_blue_text_color, "%f.2 t", Round(sd_entry.sde_entry->loadmass / 1000.0f, 3));
388 }
389 this->DrawAttrInt(_LC("MainSelector", "Nodes: "), sd_entry.sde_entry->nodecount);
390 this->DrawAttrInt(_LC("MainSelector", "Beams: "), sd_entry.sde_entry->beamcount);
391 this->DrawAttrInt(_LC("MainSelector", "Shocks: "), sd_entry.sde_entry->shockcount);
392 this->DrawAttrInt(_LC("MainSelector", "Hydros: "), sd_entry.sde_entry->hydroscount);
393 this->DrawAttrInt(_LC("MainSelector", "SoundSources: "), sd_entry.sde_entry->soundsourcescount);
394 this->DrawAttrInt(_LC("MainSelector", "Commands: "), sd_entry.sde_entry->commandscount);
395 this->DrawAttrInt(_LC("MainSelector", "Rotators: "), sd_entry.sde_entry->rotatorscount);
396 this->DrawAttrInt(_LC("MainSelector", "Exhausts: "), sd_entry.sde_entry->exhaustscount);
397 this->DrawAttrInt(_LC("MainSelector", "Flares: "), sd_entry.sde_entry->flarescount);
398 this->DrawAttrInt(_LC("MainSelector", "Flexbodies: "), sd_entry.sde_entry->flexbodiescount);
399 this->DrawAttrInt(_LC("MainSelector", "Props: "), sd_entry.sde_entry->propscount);
400 this->DrawAttrInt(_LC("MainSelector", "Wings: "), sd_entry.sde_entry->wingscount);
401 if (sd_entry.sde_entry->hasSubmeshs)
402 {
403 ImGui::Text("%s", _LC("MainSelector", "Using Submeshs: "));
404 ImGui::SameLine();
405 ImGui::TextColored(theme.value_blue_text_color, "%s", sd_entry.sde_entry->hasSubmeshs ?_LC("MainSelector", "Yes") : _LC("MainSelector", "No"));
406 }
407 if (sd_entry.sde_entry->default_skin != "")
408 {
409 ImGui::Text("%s", _LC("MainSelector", "Default skin: "));
410 ImGui::SameLine();
411 ImGui::TextColored(theme.value_blue_text_color, "%s", sd_entry.sde_entry->default_skin.c_str());
412 }
413 this->DrawAttrFloat(_LC("MainSelector", "Torque: "), sd_entry.sde_entry->torque);
414 this->DrawAttrInt(_LC("MainSelector", "Transmission Gear Count: "), sd_entry.sde_entry->numgears);
415 if (sd_entry.sde_entry->minrpm > 0)
416 {
417 ImGui::Text("%s", _LC("MainSelector", "Engine RPM: "));
418 ImGui::SameLine();
419 ImGui::TextColored(theme.value_blue_text_color, "%f - %f", sd_entry.sde_entry->minrpm, sd_entry.sde_entry->maxrpm);
420 }
421 this->DrawAttrStr(_LC("MainSelector", "Unique ID: "), sd_entry.sde_entry->uniqueid);
422 this->DrawAttrStr(_LC("MainSelector", "GUID: "), sd_entry.sde_entry->guid);
423 this->DrawAttrInt(_LC("MainSelector", "Times used: "), sd_entry.sde_entry->usagecounter);
424 this->DrawAttrStr(_LC("MainSelector", "Date and Time modified: "), sd_entry.sde_filetime_str.ToCStr());
425 this->DrawAttrStr(_LC("MainSelector", "Date and Time installed: "), sd_entry.sde_addtime_str.ToCStr());
427 {
428 this->DrawAttrStr(_LC("MainSelector", "Vehicle Type: "), sd_entry.sde_driveable_str.ToCStr());
429 }
430
431 this->DrawAttrSpecial(sd_entry.sde_entry->forwardcommands, _LC("MainSelector", "[forwards commands]"));
432 this->DrawAttrSpecial(sd_entry.sde_entry->importcommands, _LC("MainSelector", "[imports commands]"));
433 this->DrawAttrSpecial(sd_entry.sde_entry->rescuer, _LC("MainSelector", "[is rescuer]"));
434 this->DrawAttrSpecial(sd_entry.sde_entry->custom_particles, _LC("MainSelector", "[uses custom particles]"));
435 this->DrawAttrSpecial(sd_entry.sde_entry->fixescount > 0, _LC("MainSelector", "[has fixes]"));
436 // Engine type 't' (truck) is the default, do not display it
437 this->DrawAttrSpecial(sd_entry.sde_entry->enginetype == 'c', _LC("MainSelector", "[car engine]"));
438 this->DrawAttrSpecial(sd_entry.sde_entry->resource_bundle_type == "Zip", _LC("MainSelector", "[zip archive]"));
439 this->DrawAttrSpecial(sd_entry.sde_entry->resource_bundle_type == "FileSystem", _LC("MainSelector", "[unpacked in directory]"));
440
441 if (!sd_entry.sde_entry->resource_bundle_path.empty())
442 {
443 ImGui::Text("%s", _LC("MainSelector", "Source: "));
444 ImGui::SameLine();
445 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color,
446 "%s", sd_entry.sde_entry->resource_bundle_path.c_str());
447 }
448 if (!sd_entry.sde_entry->fname.empty())
449 {
450 ImGui::Text("%s", _LC("MainSelector", "Filename: "));
451 ImGui::SameLine();
452 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color,
453 "%s", sd_entry.sde_entry->fname.c_str());
454 }
455 }
456 ImGui::Checkbox(_LC("MainSelector", "Show details"), &m_show_details);
457 }
458 ImGui::EndChild();
459
460 ImGui::BeginChild("buttons");
461 if (m_selected_entry != -1)
462 {
464 if (sd_entry.sde_entry->sectionconfigs.size() > 0)
465 {
466 ImGui::PushItemWidth(ImGui::GetWindowWidth() / 2);
467 ImGui::Combo(_LC("MainSelector", "Configuration"), &m_selected_sectionconfig,
469 static_cast<int>(sd_entry.sde_entry->sectionconfigs.size()));
470 ImGui::SameLine();
471 }
472 ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * 17.5f);
473 if (ImGui::Button(_LC("MainSelector", "OK"), ImVec2(ImGui::GetFontSize() * 7.5f, 0.0f)) ||
474 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
475 {
476 this->Apply();
477 }
478 }
479 ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * 9.375f);
480 if (ImGui::Button(_LC("MainSelector", "Cancel"), ImVec2(ImGui::GetFontSize() * 7.5f, 0.0f)) || ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
481 {
482 this->Cancel();
483 }
484 ImGui::EndChild();
485
486 ImGui::EndGroup();
487
488 m_is_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
489
490 ImGui::End();
491 if (!keep_open)
492 {
493 this->Cancel();
494 }
495};
496
497template <typename T1, typename T2>
499{
500 bool operator ()(std::pair<int, Ogre::String> const& a, std::pair<int, Ogre::String> const& b) const
501 {
502 if (a.first == CID_All)
503 return true;
504 if (b.first == CID_All)
505 return false;
506 if (a.first == CID_Fresh)
507 return true;
508 if (b.first == CID_Fresh)
509 return false;
510 return a.second < b.second;
511 }
512};
513
514
516{
517 m_display_categories.clear();
518 m_display_entries.clear();
519
521 {
524 }
525
526 // Find all relevant entries
527 CacheQuery query;
533 if (m_loader_type == LT_DashBoard) // HACK for dashboards
534 {
536 }
537
538 App::GetCacheSystem()->Query(query);
539
540 m_selected_entry = -1;
541 for (CacheQueryResult const& res: query.cqy_results)
542 {
543 if (res.cqr_entry != m_advertised_entry)
544 {
545 m_display_entries.push_back(res.cqr_entry);
547 }
548 }
549
550 // Sort categories alphabetically
552 std::vector<std::pair<int, Ogre::String>> sorted_cats(cats.begin(), cats.end());
553 std::sort(sorted_cats.begin(), sorted_cats.end(), sort_cats<int, Ogre::String>());
554
555 // Display used categories
556 for (auto itor: sorted_cats)
557 {
558 size_t usage = query.cqy_res_category_usage[itor.first];
559 if (usage > 0 || itor.first == CacheCategoryId::CID_Fresh) // HACK: Always include the "fresh" category
560 {
561 m_display_categories.emplace_back(itor.first, itor.second, usage);
562 }
563 }
564}
565
567{
568 std::string input = m_search_input.ToCStr();
569
570 if (input.find(":") == std::string::npos)
571 {
573 m_search_string = input;
574 }
575 else
576 {
577 Ogre::StringVector v = Ogre::StringUtil::split(input, ":");
578 if (v.size() < 2)
579 {
581 m_search_string = "";
582 }
583 else if (v[0] == "guid")
584 {
586 m_search_string = v[1];
587 }
588 else if (v[0] == "author")
589 {
591 m_search_string = v[1];
592 }
593 else if (v[0] == "wheels")
594 {
596 m_search_string = v[1];
597 }
598 else if (v[0] == "file")
599 {
601 m_search_string = v[1];
602 }
603 else
604 {
606 m_search_string = "";
607 }
608 }
609}
610
611// Static helper
612bool MainSelector::ScComboItemGetter(void* data, int idx, const char** out_text)
613{
614 CacheEntryPtr* entry_ptr = static_cast<CacheEntryPtr*>(data);
615 if (out_text)
616 *out_text = (*entry_ptr)->sectionconfigs.at(idx).c_str();
617 return true;
618}
619
620void MainSelector::DrawAttrInt(const char* title, int val) const
621{
622 if (val > 0)
623 {
624 ImGui::Text("%s", title);
625 ImGui::SameLine();
626 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color, "%d", val);
627 }
628}
629
630void MainSelector::DrawAttrFloat(const char* title, float val) const
631{
632 if (val > 0)
633 {
634 ImGui::Text("%s", title);
635 ImGui::SameLine();
636 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color, "%f", val);
637 }
638}
639
640void MainSelector::DrawAttrSpecial(bool val, const char* label) const
641{
642 if (val)
643 {
644 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_red_text_color, "%s", label);
645 }
646}
647
648void MainSelector::DrawAttrStr(const char* desc, std::string const& str) const
649{
650 if (!str.empty())
651 {
652 ImGui::Text("%s", desc);
653 ImGui::SameLine();
654 ImGui::TextColored(App::GetGuiManager()->GetTheme().value_blue_text_color, "%s", str.c_str());
655 }
656}
657
659{
660 m_selected_entry = -1;
663 m_loader_type = LT_None; // Hide window
664 m_kb_focused = true;
665 m_is_hovered = false;
666}
667
669{
670 this->Close();
671
672 if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
673 {
674 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
675 {
677 }
679 }
680 else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
681 {
683 }
684}
685
687{
688 ROR_ASSERT(m_selected_entry > -1); // Programmer error
690
692 {
693 auto request = new LoadScriptRequest();
694 request->lsr_filename = sd_entry.sde_entry->fname;
695 request->lsr_category = ScriptCategory::GADGET;
697 this->Close();
698 }
699 else if (m_loader_type == LT_Terrain &&
700 App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
701 {
703 this->Close();
704 }
705 else if (m_loader_type == LT_DashBoard &&
706 App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
707 {
709 {
712 break;
715 break;
716 default:
717 LOG(fmt::format("[RoR|GameSettings] INTERNAL ERROR - Unrecognized dashboard type being selected ({})!",
719 break;
720 }
722 this->Close();
723 }
724 else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
725 {
727 std::string sectionconfig;
728 if (sd_entry.sde_entry->sectionconfigs.size() > 0)
729 {
730 sectionconfig = sd_entry.sde_entry->sectionconfigs[m_selected_sectionconfig];
731 }
732 this->Close();
733
734 App::GetGameContext()->OnLoaderGuiApply(type, sd_entry.sde_entry, sectionconfig);
735 }
736}
737
738// Static helper
739bool MainSelector::CatComboItemGetter(void* data, int idx, const char** out_text)
740{
741 DisplayCategoryVec* dc_vec = static_cast<DisplayCategoryVec*>(data);
742 if (out_text)
743 *out_text = dc_vec->at(idx).sdc_title.ToCStr();
744 return true;
745}
746
748 sde_entry(entry)
749{
750 // Pre-format strings
751 if (sde_entry->filetime > 0)
752 {
753 sde_filetime_str = asctime(gmtime(&sde_entry->filetime));
754 }
755 if (sde_entry->addtimestamp > 0)
756 {
757 sde_addtime_str = asctime(gmtime(&sde_entry->addtimestamp));
758 }
759
761}
762
763MainSelector::DisplayCategory::DisplayCategory(int id, std::string const& name, size_t usage)
764 : sdc_category_id(id)
765{
766 sdc_title << "(" << usage << ") " << _LC("ModCategory", name.c_str());
767}
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
void AddResourcePack(ResourcePack const &resource_pack, std::string const &override_rgn="")
Loads resources if not already loaded.
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, std::string > m_kept_searchstring
Stays empty if 'ui_keep_search' is false.
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)
const float LEFT_PANE_WIDTH
Relative to font size.
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)
Ogre::TexturePtr m_settings_icon
std::vector< DisplayCategory > DisplayCategoryVec
GUI::GameSettings GameSettings
Definition GUIManager.h:120
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
GUI::GameMainMenu GameMainMenu
Definition GUIManager.h:118
GuiTheme & GetTheme()
Definition GUIManager.h:171
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:94
@ MSG_SIM_LOAD_TERRN_REQUESTED
@ GADGET
Associated with a .gadget mod file, launched via UI or any method given below for CUSTOM scripts (use...
ContentManager * GetContentManager()
GUIManager * GetGuiManager()
GameContext * GetGameContext()
CVar * app_state
CVar * mp_state
CVar * ui_keep_search
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
bool DrawGCheckbox(CVar *cvar, const char *label)
Definition GUIUtils.cpp:287
@ 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
static const ResourcePack FAMICONS
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