16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-19 03:09:18 +02:00

add missing files

This commit is contained in:
milek7
2019-07-31 17:22:13 +02:00
parent f10e15f408
commit ee65ca77e3
12 changed files with 928 additions and 0 deletions

89
launcher/keymapper.cpp Normal file
View File

@@ -0,0 +1,89 @@
#include "stdafx.h"
#include "keymapper.h"
#include "simulation.h"
ui::keymapper_panel::keymapper_panel()
: ui_panel(STR("Keymapper"), true)
{
keyboard.init();
}
bool ui::keymapper_panel::key(int key)
{
if (!is_open || bind_active == user_command::none)
return false;
auto it = keyboard.keytonamemap.find(key);
if (it == keyboard.keytonamemap.end())
return false;
if (Global.shiftState)
key |= keyboard_input::keymodifier::shift;
if (Global.ctrlState)
key |= keyboard_input::keymodifier::control;
keyboard.bindings().insert_or_assign(bind_active, key);
bind_active = user_command::none;
keyboard.bind();
return true;
}
void ui::keymapper_panel::render()
{
if (!is_open)
return;
auto const panelname{(title.empty() ? m_name : title) + "###" + m_name};
if (ImGui::Begin(panelname.c_str(), &is_open)) {
if (ImGui::Button(STR_C("Load")))
keyboard.init();
ImGui::SameLine();
if (ImGui::Button(STR_C("Save")))
keyboard.dump_bindings();
for (const std::pair<user_command, int> &binding : keyboard.bindings()) {
ImGui::AlignTextToFramePadding();
ImGui::Text(simulation::Commands_descriptions[static_cast<std::size_t>(binding.first)].name.c_str());
std::string label;
if (binding.second & keyboard_input::keymodifier::control)
label += "ctrl + ";
if (binding.second & keyboard_input::keymodifier::shift)
label += "shift + ";
auto it = keyboard.keytonamemap.find(binding.second & 0xFFFF);
if (it != keyboard.keytonamemap.end())
label += it->second;
label = ToUpper(label);
bool styles_pushed = false;
if (bind_active == binding.first) {
label = "?";
ImVec4 active_col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
ImGui::PushStyleColor(ImGuiCol_Button, active_col);
styles_pushed = true;
}
label += "##" + std::to_string((int)binding.first);
ImGui::SameLine(ImGui::GetContentRegionAvailWidth() - 150);
if (ImGui::Button(label.c_str(), ImVec2(150, 0))) {
if (bind_active == binding.first)
bind_active = user_command::none;
else
bind_active = binding.first;
}
if (styles_pushed)
ImGui::PopStyleColor();
}
}
ImGui::End();
}

21
launcher/keymapper.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "uilayer.h"
#include "driverkeyboardinput.h"
namespace ui
{
class keymapper_panel : public ui_panel
{
public:
keymapper_panel();
void render() override;
bool key(int key);
private:
driverkeyboard_input keyboard;
user_command bind_active = user_command::none;
};
} // namespace ui

43
launcher/launchermode.cpp Normal file
View File

@@ -0,0 +1,43 @@
#include "stdafx.h"
#include "launcher/launchermode.h"
#include "launcher/launcheruilayer.h"
#include "application.h"
#include "simulation.h"
#include "Globals.h"
launcher_mode::launcher_mode()
{
m_userinterface = std::make_shared<launcher_ui>();
}
bool launcher_mode::init()
{
return true;
}
bool launcher_mode::update()
{
return true;
}
void launcher_mode::enter()
{
Application.set_cursor( GLFW_CURSOR_NORMAL );
simulation::is_ready = false;
m_userinterface->set_background( "logo" );
Application.set_title(Global.AppName);
}
void launcher_mode::exit()
{
}
void launcher_mode::on_key(const int Key, const int Scancode, const int Action, const int Mods)
{
Global.shiftState = ( Mods & GLFW_MOD_SHIFT ) ? true : false;
Global.ctrlState = ( Mods & GLFW_MOD_CONTROL ) ? true : false;
m_userinterface->on_key(Key, Action);
}

