17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 03:27:30 +01:00

feat(editor): the Signals tool goes into the signalling one: its templates under Signals in the SRK panel, the ones the station's signals are placed with too; a click on a tile or Place by a track arms placing, Esc or a click on the armed tile stops it, a click on a track else opening its station; a signal picked in 3D or in Signal heads shows its card at the top of the panel, its template, to the other side, turn around; H no longer a tool

This commit is contained in:
maj00r
2026-10-23 12:26:07 +02:00
parent 2d9be3ae10
commit eab3dca700
12 changed files with 168 additions and 119 deletions

View File

@@ -1502,7 +1502,7 @@ bool editor_mode::update()
draw_switch_slide();
if (m_track_tab == track_tab::turntable)
draw_turntable_overlay();
if (m_track_tab == track_tab::signals)
if (m_track_tab == track_tab::srk)
{
signal_refresh();
signal_move_update();
@@ -5232,7 +5232,7 @@ void editor_mode::render_first_steps()
trial::turnout},
{"Signals and posts",
nullptr,
{{"H", "The signals tool: the type of the signal in the panel, from the templates of the scenery"},
{{"I", "The signalling tool: click a type of signal in its Signals, from the templates of the scenery"},
{"LMB", "A click by a track places the signal on that side"},
{"LMB + move", "Press by the track and drag along it: the trains running that way see the signal"},
{"LMB + move", "Drag a standing signal: it moves along its track"},
@@ -5382,11 +5382,11 @@ void editor_mode::render_first_steps()
}
case trial::signals:
{
ImGui::BeginDisabled(ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::signals);
if (ImGui::Button(STR_C("The signals tool (H)")))
ImGui::BeginDisabled(ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::srk);
if (ImGui::Button(STR_C("The signalling tool (I)")))
{
choose_work_area(work_area::tracks);
show_track_tab(track_tab::signals);
show_track_tab(track_tab::srk);
}
ImGui::EndDisabled();
break;
@@ -5657,7 +5657,7 @@ void editor_mode::render_gizmo_options()
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("Off: the top view draws the rails and sleepers of the tracks without the ballast, so the ground under them shows"));
}
if (ui()->mode() == nodebank_panel::TRACK && m_track_tab != track_tab::path && m_track_tab != track_tab::turnout && m_track_tab != track_tab::turntable && m_track_tab != track_tab::signals &&
if (ui()->mode() == nodebank_panel::TRACK && m_track_tab != track_tab::path && m_track_tab != track_tab::turnout && m_track_tab != track_tab::turntable && m_track_tab != track_tab::srk &&
m_track_tab != track_tab::lineside)
return;
int op{m_gizmo_enabled ? static_cast<int>(m_gizmo_op) : -1};
@@ -5911,7 +5911,7 @@ void editor_mode::render_include_gizmo(glm::mat4 const &View, glm::mat4 const &P
// in the signal tool a press on the mast drags the signal along its track: the gizmo, whose up axis runs along the
// mast, gives way over a signal and while one is dragged
auto const yield{ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::signals && false == ImGuizmo::IsUsing() && (m_signal.moving || m_signal.placing || signal_hit() >= 0)};
auto const yield{ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::srk && false == ImGuizmo::IsUsing() && (m_signal.moving || m_signal.placing || signal_hit() >= 0)};
ImGuizmo::Enable(false == yield);
ImGuizmo::Manipulate(glm::value_ptr(View), glm::value_ptr(Projection), static_cast<ImGuizmo::OPERATION>(operation), mode, glm::value_ptr(matrix), nullptr, snap);
ImGuizmo::Enable(true);
@@ -6505,6 +6505,8 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in
cancel_track_tools();
m_straights.tool = 0;
}
else if (m_track_tab == track_tab::srk && m_signal.armed)
m_signal.armed = false;
else if (m_track_tab == track_tab::path && m_track_set.size() >= 2)
select_tracks({});
else if (m_track_tab != track_tab::path)

View File

@@ -1557,7 +1557,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri
editor_icons::icon picture;
int family; // 0 builds the track, 1 equips it, 2 checks it
};
static std::array<track_mode, 13> const &track_modes();
static std::array<track_mode, 12> const &track_modes();
bool track_plan_tab() const { return m_track_tab == track_tab::straights || m_track_tab == track_tab::route; }
track_tab plan_tab_for(TTrack const *Track) const;
// the objects along the track: a tab for each tool
@@ -1906,6 +1906,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri
std::size_t signal_batch(signal_changes const &Changes, std::vector<std::pair<TTrack *, editor_track::state>> States, std::vector<TTrack *> Created, std::string &Error) override;
bool signal_heads(std::vector<signal_head> &Heads, bool const Wait) override;
std::size_t signal_reattach(std::vector<scene::instance_handle> const &Instances) override;
void select_signal(scene::instance_handle const Instance) override;
void render_signal_templates() override;
struct signal_template
{
@@ -1950,6 +1952,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri
int revision{-1};
int kind{0};
std::array<int, 7> chosen{-1, -1, -1, -1, -1, -1, -1};
bool armed{false}; // a click by a track places the chosen template
char filter[64]{};
char name[64]{"A"};
char plate[64]{};
@@ -1975,6 +1978,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri
};
signal_tool m_signal;
bool signal_scan(bool Wait = true);
signal_template const *signal_current() const;
signal_template const *signal_armed() const;
signal_template const *signal_template_of(std::string const &File) const;
bool signal_spot_at(TTrack &Track, int const Path, glm::dvec3 const &Point, glm::dvec3 const &Cursor, int const Facing, int const Side, signal_template const &Template, signal_spot &Spot) const;

View File

@@ -82,7 +82,7 @@ editor_mode::selftest_result editor_mode::selftest_monkey(std::string const &com
lines.push_back(std::string{"button "} + Button + " release");
lines.emplace_back("wait 2");
};
char const *const tools[]{"esc", "l", "g", "c", "t", "n", "u", "h", "i", "b", "p", "v", "j", "k", "o", "q", "w", "e"};
char const *const tools[]{"esc", "l", "g", "c", "t", "n", "u", "i", "b", "p", "v", "j", "k", "o", "q", "w", "e"};
char const *const modifiers[]{"", "ctrl", "shift", "alt"};
for (int step = 0; step < steps; ++step)
{

View File

@@ -53,7 +53,7 @@ editor_mode::selftest_result editor_mode::selftest_signal(std::string const &com
if (command == "signal" || command == "signalmove" || command == "signaldelete" || command == "signalrename" || command == "signalprobe" || command == "signalflip" || command == "signalturn" || command == "signalselect" || command == "signalswap")
{
auto values{selftest_values(words)};
show_track_tab(track_tab::signals);
show_track_tab(track_tab::srk);
signal_scan();
signal_refresh(true);
auto &tool{m_signal};
@@ -96,12 +96,12 @@ editor_mode::selftest_result editor_mode::selftest_signal(std::string const &com
auto const side{values["side"] == "left" ? -1 : 1};
auto const facing{values["facing"] == "along" ? 1 : values["facing"] == "against" ? -1 : 0};
signal_spot spot;
if (false == spot_on(*signal_armed(), facing, side, spot))
if (false == spot_on(*signal_current(), facing, side, spot))
{
WriteLog("SELFTEST signal: no such track " + values["track"]);
return selftest_result::handled;
}
auto const placed{signal_place(spot, *signal_armed())};
auto const placed{signal_place(spot, *signal_current())};
WriteLog(format("SELFTEST signal %s: %s", placed ? "placed" : "failed", tool.status.c_str()));
}
else if (command == "signalmove")

View File

@@ -350,7 +350,7 @@ bool editor_mode::signal_scan(bool const Wait)
return true;
}
editor_mode::signal_template const *editor_mode::signal_armed() const
editor_mode::signal_template const *editor_mode::signal_current() const
{
auto const &tool{m_signal};
if (tool.kind < 0 || tool.kind >= kKindCount)
@@ -359,6 +359,11 @@ editor_mode::signal_template const *editor_mode::signal_armed() const
return index >= 0 && index < static_cast<int>(tool.templates.size()) ? &tool.templates[index] : nullptr;
}
editor_mode::signal_template const *editor_mode::signal_armed() const
{
return m_signal.armed ? signal_current() : nullptr;
}
editor_mode::signal_template const *editor_mode::signal_template_of(std::string const &File) const
{
auto const file{ToLower(File)};
@@ -980,7 +985,7 @@ void editor_mode::orphans_take_over(std::vector<TTrack *> const &Created, std::v
for (int list = 0; list < 6; ++list)
for (auto const &event : editor_track::events(*track, list))
carried.insert(ToLower(event.first));
// not the list of the Signals tool, nor its templates once found: the step taken may hold one of them; looked for once an event
// not the signals standing as last listed, nor the templates once found: the step taken may hold one of them; looked for once an event
// waits
std::vector<signal_standing> signals;
std::unordered_map<std::string, signal_standing const *> readers;
@@ -1504,7 +1509,7 @@ void editor_mode::render_signal_palette()
ImGui::SetNextItemWidth(-1.0f);
ImGui::InputTextWithHint("##signalfilter", STR_C("filter: name, w24, sbl..."), tool.filter, sizeof(tool.filter));
auto const filter{ToLower(tool.filter)};
auto const *armed{signal_armed()};
auto const *current{signal_current()};
std::vector<std::vector<int>> groups;
std::vector<std::string> keys;
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
@@ -1538,20 +1543,24 @@ void editor_mode::render_signal_palette()
{
auto const &group{groups[g]};
auto const &first{tool.templates[group.front()]};
auto const chosen{armed != nullptr && group_of(armed->mount, armed->lamps) == keys[g] && armed->kind == first.kind};
auto const chosen{current != nullptr && group_of(current->mount, current->lamps) == keys[g] && current->kind == first.kind};
if (g % columns != 0)
ImGui::SameLine();
ImGui::PushID(g);
auto const origin{ImGui::GetCursorScreenPos()};
if (ImGui::InvisibleButton("##tile", tile))
{
auto pick{group.front()};
if (armed != nullptr && chosen)
pick = tool.chosen[tool.kind];
tool.chosen[tool.kind] = pick;
if (chosen && tool.armed)
tool.armed = false;
else
{
tool.chosen[tool.kind] = chosen ? tool.chosen[tool.kind] : group.front();
tool.armed = true;
}
}
auto const hovered{ImGui::IsItemHovered()};
draw->AddRectFilled(origin, ImVec2(origin.x + tile.x, origin.y + tile.y), chosen ? IM_COL32(60, 90, 140, 255) : hovered ? IM_COL32(60, 60, 60, 255) : IM_COL32(40, 40, 40, 255), 4.0f);
auto const armed{chosen && tool.armed};
draw->AddRectFilled(origin, ImVec2(origin.x + tile.x, origin.y + tile.y), armed ? IM_COL32(45, 110, 60, 255) : chosen ? IM_COL32(60, 90, 140, 255) : hovered ? IM_COL32(60, 60, 60, 255) : IM_COL32(40, 40, 40, 255), 4.0f);
draw_signal_icon(draw, origin, ImVec2(tile.x, tile.y - 14.0f), first.mount, first.lamps, chosen ? overlay_color::selected : IM_COL32(120, 120, 120, 255));
auto const caption{first.lamps.empty() ? first.name : first.lamps};
auto const captionsize{ImGui::CalcTextSize(caption.c_str())};
@@ -1564,24 +1573,25 @@ void editor_mode::render_signal_palette()
ImGui::TextUnformatted(first.description.empty() ? first.name.c_str() : first.description.c_str());
ImGui::TextDisabled(STR_C("%zu variants, e.g. %s"), group.size(), first.file.c_str());
ImGui::TextDisabled("%s", first.read.empty() ? STR_C("The train doesn't read it") : STR_C("The train reads it by an event of the path"));
ImGui::TextDisabled("%s", armed ? STR_C("Click: stop placing") : STR_C("Click: place it by a track, the signalling of the station places its signals of this kind with it too"));
ImGui::EndTooltip();
}
ImGui::PopID();
}
ImGui::EndChild();
if (armed == nullptr)
if (current == nullptr)
return;
auto const key{group_of(armed->mount, armed->lamps)};
auto const key{group_of(current->mount, current->lamps)};
ImGui::SetNextItemWidth(-1.0f);
if (ImGui::BeginCombo("##signalvariant", armed->name.c_str()))
if (ImGui::BeginCombo("##signalvariant", current->name.c_str()))
{
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
{
auto const &item{tool.templates[i]};
if (item.kind != armed->kind || group_of(item.mount, item.lamps) != key)
if (item.kind != current->kind || group_of(item.mount, item.lamps) != key)
continue;
auto const label{item.name + (item.lean == "left" ? std::string{" \xE2\x86\x96"} : item.lean == "right" ? std::string{" \xE2\x86\x97"} : std::string{}) + "##" + item.file};
if (ImGui::Selectable(label.c_str(), &item == armed))
if (ImGui::Selectable(label.c_str(), &item == current))
tool.chosen[tool.kind] = i;
if (ImGui::IsItemHovered() && false == item.description.empty())
ImGui::SetTooltip("%s\n%s", item.description.c_str(), item.file.c_str());
@@ -1590,20 +1600,27 @@ void editor_mode::render_signal_palette()
}
if (ImGui::IsItemHovered())
{
auto const read{armed->read.empty() ? std::string{armed->kind == "warning" || armed->kind == "repeater" ? STR_C("The train doesn't read it: it repeats what the signal ahead of it shows") : STR_C("The train doesn't read it")}
: format(STR_C("The train reads it by %s on the path it stands by"), editor_includes::substitute(armed->read, {tool.name[0] != '\0' ? std::string{tool.name} : std::string{"?"}}).c_str())};
auto const read{current->read.empty() ? std::string{current->kind == "warning" || current->kind == "repeater" ? STR_C("The train doesn't read it: it repeats what the signal ahead of it shows") : STR_C("The train doesn't read it")}
: format(STR_C("The train reads it by %s on the path it stands by"), editor_includes::substitute(current->read, {tool.name[0] != '\0' ? std::string{tool.name} : std::string{"?"}}).c_str())};
ImGui::SetTooltip("%s\n%s", STR_C("Variant of the chosen type. A head leaning to one side is swapped for its counterpart leaning towards the track on the other side"), read.c_str());
}
if (false == armed->description.empty())
ImGui::TextWrapped("%s", armed->description.c_str());
if (false == current->description.empty())
ImGui::TextWrapped("%s", current->description.c_str());
if (tool.armed)
{
if (ImGui::Button(STR_C("Stop placing (Esc)"), ImVec2(-1.0f, 0.0f)))
tool.armed = false;
}
else if (main_button(STR_C("Place by a track"), ImVec2(-1.0f, 0.0f)))
tool.armed = true;
item_tooltip("A click by a track places the chosen signal on that side, a press dragged along it turns it to the trains running that way. The signalling of the station places its signals of each kind with the template chosen here");
}
void editor_mode::render_signal_selected()
{
auto const *found{signal_standing_of(m_signal.selected)};
auto const *found{m_signal.selected == m_instance ? signal_standing_of(m_signal.selected) : nullptr};
if (found == nullptr)
return;
ImGui::Separator();
ImGui::TextUnformatted(format(STR_C("Selected: %s (%s)"), found->name.c_str(), found->include.file.c_str()).c_str());
item_tooltip("Name, plate and the linked signal: in the node properties. Del removes it with its event");
if (found->read.empty())
@@ -1640,6 +1657,22 @@ void editor_mode::render_signal_selected()
if (ImGui::Button(STR_C("Turn around"), ImVec2(width, 0.0f)))
signal_turn(instance);
item_tooltip("Turns the signal to the trains of the other direction, they read it by the other event of the path");
ImGui::Separator();
}
void editor_mode::select_signal(scene::instance_handle const Instance)
{
signal_refresh(true);
signal_select(Instance);
}
void editor_mode::render_signal_templates()
{
ImGui::Spacing();
if (ImGui::CollapsingHeader(format("%s###srksignals", STR_C("Signals")).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
render_signal_ui();
else
m_signal.armed = false;
}
void editor_mode::render_signal_ui()
@@ -1669,8 +1702,7 @@ void editor_mode::render_signal_ui()
tool.kind = kinds.front().value;
choice_chips("signalkinds", tool.kind, kinds);
render_signal_palette();
auto const *armed{signal_armed()};
ImGui::Separator();
auto const *armed{signal_current()};
if (begin_fields("##signalnames", 0.3f))
{
field_row("Name");
@@ -1746,7 +1778,6 @@ void editor_mode::render_signal_ui()
ImGui::PopID();
if (false == tool.status.empty())
ImGui::TextWrapped("%s", tool.status.c_str());
render_signal_selected();
}
void editor_mode::draw_signal_overlay() const

View File

@@ -630,7 +630,7 @@ int resonator_at(std::vector<station::approach_point> const &Way, double const W
return best;
}
// the template of a block signal of the aspects wanted, Like: the one standing, empty for the one chosen in the Signals tool, taken
// the template of a block signal of the aspects wanted, Like: the one standing, empty for the one chosen in Signals, taken
// where it has them, else the one of their mount with them, its counterpart by the name first, then of its lights; not the first of
// a line, whose events tell a station signal; Wrong: of a wrong-track one, standing on the left, its head leaning right where one
// does, a head leaning so only for it; empty for none
@@ -1135,12 +1135,11 @@ void srk_tool::render_options()
if (false == m_layout.has_value())
{
ImGui::TextDisabled("%s", STR_C("The signalling of a station: its schematic, the numbers of the switches and the tracks, the track circuits and the signals"));
ImGui::Spacing();
gesture_rows({{"LMB on a switch", "opens its station"}});
if (false == m_status.empty())
ImGui::TextColored(kDoubtful, "%s", m_status.c_str());
ImGui::Spacing();
render_found();
m_signals.render_signal_templates();
return;
}
auto const &layout{*m_layout};
@@ -1253,7 +1252,7 @@ void srk_tool::render_options()
"each line, A, B... from the start of the km, Z, Y... from the end, the circuit from 15 m beyond it to the station bpd_ita; "
"an exit signal 15 m before the joint at the end of each station track a train is sent out of, F1, G2 towards the start, R1, "
"P2 towards the end; a shunting signal Tm1, Tm2... at the end of a shorter track. A signal standing by the place of one gets "
"its name, none is doubled. The kinds of signals chosen in the Signals tool. One step of undo");
"its name, none is doubled. Each kind with its template chosen in Signals below. One step of undo");
step_heading(4, STR_C("Lines"));
render_lines();
if (ImGui::Button(STR_C("Set the line signals"), ImVec2(-1.0f, 0.0f)))
@@ -1303,6 +1302,7 @@ void srk_tool::render_options()
}
if (false == m_status.empty())
ImGui::TextColored(kDoubtful, "%s", m_status.c_str());
m_signals.render_signal_templates();
ImGui::Spacing();
m_length_hover = -1;
if (ImGui::CollapsingHeader(format("%s (%zu)###srklengths", STR_C("Track lengths"), m_lengths.size()).c_str(), ImGuiTreeNodeFlags_DefaultOpen))
@@ -1499,7 +1499,7 @@ void srk_tool::render_lines()
"warning disc, of 3 or 4 aspects, the templates of the block signals standing swapped for those of the aspects. "
"Semi-automatic: a warning disc ToA the braking distance before the entry signal. By itself: the automatic one where "
"block signals stand along the line, of their aspects, or it's double-track, of the aspects of the block signal "
"chosen in the Signals tool, else the semi-automatic one");
"chosen in Signals below, else the semi-automatic one");
ImGui::PopID();
}
ImGui::EndTable();
@@ -1818,7 +1818,10 @@ void srk_tool::render_heads()
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable((head.name + "##srkhead" + std::to_string(index)).c_str(), static_cast<int>(index) == m_head_hover, ImGuiSelectableFlags_SpanAllColumns))
{
m_view.fly_to(head.position + glm::dvec3{0.0, 4.0, 0.0}, glm::normalize(glm::dvec3{head.travel.x, -0.3, head.travel.y}), 30.0);
m_signals.select_signal(head.instance);
}
if (ImGui::IsItemHovered())
{
m_head_hover = static_cast<int>(index);
@@ -2541,7 +2544,7 @@ void srk_tool::set_signals()
std::string names;
for (auto const &name : unfit)
names += (names.empty() ? "" : ", ") + name;
m_status += "\n" + format(STR_C("No template has the lights %s need, they got the one chosen in the Signals tool: see Signal heads"), names.c_str());
m_status += "\n" + format(STR_C("No template has the lights %s need, they got the one chosen in Signals: see Signal heads"), names.c_str());
}
}

