mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-03 15:30:21 +01:00
feat(editor): presets of the track properties, applied on the selected tracks
This commit is contained in:
@@ -184,6 +184,7 @@ set(SOURCES
|
|||||||
"input/editorkeyboardinput.cpp"
|
"input/editorkeyboardinput.cpp"
|
||||||
"editor/editorSettings.cpp"
|
"editor/editorSettings.cpp"
|
||||||
"editor/editorModelSets.cpp"
|
"editor/editorModelSets.cpp"
|
||||||
|
"editor/editorTrackPresets.cpp"
|
||||||
"editor/editorIncludeInfo.cpp"
|
"editor/editorIncludeInfo.cpp"
|
||||||
"editor/editorTerrain.cpp"
|
"editor/editorTerrain.cpp"
|
||||||
"editor/editorTrack.cpp"
|
"editor/editorTrack.cpp"
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ extern char **environ;
|
|||||||
#include "application/application.h"
|
#include "application/application.h"
|
||||||
#include "editor/editorSettings.hpp"
|
#include "editor/editorSettings.hpp"
|
||||||
#include "editor/editorModelSets.hpp"
|
#include "editor/editorModelSets.hpp"
|
||||||
|
#include "editor/editorTrackPresets.hpp"
|
||||||
#include "editor/editorIncludeInfo.hpp"
|
#include "editor/editorIncludeInfo.hpp"
|
||||||
#include "editor/editorGroundMesh.hpp"
|
#include "editor/editorGroundMesh.hpp"
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
@@ -534,6 +535,7 @@ editor_mode::editor_mode() {
|
|||||||
ui()->set_new_scenery([this]() { m_newscenery_asked = true; });
|
ui()->set_new_scenery([this]() { m_newscenery_asked = true; });
|
||||||
ui()->set_open_scenery([this]() { m_openscenery_asked = true; });
|
ui()->set_open_scenery([this]() { m_openscenery_asked = true; });
|
||||||
ui()->set_edit_menu([this]() { render_edit_menu(); });
|
ui()->set_edit_menu([this]() { render_edit_menu(); });
|
||||||
|
ui()->set_tools_menu([this]() { render_track_presets_menu(); });
|
||||||
ui()->set_menu_options([this]() {
|
ui()->set_menu_options([this]() {
|
||||||
render_object_menu();
|
render_object_menu();
|
||||||
render_view_menu();
|
render_view_menu();
|
||||||
@@ -639,6 +641,7 @@ bool editor_mode::init()
|
|||||||
EditorSettings.load();
|
EditorSettings.load();
|
||||||
Global.EditorOrthoBallast = EditorSettings.top_ballast();
|
Global.EditorOrthoBallast = EditorSettings.top_ballast();
|
||||||
EditorModelSets.load();
|
EditorModelSets.load();
|
||||||
|
EditorTrackPresets.load();
|
||||||
Camera.Init({0, 15, 0}, {glm::radians(-30.0), glm::radians(180.0), 0}, nullptr);
|
Camera.Init({0, 15, 0}, {glm::radians(-30.0), glm::radians(180.0), 0}, nullptr);
|
||||||
return m_input.init();
|
return m_input.init();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1796,6 +1796,15 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
void render_track_style(std::vector<TTrack *> const &Tracks);
|
void render_track_style(std::vector<TTrack *> const &Tracks);
|
||||||
void copy_track_style(TTrack const &Track);
|
void copy_track_style(TTrack const &Track);
|
||||||
std::size_t paste_track_style(std::vector<TTrack *> const &Tracks);
|
std::size_t paste_track_style(std::vector<TTrack *> const &Tracks);
|
||||||
|
// which groups of the properties are pasted or put on the tracks by a preset, in the order of the groups of editormodetrackprops.cpp
|
||||||
|
using track_groups = std::array<bool, 11>;
|
||||||
|
// the values of the properties of the chosen groups put on the tracks, in one step of the undo. returns: the number of the
|
||||||
|
// tracks changed, none when no value is of the chosen groups
|
||||||
|
std::optional<std::size_t> apply_track_values(std::vector<std::pair<std::string, std::string>> const &Values, track_groups const &Groups, std::vector<TTrack *> const &Tracks, char const *What);
|
||||||
|
// presets of the properties of the tracks (editorTrackPresets), made from a track and put on the selected ones
|
||||||
|
void render_track_presets(std::vector<TTrack *> const &Tracks);
|
||||||
|
void render_track_presets_menu();
|
||||||
|
std::size_t apply_track_preset(int const Id, std::vector<TTrack *> const &Tracks);
|
||||||
std::vector<TTrack *> similar_tracks(int const Property, std::vector<TTrack *> const &Tracks) const;
|
std::vector<TTrack *> similar_tracks(int const Property, std::vector<TTrack *> const &Tracks) const;
|
||||||
// values of the properties over the selected tracks, each with the number of the tracks having it
|
// values of the properties over the selected tracks, each with the number of the tracks having it
|
||||||
struct track_values
|
struct track_values
|
||||||
@@ -1841,12 +1850,19 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
{
|
{
|
||||||
std::string from;
|
std::string from;
|
||||||
std::vector<std::pair<std::string, std::string>> values;
|
std::vector<std::pair<std::string, std::string>> values;
|
||||||
// speed, environment, overhead line, track circuits, rails, trackbed, its shape, sleepers, ride and sound, frogs
|
// speed, environment, overhead line, track circuits, rails, trackbed, its shape, sleepers, ride and sound, frogs, sleeper placement
|
||||||
std::array<bool, 10> groups{false, false, false, false, true, true, true, true, false, true};
|
track_groups groups{false, false, false, false, true, true, true, true, false, true, true};
|
||||||
bool open{false};
|
bool open{false};
|
||||||
bool picking{false}; // the next click on a track in the view copies its properties
|
bool picking{false}; // the next click on a track in the view copies its properties
|
||||||
};
|
};
|
||||||
track_style m_track_style;
|
track_style m_track_style;
|
||||||
|
struct track_presets_panel
|
||||||
|
{
|
||||||
|
int selected{0}; // id of the preset chosen in the inspector
|
||||||
|
int nameid{0}; // the preset whose name is in the field
|
||||||
|
char name[128]{};
|
||||||
|
};
|
||||||
|
track_presets_panel m_track_presets;
|
||||||
std::vector<std::vector<TTrack *>> m_track_similar;
|
std::vector<std::vector<TTrack *>> m_track_similar;
|
||||||
TTrack const *m_track_list_hover{nullptr}; // in the list of the selected tracks
|
TTrack const *m_track_list_hover{nullptr}; // in the list of the selected tracks
|
||||||
int m_track_list_hover_frame{-1};
|
int m_track_list_hover_frame{-1};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "application/editormodeshared.h"
|
#include "application/editormodeshared.h"
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
#include "editor/editorGeometry.hpp"
|
#include "editor/editorGeometry.hpp"
|
||||||
|
#include "editor/editorTrackPresets.hpp"
|
||||||
|
|
||||||
#include "rendering/renderer.h"
|
#include "rendering/renderer.h"
|
||||||
#include "world/Track.h"
|
#include "world/Track.h"
|
||||||
@@ -224,10 +225,13 @@ enum group : int
|
|||||||
sleepers,
|
sleepers,
|
||||||
ride_sound,
|
ride_sound,
|
||||||
frogs,
|
frogs,
|
||||||
|
sleeper_placement, // spacing and shift of the sleepers and of the trackbed under them, apart from their models
|
||||||
group_count
|
group_count
|
||||||
};
|
};
|
||||||
|
|
||||||
char const *const kGroupLabels[group_count] = {"Speed", "Environment", "Overhead line", "Track circuits", "Rails", "Trackbed", "Shape of the trackbed", "Sleepers", "Ride and sound", "Frog and check rails"};
|
char const *const kGroupLabels[group_count] = {"Speed", "Environment", "Overhead line", "Track circuits", "Rails", "Trackbed", "Shape of the trackbed", "Sleepers", "Ride and sound", "Frog and check rails", "Sleeper placement"};
|
||||||
|
// the groups as the presets keep them in their file, by names which don't change with the order of the groups
|
||||||
|
char const *const kGroupKeys[group_count] = {"speed", "environment", "overhead", "circuits", "rails", "trackbed", "bedshape", "sleepers", "ride", "frogs", "sleeperplacement"};
|
||||||
|
|
||||||
struct track_property
|
struct track_property
|
||||||
{
|
{
|
||||||
@@ -632,13 +636,13 @@ std::vector<track_property> make_properties()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
add_float(list, "sleeperspacing", section::look_sleepers, group::sleepers, "Sleeper spacing (m)", "Distance between the sleepers along the track", &TTrack::m_sleeper_frequency, 3, 0.1f, true);
|
add_float(list, "sleeperspacing", section::look_sleepers, group::sleeper_placement, "Sleeper spacing (m)", "Distance between the sleepers along the track", &TTrack::m_sleeper_frequency, 3, 0.1f, true);
|
||||||
char const *const offsets[][3] = {{"sleeperside", "Sleeper shift sideways (m)", "Shift of each model sideways, to the right of the track"},
|
char const *const offsets[][3] = {{"sleeperside", "Sleeper shift sideways (m)", "Shift of each model sideways, to the right of the track"},
|
||||||
{"sleeperalong", "Sleeper shift along (m)", "Shift of each model along the track"},
|
{"sleeperalong", "Sleeper shift along (m)", "Shift of each model along the track"},
|
||||||
{"sleeperup", "Sleeper shift up (m)", "Shift of each model up"}};
|
{"sleeperup", "Sleeper shift up (m)", "Shift of each model up"}};
|
||||||
for (int axis = 0; axis < 3; ++axis)
|
for (int axis = 0; axis < 3; ++axis)
|
||||||
{
|
{
|
||||||
auto &property{add(list, offsets[axis][0], field::number, section::look_sleepers, group::sleepers, offsets[axis][1], offsets[axis][2])};
|
auto &property{add(list, offsets[axis][0], field::number, section::look_sleepers, group::sleeper_placement, offsets[axis][1], offsets[axis][2])};
|
||||||
property.rebuild = true;
|
property.rebuild = true;
|
||||||
property.get = [axis](TTrack const &Track) { return decimal(Track.m_sleeper_offset[axis], 3); };
|
property.get = [axis](TTrack const &Track) { return decimal(Track.m_sleeper_offset[axis], 3); };
|
||||||
property.set = [axis](TTrack &Track, std::string const &Text) {
|
property.set = [axis](TTrack &Track, std::string const &Text) {
|
||||||
@@ -649,7 +653,7 @@ std::vector<track_property> make_properties()
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
add_float(list, "bedshift", section::look_sleepers, group::sleepers, "Trackbed shift (m)", "Vertical shift of the drawn trackbed under the sleepers, negative lowers it", &TTrack::m_sleeper_ballast_z, 3, lowest, true);
|
add_float(list, "bedshift", section::look_sleepers, group::sleeper_placement, "Trackbed shift (m)", "Vertical shift of the drawn trackbed under the sleepers, negative lowers it", &TTrack::m_sleeper_ballast_z, 3, lowest, true);
|
||||||
|
|
||||||
// ride and sound
|
// ride and sound
|
||||||
add_float(list, "friction", section::ride, group::ride_sound, "Friction", "Friction coefficient of the rails, 0.15 for the dry rails; lower values make the wheels slip", &TTrack::fFriction, 3, 0.f, false);
|
add_float(list, "friction", section::ride, group::ride_sound, "Friction", "Friction coefficient of the rails, 0.15 for the dry rails; lower values make the wheels slip", &TTrack::fFriction, 3, 0.f, false);
|
||||||
@@ -750,6 +754,62 @@ void row_tooltip(track_property const &Property, value_counts const &Values, std
|
|||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the values of the properties of the track, as they're copied and kept by the presets
|
||||||
|
std::vector<std::pair<std::string, std::string>> values_of_track(TTrack const &Track)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> values;
|
||||||
|
for (auto const &property : properties())
|
||||||
|
if (property.applies(Track))
|
||||||
|
values.emplace_back(property.key, property.get(Track));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the groups some of the values are of
|
||||||
|
std::array<bool, group_count> groups_in(std::vector<std::pair<std::string, std::string>> const &Values)
|
||||||
|
{
|
||||||
|
std::array<bool, group_count> groups{};
|
||||||
|
for (auto const &[key, value] : Values)
|
||||||
|
for (auto const &property : properties())
|
||||||
|
if (key == property.key)
|
||||||
|
groups[property.group] = true;
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the groups the preset overwrites, from the names it keeps them by
|
||||||
|
std::array<bool, group_count> groups_of(editor_track_preset const &Preset)
|
||||||
|
{
|
||||||
|
std::array<bool, group_count> groups{};
|
||||||
|
for (auto const &key : Preset.groups)
|
||||||
|
for (int group = 0; group < group_count; ++group)
|
||||||
|
if (key == kGroupKeys[group])
|
||||||
|
groups[group] = true;
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> group_keys(std::array<bool, group_count> const &Groups)
|
||||||
|
{
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
for (int group = 0; group < group_count; ++group)
|
||||||
|
if (Groups[group])
|
||||||
|
keys.emplace_back(kGroupKeys[group]);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
// what the preset puts on the tracks, for its tooltip
|
||||||
|
std::string preset_summary(editor_track_preset const &Preset)
|
||||||
|
{
|
||||||
|
auto const groups{groups_of(Preset)};
|
||||||
|
auto const available{groups_in(Preset.values)};
|
||||||
|
std::string overwrites;
|
||||||
|
for (int group = 0; group < group_count; ++group)
|
||||||
|
if (groups[group] && available[group])
|
||||||
|
overwrites += (overwrites.empty() ? "" : ", ") + std::string{STR_C(kGroupLabels[group])};
|
||||||
|
auto text{overwrites.empty() ? std::string{STR_C("Overwrites nothing: choose what it overwrites in the inspector")} : format(STR_C("Overwrites: %s"), overwrites.c_str())};
|
||||||
|
if (false == Preset.from.empty())
|
||||||
|
text += "\n" + format(STR_C("Taken from %s"), Preset.from.c_str());
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::vector<TTrack *> editor_mode::selected_tracks()
|
std::vector<TTrack *> editor_mode::selected_tracks()
|
||||||
@@ -1288,6 +1348,7 @@ void editor_mode::render_track_properties(std::vector<TTrack *> const &Tracks)
|
|||||||
if (any_in(section::look_sleepers, false) && ImGui::TreeNode(STR_C("Sleeper models")))
|
if (any_in(section::look_sleepers, false) && ImGui::TreeNode(STR_C("Sleeper models")))
|
||||||
{
|
{
|
||||||
rows(section::look_sleepers, false);
|
rows(section::look_sleepers, false);
|
||||||
|
render_track_presets(Tracks);
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1298,50 +1359,230 @@ void editor_mode::render_track_properties(std::vector<TTrack *> const &Tracks)
|
|||||||
void editor_mode::copy_track_style(TTrack const &Track)
|
void editor_mode::copy_track_style(TTrack const &Track)
|
||||||
{
|
{
|
||||||
auto &style{m_track_style};
|
auto &style{m_track_style};
|
||||||
style.values.clear();
|
|
||||||
style.from = name_of(Track);
|
style.from = name_of(Track);
|
||||||
for (auto const &property : properties())
|
style.values = values_of_track(Track);
|
||||||
if (property.applies(Track))
|
|
||||||
style.values.emplace_back(property.key, property.get(Track));
|
|
||||||
ui()->set_status(format(STR_C("Properties of %s copied: Ctrl+Shift+V pastes them on the selected tracks"), style.from.c_str()), false);
|
ui()->set_status(format(STR_C("Properties of %s copied: Ctrl+Shift+V pastes them on the selected tracks"), style.from.c_str()), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t editor_mode::paste_track_style(std::vector<TTrack *> const &Tracks)
|
std::size_t editor_mode::paste_track_style(std::vector<TTrack *> const &Tracks)
|
||||||
{
|
{
|
||||||
static_assert(group_count == std::tuple_size_v<decltype(track_style::groups)>, "a choice for each group of the pasted properties");
|
|
||||||
auto const &style{m_track_style};
|
auto const &style{m_track_style};
|
||||||
if (style.values.empty() || Tracks.empty())
|
if (style.values.empty() || Tracks.empty())
|
||||||
return 0;
|
return 0;
|
||||||
|
auto const changed{apply_track_values(style.values, style.groups, Tracks, "Pasted properties")};
|
||||||
|
if (false == changed.has_value())
|
||||||
|
{
|
||||||
|
ui()->set_status(STR_C("Nothing chosen to paste"), true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (Tracks.size() == 1)
|
||||||
|
ui()->set_status(*changed > 0 ? format(STR_C("Properties of %s pasted, Ctrl+Z takes them back"), style.from.c_str()) : std::string{STR_C("The track has these properties already")}, false);
|
||||||
|
return *changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::size_t> editor_mode::apply_track_values(std::vector<std::pair<std::string, std::string>> const &Values, track_groups const &Groups, std::vector<TTrack *> const &Tracks, char const *What)
|
||||||
|
{
|
||||||
|
static_assert(group_count == std::tuple_size_v<track_groups>, "a choice for each group of the pasted properties");
|
||||||
auto const &list{properties()};
|
auto const &list{properties()};
|
||||||
std::vector<std::pair<track_property const *, std::string>> pasted;
|
std::vector<std::pair<track_property const *, std::string>> pasted;
|
||||||
bool rebuild{false};
|
bool rebuild{false};
|
||||||
for (auto const late : {false, true})
|
for (auto const late : {false, true})
|
||||||
for (auto const &[key, value] : style.values)
|
for (auto const &[key, value] : Values)
|
||||||
{
|
{
|
||||||
auto const index{track_property_index(key)};
|
auto const index{track_property_index(key)};
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
continue;
|
continue;
|
||||||
auto const &property{list[index]};
|
auto const &property{list[index]};
|
||||||
if (property.late != late || false == style.groups[property.group])
|
if (property.late != late || false == Groups[property.group])
|
||||||
continue;
|
continue;
|
||||||
pasted.emplace_back(&property, value);
|
pasted.emplace_back(&property, value);
|
||||||
rebuild |= property.rebuild;
|
rebuild |= property.rebuild;
|
||||||
}
|
}
|
||||||
if (pasted.empty())
|
if (pasted.empty())
|
||||||
{
|
return std::nullopt;
|
||||||
ui()->set_status(STR_C("Nothing chosen to paste"), true);
|
return tracks_apply(Tracks, [&](TTrack &Track) {
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
auto const changed{tracks_apply(Tracks, [&](TTrack &Track) {
|
|
||||||
bool any{false};
|
bool any{false};
|
||||||
for (auto const &[property, value] : pasted)
|
for (auto const &[property, value] : pasted)
|
||||||
if (property->applies(Track))
|
if (property->applies(Track))
|
||||||
any |= property->set(Track, value);
|
any |= property->set(Track, value);
|
||||||
return any;
|
return any;
|
||||||
}, rebuild, "Pasted properties")};
|
}, rebuild, What);
|
||||||
if (Tracks.size() == 1)
|
}
|
||||||
ui()->set_status(changed > 0 ? format(STR_C("Properties of %s pasted, Ctrl+Z takes them back"), style.from.c_str()) : std::string{STR_C("The track has these properties already")}, false);
|
|
||||||
return changed;
|
std::size_t editor_mode::apply_track_preset(int const Id, std::vector<TTrack *> const &Tracks)
|
||||||
|
{
|
||||||
|
auto const *preset{EditorTrackPresets.find(Id)};
|
||||||
|
if (preset == nullptr || Tracks.empty())
|
||||||
|
return 0;
|
||||||
|
auto const name{preset->name};
|
||||||
|
auto const changed{apply_track_values(preset->values, groups_of(*preset), Tracks, "Applied a track preset")};
|
||||||
|
if (false == changed.has_value())
|
||||||
|
{
|
||||||
|
ui()->set_status(format(STR_C("The preset \"%s\" overwrites nothing: choose what it overwrites"), name.c_str()), true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*changed > 0)
|
||||||
|
ui()->set_status(format(STR_P("Preset \"%s\" applied on %d track, Ctrl+Z takes it back", "Preset \"%s\" applied on %d tracks, Ctrl+Z takes it back", static_cast<long long>(*changed)), name.c_str(), static_cast<int>(*changed)), false);
|
||||||
|
else
|
||||||
|
ui()->set_status(STR_C("The tracks have the properties of the preset already"), false);
|
||||||
|
return *changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_mode::render_track_presets(std::vector<TTrack *> const &Tracks)
|
||||||
|
{
|
||||||
|
if (Tracks.empty() || false == ImGui::TreeNode(STR_C("Presets")))
|
||||||
|
return;
|
||||||
|
auto &panel{m_track_presets};
|
||||||
|
auto const &presets{EditorTrackPresets.presets()};
|
||||||
|
if (EditorTrackPresets.find(panel.selected) == nullptr)
|
||||||
|
panel.selected = presets.empty() ? 0 : presets.front().id;
|
||||||
|
auto const single{Tracks.size() == 1};
|
||||||
|
auto const *selected{EditorTrackPresets.find(panel.selected)};
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
|
if (ImGui::BeginCombo("##trackpreset", selected != nullptr ? selected->name.c_str() : STR_C("No presets yet")))
|
||||||
|
{
|
||||||
|
for (auto const &preset : presets)
|
||||||
|
{
|
||||||
|
if (ImGui::Selectable(preset.name.c_str(), preset.id == panel.selected))
|
||||||
|
panel.selected = preset.id;
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("%s", preset_summary(preset).c_str());
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
if (selected != nullptr && ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("%s", preset_summary(*selected).c_str());
|
||||||
|
|
||||||
|
auto const width{button_width(2)};
|
||||||
|
ImGui::BeginDisabled(selected == nullptr);
|
||||||
|
if (ImGui::Button(format(STR_P("Apply on %d track", "Apply on %d tracks", static_cast<long long>(Tracks.size())), static_cast<int>(Tracks.size())).c_str(), ImVec2(width, 0.0f)))
|
||||||
|
apply_track_preset(panel.selected, Tracks);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
item_tooltip("Puts the values of the preset on the selected tracks, of what it overwrites; Ctrl+Z takes them back.\nTools in the menu applies the presets too");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::BeginDisabled(false == single);
|
||||||
|
if (ImGui::Button(STR_C("New from the track"), ImVec2(width, 0.0f)))
|
||||||
|
{
|
||||||
|
// rails, trackbed, its shape, sleepers, frogs and sleeper placement at first, the look of the track
|
||||||
|
std::array<bool, group_count> groups{};
|
||||||
|
for (auto const group : {group::rails, group::trackbed, group::trackbed_shape, group::sleepers, group::frogs, group::sleeper_placement})
|
||||||
|
groups[group] = true;
|
||||||
|
panel.selected = EditorTrackPresets.create(STR_C("New preset"), name_of(*Tracks.front()), values_of_track(*Tracks.front()), group_keys(groups));
|
||||||
|
panel.nameid = 0;
|
||||||
|
ui()->set_status(format(STR_C("Properties of %s saved as a preset: give it a name and choose what it overwrites"), name_of(*Tracks.front()).c_str()), false);
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||||
|
ImGui::SetTooltip("%s", STR_C(single ? "Saves the properties of this track as a new preset" : "Select one track to save its properties as a preset"));
|
||||||
|
|
||||||
|
// pointers to the presets don't outlive a change of their list
|
||||||
|
selected = EditorTrackPresets.find(panel.selected);
|
||||||
|
if (selected == nullptr)
|
||||||
|
{
|
||||||
|
ImGui::PushTextWrapPos(0.0f);
|
||||||
|
ImGui::TextDisabled("%s", STR_C("A preset keeps the properties of a track: its rails, trackbed, sleepers... to put them on other tracks,\nwith the choice of what it overwrites"));
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::TreePop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto const id{selected->id};
|
||||||
|
|
||||||
|
// the name
|
||||||
|
if (panel.nameid != id)
|
||||||
|
{
|
||||||
|
std::snprintf(panel.name, sizeof(panel.name), "%s", selected->name.c_str());
|
||||||
|
panel.nameid = id;
|
||||||
|
}
|
||||||
|
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(STR_C("Rename")).x - ImGui::GetStyle().FramePadding.x * 2 - ImGui::GetStyle().ItemSpacing.x);
|
||||||
|
auto const entered{ImGui::InputText("##presetname", panel.name, IM_ARRAYSIZE(panel.name), ImGuiInputTextFlags_EnterReturnsTrue)};
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(STR_C("Rename")) || entered)
|
||||||
|
{
|
||||||
|
EditorTrackPresets.rename(id, panel.name);
|
||||||
|
panel.nameid = 0; // reload, the name could have been adjusted to stay unique
|
||||||
|
selected = EditorTrackPresets.find(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// what it overwrites, of the groups it has values of
|
||||||
|
ImGui::TextDisabled("%s", STR_C("Overwrites:"));
|
||||||
|
// its own id scope: the group checkboxes share their labels with the sections of the properties
|
||||||
|
ImGui::PushID("presetgroups");
|
||||||
|
auto const available{groups_in(selected->values)};
|
||||||
|
auto groups{groups_of(*selected)};
|
||||||
|
auto changed{false};
|
||||||
|
int column{0};
|
||||||
|
auto const half{ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x * 0.5f};
|
||||||
|
for (int group = 0; group < group_count; ++group)
|
||||||
|
{
|
||||||
|
if (false == available[group])
|
||||||
|
continue;
|
||||||
|
if (column % 2 == 1)
|
||||||
|
ImGui::SameLine(half);
|
||||||
|
changed |= ImGui::Checkbox(STR_C(kGroupLabels[group]), &groups[group]);
|
||||||
|
++column;
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
// the groups it has no values of are kept as they were, for a track they're taken again from
|
||||||
|
auto kept{groups_of(*selected)};
|
||||||
|
for (int group = 0; group < group_count; ++group)
|
||||||
|
if (available[group])
|
||||||
|
kept[group] = groups[group];
|
||||||
|
EditorTrackPresets.set_groups(id, group_keys(kept));
|
||||||
|
selected = EditorTrackPresets.find(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(false == single);
|
||||||
|
if (ImGui::Button(STR_C("Update from the track"), ImVec2(width, 0.0f)))
|
||||||
|
{
|
||||||
|
EditorTrackPresets.update(id, name_of(*Tracks.front()), values_of_track(*Tracks.front()));
|
||||||
|
ui()->set_status(format(STR_C("Preset \"%s\" takes the properties of %s now"), selected->name.c_str(), name_of(*Tracks.front()).c_str()), false);
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||||
|
ImGui::SetTooltip("%s", STR_C(single ? "The preset takes the properties of this track in place of the ones it had; what it overwrites stays" : "Select one track to take its properties into the preset"));
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(STR_C("Delete"), ImVec2(width, 0.0f)))
|
||||||
|
ImGui::OpenPopup("##deletepreset");
|
||||||
|
if (ImGui::BeginPopup("##deletepreset"))
|
||||||
|
{
|
||||||
|
ImGui::Text(STR_C("Delete preset \"%s\"?"), selected->name.c_str());
|
||||||
|
if (ImGui::Button(STR_C("Delete##confirm")))
|
||||||
|
{
|
||||||
|
EditorTrackPresets.remove(id);
|
||||||
|
panel.selected = 0;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(STR_C("Cancel")))
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_mode::render_track_presets_menu()
|
||||||
|
{
|
||||||
|
if (false == ImGui::BeginMenu(STR_C("Track presets")))
|
||||||
|
return;
|
||||||
|
auto const &presets{EditorTrackPresets.presets()};
|
||||||
|
if (presets.empty())
|
||||||
|
ImGui::TextDisabled("%s", STR_C("No presets: save one in the inspector of a track, Look, Sleeper models, Presets"));
|
||||||
|
auto const tracks{selected_tracks()};
|
||||||
|
int chosen{0};
|
||||||
|
for (auto const &preset : presets)
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem(preset.name.c_str(), nullptr, false, false == tracks.empty()))
|
||||||
|
chosen = preset.id;
|
||||||
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||||
|
ImGui::SetTooltip("%s", tracks.empty() ? STR_C("Select the tracks to apply the preset on first") : (format(STR_P("Applies the preset on %d selected track", "Applies the preset on %d selected tracks", static_cast<long long>(tracks.size())), static_cast<int>(tracks.size())) + "\n" + preset_summary(preset)).c_str());
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
if (chosen != 0)
|
||||||
|
apply_track_preset(chosen, tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_mode::render_track_style(std::vector<TTrack *> const &Tracks)
|
void editor_mode::render_track_style(std::vector<TTrack *> const &Tracks)
|
||||||
@@ -1614,7 +1855,7 @@ editor_mode::selftest_result editor_mode::selftest_props(std::string const &Comm
|
|||||||
}
|
}
|
||||||
else if (action == "groups")
|
else if (action == "groups")
|
||||||
{
|
{
|
||||||
// the groups pasted, as their numbers: 0 speed ... 9 frogs
|
// the groups pasted, as their numbers: 0 speed ... 9 frogs, 10 sleeper placement
|
||||||
m_track_style.groups.fill(false);
|
m_track_style.groups.fill(false);
|
||||||
for (int group; Words >> group;)
|
for (int group; Words >> group;)
|
||||||
if (group >= 0 && group < group_count)
|
if (group >= 0 && group < group_count)
|
||||||
|
|||||||
@@ -989,6 +989,15 @@ void editor_ui::render_menu_contents()
|
|||||||
m_menuoptions();
|
m_menuoptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the tools of the editor mode, in the tools menu after the common ones (logging, screenshot)
|
||||||
|
void editor_ui::render_tools_menu()
|
||||||
|
{
|
||||||
|
if (!m_toolsmenu)
|
||||||
|
return;
|
||||||
|
ImGui::Separator();
|
||||||
|
m_toolsmenu();
|
||||||
|
}
|
||||||
|
|
||||||
// the windows of the editor and their layout, in the windows menu after the common ones (log, hierarchy, history, settings)
|
// the windows of the editor and their layout, in the windows menu after the common ones (log, hierarchy, history, settings)
|
||||||
void editor_ui::render_windows_menu()
|
void editor_ui::render_windows_menu()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ class editor_ui : public ui_layer
|
|||||||
{
|
{
|
||||||
m_editmenu = std::move(Renderer);
|
m_editmenu = std::move(Renderer);
|
||||||
}
|
}
|
||||||
|
// draws the items of the editor mode in the tools menu, after the common ones
|
||||||
|
void set_tools_menu(std::function<void()> Renderer)
|
||||||
|
{
|
||||||
|
m_toolsmenu = std::move(Renderer);
|
||||||
|
}
|
||||||
// draws the fields of work of the editor mode in a row above the toolbar
|
// draws the fields of work of the editor mode in a row above the toolbar
|
||||||
void set_workspace_bar(std::function<void()> Renderer)
|
void set_workspace_bar(std::function<void()> Renderer)
|
||||||
{
|
{
|
||||||
@@ -170,6 +175,7 @@ class editor_ui : public ui_layer
|
|||||||
void scene_row(std::size_t const Entry);
|
void scene_row(std::size_t const Entry);
|
||||||
void render_menu_contents() override;
|
void render_menu_contents() override;
|
||||||
void render_windows_menu() override;
|
void render_windows_menu() override;
|
||||||
|
void render_tools_menu() override;
|
||||||
int palette() const override;
|
int palette() const override;
|
||||||
void render_dockspace() override;
|
void render_dockspace() override;
|
||||||
void render_() override;
|
void render_() override;
|
||||||
@@ -203,6 +209,7 @@ class editor_ui : public ui_layer
|
|||||||
std::function<void()> m_toolbaroptions;
|
std::function<void()> m_toolbaroptions;
|
||||||
std::function<void()> m_workspacebar;
|
std::function<void()> m_workspacebar;
|
||||||
std::function<void()> m_editmenu;
|
std::function<void()> m_editmenu;
|
||||||
|
std::function<void()> m_toolsmenu;
|
||||||
bool m_layoutreset{false}; // the default layout is to be built again on the next frame
|
bool m_layoutreset{false}; // the default layout is to be built again on the next frame
|
||||||
bool m_tooloptionsopen{true};
|
bool m_tooloptionsopen{true};
|
||||||
bool m_inspectoropen{true};
|
bool m_inspectoropen{true};
|
||||||
|
|||||||
@@ -696,6 +696,7 @@ void ui_layer::render_menu_contents()
|
|||||||
|
|
||||||
if (ImGui::MenuItem(STR_C("Screenshot"), "PrtScr"))
|
if (ImGui::MenuItem(STR_C("Screenshot"), "PrtScr"))
|
||||||
Application.queue_screenshot();
|
Application.queue_screenshot();
|
||||||
|
render_tools_menu();
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginMenu(STR_C("Windows")))
|
if (ImGui::BeginMenu(STR_C("Windows")))
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ protected:
|
|||||||
virtual void render_menu_contents();
|
virtual void render_menu_contents();
|
||||||
// items of the windows menu which belong to the ui of a mode, after the common ones
|
// items of the windows menu which belong to the ui of a mode, after the common ones
|
||||||
virtual void render_windows_menu() {}
|
virtual void render_windows_menu() {}
|
||||||
|
// items of the tools menu which belong to the ui of a mode, after the common ones
|
||||||
|
virtual void render_tools_menu() {}
|
||||||
// colours this ui is drawn in, see imgui_colors()
|
// colours this ui is drawn in, see imgui_colors()
|
||||||
virtual int palette() const
|
virtual int palette() const
|
||||||
{
|
{
|
||||||
|
|||||||
233
editor/editorTrackPresets.cpp
Normal file
233
editor/editorTrackPresets.cpp
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "editor/editorTrackPresets.hpp"
|
||||||
|
#include "utilities/Logs.h"
|
||||||
|
#include "utilities/utilities.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
editorTrackPresets EditorTrackPresets;
|
||||||
|
|
||||||
|
// file layout:
|
||||||
|
// preset <name>
|
||||||
|
// from <track name>
|
||||||
|
// groups <group key> <group key>...
|
||||||
|
// value <property key> <value, the rest of the line>
|
||||||
|
// ...
|
||||||
|
// endpreset
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
fs::path presets_path()
|
||||||
|
{
|
||||||
|
fs::path p = user_config_path("eu07_editor_track_presets.txt");
|
||||||
|
return p.empty() ? fs::path("eu07_editor_track_presets.txt") : p;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trimmed(std::string Text)
|
||||||
|
{
|
||||||
|
auto const first = Text.find_first_not_of(" \t");
|
||||||
|
if (first == std::string::npos)
|
||||||
|
return {};
|
||||||
|
auto const last = Text.find_last_not_of(" \t");
|
||||||
|
return Text.substr(first, last - first + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// one value per line in the file
|
||||||
|
std::string single_line(std::string Text)
|
||||||
|
{
|
||||||
|
std::replace_if(Text.begin(), Text.end(), [](char const Character) { return Character == '\r' || Character == '\n' || Character == '\t'; }, ' ');
|
||||||
|
return trimmed(Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the line past its first word, when it starts with the word
|
||||||
|
bool keyword(std::string const &Line, char const *Word, std::string &Rest)
|
||||||
|
{
|
||||||
|
auto const length{std::char_traits<char>::length(Word)};
|
||||||
|
if (Line.compare(0, length, Word) != 0 || (Line.size() > length && Line[length] != ' '))
|
||||||
|
return false;
|
||||||
|
Rest = Line.size() > length ? trimmed(Line.substr(length + 1)) : std::string{};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool editorTrackPresets::load()
|
||||||
|
{
|
||||||
|
m_presets.clear();
|
||||||
|
|
||||||
|
const fs::path path = presets_path();
|
||||||
|
std::error_code ec;
|
||||||
|
if (!fs::exists(path, ec))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::ifstream stream(path, std::ios_base::in | std::ios_base::binary);
|
||||||
|
if (!stream.is_open())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
editor_track_preset *current{nullptr};
|
||||||
|
std::string line;
|
||||||
|
std::string rest;
|
||||||
|
while (std::getline(stream, line))
|
||||||
|
{
|
||||||
|
line = single_line(line);
|
||||||
|
if (line.empty() || line.rfind("//", 0) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// a preset header also closes a preset with missing "endpreset"
|
||||||
|
if (keyword(line, "preset", rest))
|
||||||
|
{
|
||||||
|
editor_track_preset preset;
|
||||||
|
preset.id = m_nextid++;
|
||||||
|
preset.name = unique_name(rest);
|
||||||
|
m_presets.push_back(std::move(preset));
|
||||||
|
current = &m_presets.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line == "endpreset")
|
||||||
|
{
|
||||||
|
current = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current == nullptr)
|
||||||
|
continue;
|
||||||
|
if (keyword(line, "from", rest))
|
||||||
|
current->from = rest;
|
||||||
|
else if (keyword(line, "groups", rest))
|
||||||
|
{
|
||||||
|
std::istringstream words{rest};
|
||||||
|
current->groups.clear();
|
||||||
|
for (std::string group; words >> group;)
|
||||||
|
current->groups.push_back(group);
|
||||||
|
}
|
||||||
|
else if (keyword(line, "value", rest) && false == rest.empty())
|
||||||
|
{
|
||||||
|
auto const split{rest.find(' ')};
|
||||||
|
if (split == std::string::npos)
|
||||||
|
current->values.emplace_back(rest, std::string{});
|
||||||
|
else
|
||||||
|
current->values.emplace_back(rest.substr(0, split), trimmed(rest.substr(split + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool editorTrackPresets::save() const
|
||||||
|
{
|
||||||
|
const fs::path path = presets_path();
|
||||||
|
std::error_code ec;
|
||||||
|
fs::create_directories(path.parent_path(), ec);
|
||||||
|
|
||||||
|
std::ofstream stream(path, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
|
||||||
|
if (!stream.is_open())
|
||||||
|
{
|
||||||
|
ErrorLog("failed to save editor track presets");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << "// MaSzyna editor: user-defined presets of the track properties\n";
|
||||||
|
for (auto const &preset : m_presets)
|
||||||
|
{
|
||||||
|
stream << "\npreset " << preset.name << "\n";
|
||||||
|
if (false == preset.from.empty())
|
||||||
|
stream << "from " << preset.from << "\n";
|
||||||
|
stream << "groups";
|
||||||
|
for (auto const &group : preset.groups)
|
||||||
|
stream << ' ' << group;
|
||||||
|
stream << "\n";
|
||||||
|
for (auto const &[key, value] : preset.values)
|
||||||
|
stream << "value " << key << ' ' << single_line(value) << "\n";
|
||||||
|
stream << "endpreset\n";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor_track_preset const *editorTrackPresets::find(int const Id) const
|
||||||
|
{
|
||||||
|
auto const lookup = std::find_if(m_presets.begin(), m_presets.end(), [Id](auto const &Preset) { return Preset.id == Id; });
|
||||||
|
return lookup != m_presets.end() ? &(*lookup) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor_track_preset *editorTrackPresets::find_(int const Id)
|
||||||
|
{
|
||||||
|
auto const lookup = std::find_if(m_presets.begin(), m_presets.end(), [Id](auto const &Preset) { return Preset.id == Id; });
|
||||||
|
return lookup != m_presets.end() ? &(*lookup) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int editorTrackPresets::create(std::string const &Name, std::string const &From, std::vector<std::pair<std::string, std::string>> const &Values, std::vector<std::string> const &Groups)
|
||||||
|
{
|
||||||
|
editor_track_preset preset;
|
||||||
|
preset.id = m_nextid++;
|
||||||
|
preset.name = unique_name(Name);
|
||||||
|
preset.from = single_line(From);
|
||||||
|
preset.values = Values;
|
||||||
|
preset.groups = Groups;
|
||||||
|
m_presets.push_back(std::move(preset));
|
||||||
|
save();
|
||||||
|
return m_presets.back().id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorTrackPresets::remove(int const Id)
|
||||||
|
{
|
||||||
|
auto const lookup = std::find_if(m_presets.begin(), m_presets.end(), [Id](auto const &Preset) { return Preset.id == Id; });
|
||||||
|
if (lookup == m_presets.end())
|
||||||
|
return;
|
||||||
|
m_presets.erase(lookup);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorTrackPresets::rename(int const Id, std::string const &Name)
|
||||||
|
{
|
||||||
|
auto *preset = find_(Id);
|
||||||
|
if (preset == nullptr)
|
||||||
|
return;
|
||||||
|
auto const name = unique_name(Name, Id);
|
||||||
|
if (name == preset->name)
|
||||||
|
return;
|
||||||
|
preset->name = name;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorTrackPresets::update(int const Id, std::string const &From, std::vector<std::pair<std::string, std::string>> const &Values)
|
||||||
|
{
|
||||||
|
auto *preset = find_(Id);
|
||||||
|
if (preset == nullptr)
|
||||||
|
return;
|
||||||
|
preset->from = single_line(From);
|
||||||
|
preset->values = Values;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorTrackPresets::set_groups(int const Id, std::vector<std::string> const &Groups)
|
||||||
|
{
|
||||||
|
auto *preset = find_(Id);
|
||||||
|
if (preset == nullptr || preset->groups == Groups)
|
||||||
|
return;
|
||||||
|
preset->groups = Groups;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string editorTrackPresets::unique_name(std::string const &Base, int const IgnoreId) const
|
||||||
|
{
|
||||||
|
auto base = single_line(Base);
|
||||||
|
if (base.empty())
|
||||||
|
base = "New preset";
|
||||||
|
auto const taken = [&](std::string const &Name) {
|
||||||
|
return std::any_of(m_presets.begin(), m_presets.end(), [&](auto const &Preset) { return Preset.id != IgnoreId && Preset.name == Name; });
|
||||||
|
};
|
||||||
|
auto name = base;
|
||||||
|
for (int suffix = 2; taken(name); ++suffix)
|
||||||
|
name = base + " " + std::to_string(suffix);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
58
editor/editorTrackPresets.hpp
Normal file
58
editor/editorTrackPresets.hpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
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 <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// user-defined presets of the properties of tracks (rails, trackbed, sleepers...), taken from a track and put on others
|
||||||
|
// with the choice of what they overwrite. stored next to the user's ini files, like the model sets
|
||||||
|
struct editor_track_preset
|
||||||
|
{
|
||||||
|
int id{0}; // runtime identifier, stable while the editor runs (renames don't break references)
|
||||||
|
std::string name;
|
||||||
|
std::string from; // name of the track the values were taken from
|
||||||
|
// the values of the properties as the track inspector reads and writes them, by their keys
|
||||||
|
std::vector<std::pair<std::string, std::string>> values;
|
||||||
|
// the groups of the properties the preset overwrites, by their keys; the other values of it are kept but left out
|
||||||
|
std::vector<std::string> groups;
|
||||||
|
};
|
||||||
|
|
||||||
|
class editorTrackPresets
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool load();
|
||||||
|
bool save() const;
|
||||||
|
|
||||||
|
std::vector<editor_track_preset> const &presets() const
|
||||||
|
{
|
||||||
|
return m_presets;
|
||||||
|
}
|
||||||
|
editor_track_preset const *find(int Id) const;
|
||||||
|
// all modifying methods save the presets right away
|
||||||
|
// returns: id of the new preset
|
||||||
|
int create(std::string const &Name, std::string const &From, std::vector<std::pair<std::string, std::string>> const &Values, std::vector<std::string> const &Groups);
|
||||||
|
void remove(int Id);
|
||||||
|
void rename(int Id, std::string const &Name);
|
||||||
|
// the values taken again, from another track or the same one changed
|
||||||
|
void update(int Id, std::string const &From, std::vector<std::pair<std::string, std::string>> const &Values);
|
||||||
|
void set_groups(int Id, std::vector<std::string> const &Groups);
|
||||||
|
// name not used by any other preset, built from specified base
|
||||||
|
std::string unique_name(std::string const &Base, int IgnoreId = 0) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
editor_track_preset *find_(int Id);
|
||||||
|
|
||||||
|
std::vector<editor_track_preset> m_presets;
|
||||||
|
int m_nextid{1};
|
||||||
|
};
|
||||||
|
|
||||||
|
extern editorTrackPresets EditorTrackPresets;
|
||||||
@@ -10166,3 +10166,95 @@ msgstr "zmieniony, niezapisany"
|
|||||||
|
|
||||||
msgid "new, not saved"
|
msgid "new, not saved"
|
||||||
msgstr "nowy, niezapisany"
|
msgstr "nowy, niezapisany"
|
||||||
|
|
||||||
|
# scenery editor: presets of the track properties
|
||||||
|
|
||||||
|
msgid "Sleeper placement"
|
||||||
|
msgstr "Rozmieszczenie podkładów"
|
||||||
|
|
||||||
|
msgid "Overwrites nothing: choose what it overwrites in the inspector"
|
||||||
|
msgstr "Niczego nie nadpisuje: wybierz w inspektorze, co ma nadpisywać"
|
||||||
|
|
||||||
|
msgid "Overwrites: %s"
|
||||||
|
msgstr "Nadpisuje: %s"
|
||||||
|
|
||||||
|
msgid "Taken from %s"
|
||||||
|
msgstr "Wzięty z %s"
|
||||||
|
|
||||||
|
msgid "The preset \"%s\" overwrites nothing: choose what it overwrites"
|
||||||
|
msgstr "Zestaw \"%s\" niczego nie nadpisuje: wybierz, co ma nadpisywać"
|
||||||
|
|
||||||
|
msgid "Preset \"%s\" applied on %d track, Ctrl+Z takes it back"
|
||||||
|
msgid_plural "Preset \"%s\" applied on %d tracks, Ctrl+Z takes it back"
|
||||||
|
msgstr[0] "Zestaw \"%s\" zastosowany na %d torze, Ctrl+Z go cofa"
|
||||||
|
msgstr[1] "Zestaw \"%s\" zastosowany na %d torach, Ctrl+Z go cofa"
|
||||||
|
msgstr[2] "Zestaw \"%s\" zastosowany na %d torach, Ctrl+Z go cofa"
|
||||||
|
|
||||||
|
msgid "The tracks have the properties of the preset already"
|
||||||
|
msgstr "Tory mają już właściwości tego zestawu"
|
||||||
|
|
||||||
|
msgid "Presets"
|
||||||
|
msgstr "Zestawy"
|
||||||
|
|
||||||
|
msgid "No presets yet"
|
||||||
|
msgstr "Brak zestawów"
|
||||||
|
|
||||||
|
msgid "Apply on %d track"
|
||||||
|
msgid_plural "Apply on %d tracks"
|
||||||
|
msgstr[0] "Zastosuj na %d torze"
|
||||||
|
msgstr[1] "Zastosuj na %d torach"
|
||||||
|
msgstr[2] "Zastosuj na %d torach"
|
||||||
|
|
||||||
|
msgid "Puts the values of the preset on the selected tracks, of what it overwrites; Ctrl+Z takes them back.\nTools in the menu applies the presets too"
|
||||||
|
msgstr "Nadaje zaznaczonym torom wartości zestawu, z tego, co nadpisuje; Ctrl+Z je cofa.\nZestawy można też zastosować z menu Narzędzia"
|
||||||
|
|
||||||
|
msgid "New from the track"
|
||||||
|
msgstr "Nowy z toru"
|
||||||
|
|
||||||
|
msgid "New preset"
|
||||||
|
msgstr "Nowy zestaw"
|
||||||
|
|
||||||
|
msgid "Properties of %s saved as a preset: give it a name and choose what it overwrites"
|
||||||
|
msgstr "Właściwości %s zapisane jako zestaw: nadaj mu nazwę i wybierz, co ma nadpisywać"
|
||||||
|
|
||||||
|
msgid "Saves the properties of this track as a new preset"
|
||||||
|
msgstr "Zapisuje właściwości tego toru jako nowy zestaw"
|
||||||
|
|
||||||
|
msgid "Select one track to save its properties as a preset"
|
||||||
|
msgstr "Zaznacz jeden tor, żeby zapisać jego właściwości jako zestaw"
|
||||||
|
|
||||||
|
msgid "A preset keeps the properties of a track: its rails, trackbed, sleepers... to put them on other tracks,\nwith the choice of what it overwrites"
|
||||||
|
msgstr "Zestaw przechowuje właściwości toru: szyny, podsypkę, podkłady... żeby nadać je innym torom,\nz wyborem tego, co nadpisuje"
|
||||||
|
|
||||||
|
msgid "Overwrites:"
|
||||||
|
msgstr "Nadpisuje:"
|
||||||
|
|
||||||
|
msgid "Update from the track"
|
||||||
|
msgstr "Aktualizuj z toru"
|
||||||
|
|
||||||
|
msgid "Preset \"%s\" takes the properties of %s now"
|
||||||
|
msgstr "Zestaw \"%s\" ma teraz właściwości %s"
|
||||||
|
|
||||||
|
msgid "The preset takes the properties of this track in place of the ones it had; what it overwrites stays"
|
||||||
|
msgstr "Zestaw przejmuje właściwości tego toru w miejsce dotychczasowych; wybór tego, co nadpisuje, zostaje"
|
||||||
|
|
||||||
|
msgid "Select one track to take its properties into the preset"
|
||||||
|
msgstr "Zaznacz jeden tor, żeby przenieść jego właściwości do zestawu"
|
||||||
|
|
||||||
|
msgid "Delete preset \"%s\"?"
|
||||||
|
msgstr "Usunąć zestaw \"%s\"?"
|
||||||
|
|
||||||
|
msgid "Track presets"
|
||||||
|
msgstr "Zestawy torów"
|
||||||
|
|
||||||
|
msgid "No presets: save one in the inspector of a track, Look, Sleeper models, Presets"
|
||||||
|
msgstr "Brak zestawów: zapisz zestaw w inspektorze toru, Wygląd, Modele podkładów, Zestawy"
|
||||||
|
|
||||||
|
msgid "Select the tracks to apply the preset on first"
|
||||||
|
msgstr "Najpierw zaznacz tory, na których zastosować zestaw"
|
||||||
|
|
||||||
|
msgid "Applies the preset on %d selected track"
|
||||||
|
msgid_plural "Applies the preset on %d selected tracks"
|
||||||
|
msgstr[0] "Stosuje zestaw na %d zaznaczonym torze"
|
||||||
|
msgstr[1] "Stosuje zestaw na %d zaznaczonych torach"
|
||||||
|
msgstr[2] "Stosuje zestaw na %d zaznaczonych torach"
|
||||||
|
|||||||
Reference in New Issue
Block a user