45
launcher/launchermode.h Normal file
View File

@@ -0,0 +1,45 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "applicationmode.h"
class launcher_mode : public application_mode {
public:
// constructors
launcher_mode();
// methods
// initializes internal data structures of the mode. returns: true on success, false otherwise
bool
init() override;
// mode-specific update of simulation data. returns: false on error, true otherwise
bool
update() override;
// maintenance method, called when the mode is activated
void
enter() override;
// maintenance method, called when the mode is deactivated
void
exit() override;
// input handlers
void
on_key( int const Key, int const Scancode, int const Action, int const Mods );
void
on_cursor_pos( double const Horizontal, double const Vertical ) override { ; }
void
on_mouse_button( int const Button, int const Action, int const Mods ) override { ; }
void
on_scroll( double const Xoffset, double const Yoffset ) override { ; }
void
on_event_poll() override { ; }
bool
is_command_processor() override { return false; }
};

View File

@@ -0,0 +1,18 @@
#include "stdafx.h"
#include "launcher/launcheruilayer.h"
launcher_ui::launcher_ui()
{
add_external_panel(&m_scenerylist_panel);
add_external_panel(&m_keymapper_panel);
add_external_panel(&m_mainmenu_panel);
add_external_panel(&m_vehiclepicker_panel);
}
bool launcher_ui::on_key(const int Key, const int Action)
{
if (m_keymapper_panel.key(Key))
return true;
return ui_layer::on_key(Key, Action);
}

View File

@@ -0,0 +1,28 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "uilayer.h"
#include "launcher/scenery_list.h"
#include "launcher/keymapper.h"
#include "launcher/mainmenu.h"
#include "launcher/vehicle_picker.h"
class launcher_ui : public ui_layer {
public:
launcher_ui();
bool on_key(const int Key, const int Action) override;
private:
ui::scenerylist_panel m_scenerylist_panel;
ui::keymapper_panel m_keymapper_panel;
ui::mainmenu_panel m_mainmenu_panel;
ui::vehiclepicker_panel m_vehiclepicker_panel;
};

26
launcher/mainmenu.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include "stdafx.h"
#include "launcher/mainmenu.h"
ui::mainmenu_panel::mainmenu_panel()
: ui_panel(STR("Main menu"), true)
{
}
void ui::mainmenu_panel::render()
{
if (!is_open)
return;
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse;
ImVec2 display_size = ImGui::GetIO().DisplaySize;
ImGui::SetNextWindowPos(ImVec2(display_size.x * 0.66f, display_size.y / 2.0f));
ImGui::SetNextWindowSize(ImVec2(200, -1));
if (ImGui::Begin(m_name.c_str(), nullptr, flags)) {
ImGui::Button(STR_C("Scenario list"), ImVec2(-1, 0));
ImGui::Button(STR_C("Settings"), ImVec2(-1, 0));
ImGui::Button(STR_C("Quit"), ImVec2(-1, 0));
}
ImGui::End();
}

14
launcher/mainmenu.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "uilayer.h"
namespace ui
{
class mainmenu_panel : public ui_panel
{
public:
mainmenu_panel();
void render() override;
};
} // namespace ui

165
launcher/scenery_list.cpp Normal file
View File