View File

@@ -328,7 +328,7 @@ class srk_tool final : public editor_tool
// the standing ones; false with the reason while it has none
bool lines_of_open(std::vector<standing_signal> const &Standing, std::vector<station::line> &Lines, std::vector<station::signal> &Entries, std::vector<std::size_t> &Owners, std::string &Problem) const;
// the block of the line: as set by hand for its entry signal, else the automatic one where block signals stand along it, of four
// aspects where one of them has, else on a double-track line, of the aspects of the block signal chosen in the Signals tool, else
// aspects where one of them has, else on a double-track line, of the aspects of the block signal chosen in Signals, else
// the semi-automatic one; Own: the block it has by itself
block_kind block_of(station::line const &Line, std::string const &Entry, std::vector<standing_signal> const &Standing, std::vector<signal_head> const &Heads, block_reason &Why, block_kind &Own, block_reason &Own_why, std::size_t &Blocks) const;
// the automatic block of the double-track line both ways: as set by hand for its entry signal, else where block signals stand

View File

@@ -453,17 +453,16 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve
profile_restore();
}
std::array<editor_mode::track_mode, 13> const &editor_mode::track_modes()
std::array<editor_mode::track_mode, 12> const &editor_mode::track_modes()
{
static std::array<track_mode, 13> const modes{{
static std::array<track_mode, 12> const modes{{
{"Select", "Esc", track_tab::path, "LMB on a path selects it: its points, control vectors and parameters", editor_icons::icon::select, 0},
{"Lay track", "L", track_tab::lay, "Lays new track through the clicked points, on the ground or from a free end of a track", editor_icons::icon::lay_track, 0},
{"Plan", "G", track_tab::straights, "The plan of the line laid: LMB on a straight drags its ends or the middle, on a curve its vertex and radius. C works too", editor_icons::icon::curve, 0},
{"Switch", "T", track_tab::turnout, "Puts switches into the selected straight or curve, edits the geometry of the selected switch", editor_icons::icon::turnout, 0},
{"Parallel", "N", track_tab::parallel, "A second track beside a track: LMB on it, then the side and the spacing", editor_icons::icon::parallel, 0},
{"Turntable", "U", track_tab::turntable, "Places a turntable, leads tracks out of it and fits the tracks around to it", editor_icons::icon::turntable, 1},
{"Signals", "H", track_tab::signals, "Signals by the track, from the templates of the scenery, with the event the train reads them by", editor_icons::icon::signal, 1},
{"Signalling", "I", track_tab::srk, "The signalling of a station: LMB on a switch lays its station out as a schematic", editor_icons::icon::station, 1},
{"Signalling", "I", track_tab::srk, "The signals and the signalling of the stations: LMB on a switch lays its station out as a schematic, the signals placed by hand from the templates in the panel", editor_icons::icon::signal, 1},
{"Kilometrage", "X", track_tab::kilometrage, "The km of the railway lines: LMB on a track puts a point of the km there, a jump of the km too", editor_icons::icon::kilometrage, 1},
{"Objects", "B", track_tab::lineside, "Along the track: hectometre posts, fouling point markers, trainsets", editor_icons::icon::objects, 1},
{"Profile", "P", track_tab::profile, "Vertical profile (grade line) along the line", editor_icons::icon::profile, 2},
@@ -492,6 +491,7 @@ void editor_mode::show_track_tab(track_tab const Wanted)
m_lay.status.clear();
m_lay.active = Tab == track_tab::lay;
m_switch.armed = -1;
m_signal.armed = false;
m_track_tab = Tab;
if ((Tab == track_tab::lay || Tab == track_tab::straights || Tab == track_tab::route) && false == m_norms_asked && EditorSettings.norms() == editorSettings::track_norms::ask && std::getenv("EU07_EDITOR_SELFTEST") == nullptr)
{
@@ -525,7 +525,7 @@ void editor_mode::show_track_tab(track_tab const Wanted)
scan_turntables(false);
turntable_read(track);
break;
case track_tab::signals:
case track_tab::srk:
signal_refresh(true);
break;
default:
@@ -608,7 +608,6 @@ std::string editor_mode::track_mode_name() const
case track_tab::lineside: return STR_C("Objects along the track");
case track_tab::infra: return STR_C("Infrastructure");
case track_tab::parallel: return STR_C("Parallel track");
case track_tab::signals: return STR_C("Signal");
case track_tab::srk: return STR_C("Signalling");
case track_tab::kilometrage: return STR_C("Kilometrage");
case track_tab::turntable: return STR_C("Turntable");
@@ -707,8 +706,10 @@ void editor_mode::render_track_tool_options()
ImGui::TextDisabled("%s", STR_C("What the check finds is listed in its window"));
break;
case track_tab::lineside: render_lineside_ui(); break;
case track_tab::signals: render_signal_ui(); break;
case track_tab::srk: m_srk.render_options(); break;
case track_tab::srk:
render_signal_selected();
m_srk.render_options();
break;
case track_tab::kilometrage: m_km.render_options(); break;
case track_tab::turntable: render_turntable_ui(); break;
default:
@@ -1711,16 +1712,17 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
hints = {{"LMB on a track", "the line the objects go along"}};
else if (m_track_window_open && m_track_tab == track_tab::parallel)
hints = {{"LMB on a track", "the new track goes beside it"}, {"Click on a side in the panel", "the side of the new track"}};
else if (m_track_window_open && m_track_tab == track_tab::signals && m_signal.moving)
else if (m_track_window_open && m_track_tab == track_tab::srk && m_signal.moving)
hints = {{"Drag along", "moves the signal along the track"}, {"Drag over the track", "to the other side"}, {"Release", "done"}, {"Esc", "cancel"}};
else if (m_track_window_open && m_track_tab == track_tab::signals && m_signal.placing)
else if (m_track_window_open && m_track_tab == track_tab::srk && m_signal.placing)
hints = {{"Drag along", "the trains it faces run that way"}, {"Release", "done"}, {"Esc", "cancel"}};
else if (m_track_window_open && m_track_tab == track_tab::signals && signal_armed() == nullptr)
hints = {{"List in the panel", "the type of the signal"}, {"Drag a signal", "moves it along the track"}, {"LMB on a signal", "selects it"}};
else if (m_track_window_open && m_track_tab == track_tab::signals)
else if (m_track_window_open && m_track_tab == track_tab::srk && false == m_srk.drawing())
{
hints = {{"LMB by a track", "places the signal on that side"}, {"Press+drag along", "the trains it faces run that way"}, {"Drag a signal", "moves it along the track"}, {"LMB on a signal", "selects it"}};
if (m_signal.selected != 0)
if (signal_armed() != nullptr)
hints = {{"LMB by a track", "places the signal on that side"}, {"Press+drag along", "the trains it faces run that way"}, {"Esc", "stops placing"}};
else
hints = {{"LMB on a switch", "opens its station"}, {"List in the panel", "the type of the signal"}, {"Drag a signal", "moves it along the track"}, {"LMB on a signal", "selects it"}};
if (m_signal.selected != 0 && m_signal.selected == m_instance)
hints.push_back({"Del", "removes the selected signal with its event"});
}
else if (m_track_window_open && m_track_tab == track_tab::turntable && m_turntable.placing)

View File

@@ -424,40 +424,6 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
}
if (m_track_tab == track_tab::turntable)
return turntable_intent(ground, shift);
if (m_track_tab == track_tab::signals)
{
if (auto const hit{signal_hit()}; hit >= 0)
{
auto const &standing{m_signal.standing[hit]};
intent.index = hit;
intent.position = standing.include.location;
intent.action = format(STR_C("Click: select the signal %s, drag: move it along the track"), standing.name.c_str());
intent.what = kind::signal_move;
return intent;
}
auto const *armed{signal_armed()};
if (m_hover.track != nullptr && armed != nullptr)
{
signal_spot spot;
if (signal_spot_at(*m_hover.track, m_hover.path, m_hover.point, cursor_level(m_hover.point.y), 0, 0, *armed, spot))
{
intent.track = m_hover.track;
intent.path = m_hover.path;
intent.position = spot.position;
intent.action = signal_intent_label(*armed, spot);
intent.what = kind::signal_place;
return intent;
}
}
if (m_hover.track != nullptr)
{
intent.track = m_hover.track;
intent.path = m_hover.path;
intent.position = m_hover.point;
return make(kind::select, "Choose the type of the signal in the Tracks window first");
}
return intent;
}
if (m_track_tab == track_tab::srk && m_srk.drawing())
{
bool valid{false};
@@ -468,12 +434,38 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
intent.what = kind::srk_boundary;
return intent;
}
if (m_track_tab == track_tab::srk && m_hover.track != nullptr)
if (m_track_tab == track_tab::srk)
{
intent.track = m_hover.track;
intent.path = m_hover.path;
intent.position = m_hover.point;
return make(kind::select, m_hover.track->eType != tt_Normal ? "Click: the station of this switch" : "Click: the station of the switch nearest along this track");
if (auto const hit{signal_hit()}; hit >= 0)
{
auto const &standing{m_signal.standing[hit]};
intent.index = hit;
intent.position = standing.include.location;
intent.action = format(STR_C("Click: select the signal %s, drag: move it along the track"), standing.name.c_str());
intent.what = kind::signal_move;
return intent;
}
if (auto const *armed{signal_armed()}; armed != nullptr)
{
signal_spot spot;
if (m_hover.track != nullptr && signal_spot_at(*m_hover.track, m_hover.path, m_hover.point, cursor_level(m_hover.point.y), 0, 0, *armed, spot))
{
intent.track = m_hover.track;
intent.path = m_hover.path;
intent.position = spot.position;
intent.action = signal_intent_label(*armed, spot);
intent.what = kind::signal_place;
}
return intent;
}
if (m_hover.track != nullptr)
{
intent.track = m_hover.track;
intent.path = m_hover.path;
intent.position = m_hover.point;
return make(kind::select, m_hover.track->eType != tt_Normal ? "Click: the station of this switch" : "Click: the station of the switch nearest along this track");
}
return intent;
}
if (m_track_tab == track_tab::kilometrage && m_hover.track != nullptr)
{

View File

@@ -177,17 +177,16 @@ void editor_mode::work_area_tools(std::vector<tool_button> &Buttons)
add(track_tab::straights, false);
add(track_tab::turnout, false);
add(track_tab::parallel, false);
auto const advanced{m_track_tab == track_tab::turntable || m_track_tab == track_tab::signals || m_track_tab == track_tab::srk || m_track_tab == track_tab::kilometrage || m_track_tab == track_tab::lineside || m_track_tab == track_tab::profile || m_track_tab == track_tab::speed ||
auto const advanced{m_track_tab == track_tab::turntable || m_track_tab == track_tab::srk || m_track_tab == track_tab::kilometrage || m_track_tab == track_tab::lineside || m_track_tab == track_tab::profile || m_track_tab == track_tab::speed ||
m_track_tab == track_tab::joints || m_gauge.is_open()};
auto const more{EditorSettings.track_tools_more()};
Buttons.push_back({icon::more, more ? STR_C("Less") : STR_C("More"), nullptr, STR_C("Turntable, signals, signalling, kilometrage, objects, trainset, profile, speed, joints, gauge"), more, [more]() {
Buttons.push_back({icon::more, more ? STR_C("Less") : STR_C("More"), nullptr, STR_C("Turntable, signalling and signals, kilometrage, objects, trainset, profile, speed, joints, gauge"), more, [more]() {
EditorSettings.track_tools_more(false == more);
EditorSettings.save();
}, true});
if (false == more && false == advanced)
break;
add(track_tab::turntable, true);
add(track_tab::signals, false);
add(track_tab::srk, false);
add(track_tab::kilometrage, false);
add(track_tab::lineside, false);

View File

@@ -36,7 +36,7 @@ enum class editor_selftest_result
enum class editor_first_step { tracks, lay, laid, wheel, join, turnout, undo, save, drive, look, top, select, shape, profile, profile_point, profile_apply, terrain, nmt, plan, plan_band, plan_apply, signal, posts, count };
enum class editor_track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, signals, parallel, srk, kilometrage };
enum class editor_track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, parallel, srk, kilometrage };
struct track_hover
{
@@ -116,7 +116,7 @@ struct signal_head
std::string mount;
std::string lamps; // its chambers from the top: z green, c red, p orange, b white, n blue, x blind
std::vector<int> speeds; // of the signal itself, km/h: -1 the highest allowed, 20 the substitute signal
bool chosen{false}; // the Signals tool places it for its kind
bool chosen{false}; // chosen for its kind among the templates of the signals, placed by hand and with the signalling
int aspects{0}; // of a block signal: 3, or 4 with the warning two signals ahead
std::string lean; // its head leans towards the track: left, standing on the right of it, right, or none
};
@@ -219,6 +219,10 @@ class editor_signals
virtual bool signal_heads(std::vector<signal_head> &Heads, bool const Wait) = 0;
// the signals given which no track reads, read again by the track they stand by: one step of the history. returns: how many
virtual std::size_t signal_reattach(std::vector<scene::instance_handle> const &Instances) = 0;
// the signal picked, its card at the top of the panel
virtual void select_signal(scene::instance_handle const Instance) = 0;
// the templates of the signals: the one of each kind placed by hand and with the signalling of a station
virtual void render_signal_templates() = 0;
protected:
~editor_signals() = default;