@@ -0,0 +1,165 @@
#include "stdafx.h"
#include "scenery_list.h"
#include "imgui/imgui.h"
#include "utilities.h"
#include "renderer.h"
#include <filesystem>
ui::scenerylist_panel::scenerylist_panel()
: ui_panel(STR("Scenario list"), true)
{
scan_scenarios();
std::sort(scenarios.begin(), scenarios.end(),
[](const scenery_desc &a, const scenery_desc &b) { return a.path < b.path; });
}
void ui::scenerylist_panel::render()
{
if (!is_open)
return;
auto const panelname{(title.empty() ? m_name : title) + "###" + m_name};
if (ImGui::Begin(panelname.c_str(), &is_open)) {
ImGui::Columns(3);
if (ImGui::BeginChild("child1")) {
std::string prev_prefix;
bool collapse_open = false;
for (auto const &desc : scenarios) {
std::string name = desc.path.stem();
std::string prefix = name.substr(0, name.find_first_of("-_"));
if (prefix.empty())
prefix = name;
bool just_opened = false;
if (prefix != prev_prefix) {
collapse_open = ImGui::CollapsingHeader(prefix.c_str());
just_opened = ImGui::IsItemDeactivated();
prev_prefix = prefix;
}
if (collapse_open) {
if (just_opened)
selected_scenery = &desc;
ImGui::Indent(10.0f);
if (ImGui::Selectable(name.c_str(), &desc == selected_scenery))
selected_scenery = &desc;
ImGui::Unindent(10.0f);
}
}
} ImGui::EndChild();
ImGui::NextColumn();
if (ImGui::BeginChild("child2")) {
if (selected_scenery) {
ImGui::TextWrapped("%s", selected_scenery->name.c_str());
ImGui::TextWrapped("%s", selected_scenery->description.c_str());
ImGui::Separator();
for (auto const &link : selected_scenery->links) {
if (ImGui::Button(link.second.c_str(), ImVec2(-1, 0))) {
std::string file = ToLower(link.first);
#ifdef _WIN32
system(("start \"" + file + "\"").c_str());
#elif __linux__
system(("xdg-open \"" + file + "\"").c_str());
#elif __APPLE__
system(("open \"" + file + "\"").c_str());
#endif
}
}
}
} ImGui::EndChild();
ImGui::NextColumn();
if (selected_scenery) {
if (ImGui::BeginChild("child3", ImVec2(0, 0), false, ImGuiWindowFlags_NoScrollbar)) {
if (!selected_scenery->image_path.empty()) {
scenery_desc *desc = const_cast<scenery_desc*>(selected_scenery);
desc->image = GfxRenderer.Fetch_Texture(selected_scenery->image_path, true);
desc->image_path.clear();
}
if (selected_scenery->image != null_handle) {
opengl_texture &tex = GfxRenderer.Texture(selected_scenery->image);
tex.create();
if (tex.is_ready) {
float avail_width = ImGui::GetContentRegionAvailWidth();
float height = avail_width / tex.width() * tex.height();
ImGui::Image(reinterpret_cast<void *>(tex.id), ImVec2(avail_width, height), ImVec2(0, 1), ImVec2(1, 0));
}
}
} ImGui::EndChild();
if (ImGui::Begin(STR_C("Select trainset"))) {
for (auto const &trainset : selected_scenery->trainsets) {
ImGui::Selectable("Trainset", false);
}
}
ImGui::End();
}
}
ImGui::End();
}
void ui::scenerylist_panel::scan_scenarios()
{
for (auto &f : std::filesystem::directory_iterator("scenery")) {
std::filesystem::path path(f.path());
if (*(path.filename().string().begin()) == '$')
continue;
if (string_ends_with(path, ".scn"))
scan_scn(path);
}
}
void ui::scenerylist_panel::scan_scn(std::string path)
{
scenarios.emplace_back();
scenery_desc &desc = scenarios.back();
desc.path = path;
std::ifstream stream(path, std::ios_base::binary | std::ios_base::in);
std::string line;
while (std::getline(stream, line))
{
if (line.size() < 6 || !string_starts_with(line, "//$"))
continue;
if (line[3] == 'i')
desc.image_path = "scenery/images/" + line.substr(5);
else if (line[3] == 'n')
desc.name = line.substr(5);
else if (line[3] == 'd')
desc.description += line.substr(5) + '\n';
else if (line[3] == 'f') {
std::string lang;
std::string file;
std::string label;
std::istringstream stream(line.substr(5));
stream >> lang >> file;
std::getline(stream, label);
desc.links.push_back(std::make_pair(file, label));
}
else if (line[3] == 'o') {
desc.trainsets.emplace_back();
trainset_desc &trainset = desc.trainsets.back();
trainset.name = line.substr(5);
}
}
}

53
launcher/scenery_list.h Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include "uilayer.h"
#include <filesystem>
struct trainset_desc {
std::string description;
std::string name;
std::string track;
float offset { 0.f };
float velocity { 0.f };
std::vector<std::string> vehicles;
std::vector<int> couplings;
};
struct scenery_desc {
std::filesystem::path path;
std::string name;
std::string description;
std::string image_path;
texture_handle image = 0;
std::vector<std::pair<std::string, std::string>> links;
std::vector<trainset_desc> trainsets;
std::string title() {
if (!name.empty())
return name;
else
return path.stem().string();
}
};
namespace ui
{
class scenerylist_panel : public ui_panel
{
public:
scenerylist_panel();
void render() override;
private:
std::vector<scenery_desc> scenarios;
scenery_desc const *selected_scenery = nullptr;
void scan_scenarios();
void scan_scn(std::string path);
};
} // namespace ui

324
launcher/vehicle_picker.cpp Normal file
View File

@@ -0,0 +1,324 @@
#include "stdafx.h"
#include "launcher/vehicle_picker.h"
#include "renderer.h"
GLuint ui::deferred_image::get()
{
if (!path.empty()) {
image = GfxRenderer.Fetch_Texture(path, true);
path.clear();
}
if (image != null_handle) {
opengl_texture &tex = GfxRenderer.Texture(image);
tex.create();
if (tex.is_ready)
return tex.id;
}
return -1;
}
glm::ivec2 ui::deferred_image::size()
{
if (image != null_handle) {
opengl_texture &tex = GfxRenderer.Texture(image);
return glm::ivec2(tex.width(), tex.height());
}
return glm::ivec2();
}
ui::vehiclepicker_panel::vehiclepicker_panel()
: ui_panel(STR("Select vehicle"), true)
{
auto now = std::chrono::high_resolution_clock::now();
bank.scan_textures();
auto diff = std::chrono::high_resolution_clock::now() - now;
std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(diff).count() << std::endl;
filter.resize(256, 0);
}
void ui::vehiclepicker_panel::render()
{
if (!is_open)
return;
static std::map<vehicle_type, std::string> type_names =
{
{ vehicle_type::electric_loco, STRN("Electric locos") },
{ vehicle_type::diesel_loco, STRN("Diesel locos") },
{ vehicle_type::steam_loco, STRN("Steam locos") },
{ vehicle_type::railcar, STRN("Railcars") },
{ vehicle_type::emu, STRN("EMU") },
{ vehicle_type::utility, STRN("Utility") },
{ vehicle_type::draisine, STRN("Draisines") },
{ vehicle_type::tram, STRN("Trams") },
{ vehicle_type::carriage, STRN("Carriages") },
{ vehicle_type::truck, STRN("Trucks") },
{ vehicle_type::bus, STRN("Buses") },
{ vehicle_type::car, STRN("Cars") },
{ vehicle_type::man, STRN("People") },
{ vehicle_type::animal, STRN("Animals") },
{ vehicle_type::unknown, STRN("Unknown") }
};
if (ImGui::Begin(m_name.c_str())) {
ImGui::InputText("##filter", &filter[0], filter.size());
ImGui::Columns(3);
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.0f, 0.5f));
if (ImGui::BeginChild("box1")) {
for (auto const &e : type_names) {
deferred_image *image;
auto it = bank.category_icons.find(e.first);
if (it != bank.category_icons.end())
image = &it->second;
if (selectable_image(Translations.lookup_s(e.second).c_str(), e.first == selected_type, image))
selected_type = e.first;
}
}
ImGui::EndChild();
ImGui::NextColumn();
if (ImGui::BeginChild("box2")) {
for (auto const &v : bank.vehicles) {
auto desc = v.second;
if (selected_type == desc->type) {
if (desc->matching_skinsets.size() == 0)
continue;
auto image = &desc->matching_skinsets[0].mini;
if (selectable_image(desc->path.stem().c_str(), desc == selected_vehicle, image))
selected_vehicle = desc;
}
}
}
ImGui::EndChild();
ImGui::NextColumn();
if (ImGui::BeginChild("box3")) {
for (auto const &v : bank.vehicles) {
auto desc = v.second;
if (selected_vehicle == desc) {
for (auto &skin : desc->matching_skinsets) {
auto image = &skin.mini;
if (selectable_image(skin.skins[0].stem().c_str(), &skin == selected_skinset, image))
selected_skinset = &skin;
}
}
}
}
ImGui::EndChild();
ImGui::PopStyleVar();
}
ImGui::End();
}
bool ui::vehiclepicker_panel::selectable_image(const char *desc, bool selected, deferred_image* image)
{
bool ret = ImGui::Selectable(desc, selected, 0, ImVec2(0, 30));
if (!image)
return ret;
GLuint tex = image->get();
if (tex != -1) {
glm::ivec2 size = image->size();
float width = 30.0f / size.y * size.x;
ImGui::SameLine(ImGui::GetContentRegionAvailWidth() - width);
ImGui::Image(reinterpret_cast<void*>(tex), ImVec2(width, 30), ImVec2(0, 1), ImVec2(1, 0));
}
return ret;
}
void ui::vehicles_bank::scan_textures()
{
for (auto &f : std::filesystem::recursive_directory_iterator("dynamic")) {
if (f.path().filename() != "textures.txt")
continue;
std::fstream stream(f.path(), std::ios_base::binary | std::ios_base::in);
ctx_path = f.path().parent_path();
std::string line;
while (std::getline(stream, line))
{
if (line.size() < 3)
continue;
if (line.back() == '\r')
line.pop_back();
parse_entry(line);
}
}
}
void ui::vehicles_bank::parse_entry(const std::string &line)
{
std::string content;
std::string comments;
size_t pos = line.find("//");
if (pos != -1)
comments = line.substr(pos);
content = line.substr(0, pos);
std::istringstream stream(content);
std::string target;
std::getline(stream, target, '=');
std::string param;
while (std::getline(stream, param, '=')) {
if (line[0] == '!')
parse_category_entry(param);
else if (line[0] == '*' && line[1] == '*')
parse_texture_rule(target.substr(2), param);
else if (line[0] == '*')
parse_coupling_rule(target.substr(1), param);
else if (line[0] != '@')
parse_texture_info(target, param, comments);
}
}
void ui::vehicles_bank::parse_category_entry(const std::string &param)
{
static std::unordered_map<char, vehicle_type> type_map = {
{ 'e', vehicle_type::electric_loco },
{ 's', vehicle_type::diesel_loco },
{ 'p', vehicle_type::steam_loco },
{ 'a', vehicle_type::railcar },
{ 'z', vehicle_type::emu },
{ 'r', vehicle_type::utility },
{ 'd', vehicle_type::draisine },
{ 't', vehicle_type::tram },
{ 'c', vehicle_type::truck },
{ 'b', vehicle_type::bus },
{ 'o', vehicle_type::car },
{ 'h', vehicle_type::man },
{ 'f', vehicle_type::animal },
};
ctx_type = vehicle_type::unknown;
std::istringstream stream(param);
std::string tok;
std::getline(stream, tok, ',');
if (tok.size() < 1)
return;
auto it = type_map.find(tok[0]);
if (it != type_map.end())
ctx_type = it->second;
else if (tok[0] >= 'A' && tok[0] <= 'Z')
ctx_type = vehicle_type::carriage;
std::string mini;
std::getline(stream, mini, ',');
category_icons.emplace(ctx_type, "textures/mini/" + ToLower(mini));
}
void ui::vehicles_bank::parse_texture_info(const std::string &target, const std::string &param, const std::string &comment)
{
std::istringstream stream(param);
std::string model, mini, miniplus;
std::getline(stream, model, ',');
std::getline(stream, mini, ',');
std::getline(stream, miniplus, ',');
skin_set set;
set.group = mini;
if (!miniplus.empty())
set.mini = deferred_image("textures/mini/" + ToLower(miniplus));
std::istringstream tex_stream(target);
std::string texture;
while (std::getline(tex_stream, texture, '|')) {
auto path = ctx_path;
path.append(texture);
set.skins.push_back(path);
}
get_vehicle(model)->matching_skinsets.push_back(set);
}
void ui::vehicles_bank::parse_coupling_rule(const std::string &target, const std::string &param)
{
if (param == "?")
return;
std::istringstream stream(param);
int coupling_flag;
stream >> coupling_flag;
stream.ignore(1000, ',');
std::string connected;
std::getline(stream, connected, ',');
std::string param1, param2;
std::getline(stream, param1, ',');
std::getline(stream, param2, ',');
coupling_rule rule;
rule.coupled_vehicle = get_vehicle(target);
rule.coupling_flag = coupling_flag;
get_vehicle(connected)->coupling_rules.push_back(rule);
}
void ui::vehicles_bank::parse_texture_rule(const std::string &target, const std::string &param)
{
std::istringstream stream(param);
std::string prev;
std::getline(stream, prev, ',');
std::string replace_rule;
while (std::getline(stream, replace_rule, ',')) {
if (replace_rule == "-")
break;
std::istringstream rule_stream(replace_rule);
std::string src, dst;
std::getline(rule_stream, src, '-');
std::getline(rule_stream, dst, '-');
texture_rule rule;
rule.previous_vehicle = get_vehicle(prev);
rule.replace_rules.emplace_back(src, dst);
get_vehicle(target)->texture_rules.push_back(rule);
}
}
std::shared_ptr<ui::vehicle_desc> ui::vehicles_bank::get_vehicle(const std::string &name)
{
auto path = ctx_path;
path.append(name);
auto it = vehicles.find(path);
if (it != vehicles.end()) {
return it->second;
}
else {
auto desc = std::make_shared<vehicle_desc>();
desc->type = ctx_type;
desc->path = path;
vehicles.emplace(path, desc);
return desc;
}
}

102
launcher/vehicle_picker.h Normal file
View File

@@ -0,0 +1,102 @@
#pragma once
#include "uilayer.h"
namespace ui {
enum class vehicle_type {
none = -1,
electric_loco,
diesel_loco,
steam_loco,
railcar,
emu,
utility,
draisine,
tram,
carriage,
truck,
bus,
car,
man,
animal,
unknown
};
class deferred_image {
public:
deferred_image() = default;
deferred_image(const std::string &p) : path(p) { }
GLuint get();
glm::ivec2 size();
private:
std::string path;
texture_handle image = 0;
};
struct skin_set {
std::vector<std::filesystem::path> skins;
deferred_image mini;
std::string group;
};
struct vehicle_desc;
struct texture_rule {
std::shared_ptr<vehicle_desc> previous_vehicle;
std::vector<std::pair<std::string, std::string>> replace_rules;
};
struct coupling_rule {
std::shared_ptr<vehicle_desc> coupled_vehicle;
int coupling_flag;
};
struct vehicle_desc {
vehicle_type type;
std::filesystem::path path;
std::vector<skin_set> matching_skinsets;
std::vector<coupling_rule> coupling_rules;
std::vector<texture_rule> texture_rules;
};
class vehicles_bank {
public:
std::unordered_map<vehicle_type, deferred_image> category_icons;
std::map<std::filesystem::path, std::shared_ptr<vehicle_desc>> vehicles;
std::map<std::string, std::vector<std::shared_ptr<vehicle_desc>>> group_map;
void scan_textures();
private:
void parse_entry(const std::string &line);
void parse_category_entry(const std::string &param);
void parse_coupling_rule(const std::string &target, const std::string &param);
void parse_texture_rule(const std::string &target, const std::string &param);
void parse_texture_info(const std::string &target, const std::string &param, const std::string &comment);
std::shared_ptr<vehicle_desc> get_vehicle(const std::string &name);
vehicle_type ctx_type = vehicle_type::unknown;
std::filesystem::path ctx_path;
std::string ctx_model;
};
class vehiclepicker_panel : public ui_panel
{
public:
vehiclepicker_panel();
void render() override;
private:
bool selectable_image(const char *desc, bool selected, deferred_image *image);
vehicle_type selected_type = vehicle_type::none;
std::shared_ptr<vehicle_desc> selected_vehicle;
const skin_set *selected_skinset = nullptr;
vehicles_bank bank;
std::string filter;
};
} // namespace ui