From eab3dca70084a1132cee90bb4611feb0a40282d5 Mon Sep 17 00:00:00 2001 From: maj00r Date: Fri, 23 Oct 2026 12:26:07 +0200 Subject: [PATCH] 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 --- application/editormode.cpp | 16 ++--- application/editormode.h | 6 +- application/editormodeselftestmonkey.cpp | 2 +- application/editormodeselftestsignal.cpp | 6 +- application/editormodesignal.cpp | 77 +++++++++++++++++------- application/editormodesrk.cpp | 15 +++-- application/editormodesrk.h | 2 +- application/editormodetrack.cpp | 32 +++++----- application/editormodetrackux.cpp | 70 ++++++++++----------- application/editormodeviewport.cpp | 5 +- application/editortool.h | 8 ++- lang/editor_pl.po | 48 +++++++++------ 12 files changed, 168 insertions(+), 119 deletions(-) diff --git a/application/editormode.cpp b/application/editormode.cpp index 35742e4d..16a22c92 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -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(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(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) diff --git a/application/editormode.h b/application/editormode.h index d450a717..80cc6388 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -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 const &track_modes(); + static std::array 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> States, std::vector Created, std::string &Error) override; bool signal_heads(std::vector &Heads, bool const Wait) override; std::size_t signal_reattach(std::vector 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 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; diff --git a/application/editormodeselftestmonkey.cpp b/application/editormodeselftestmonkey.cpp index 255e9147..e01cf08c 100644 --- a/application/editormodeselftestmonkey.cpp +++ b/application/editormodeselftestmonkey.cpp @@ -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) { diff --git a/application/editormodeselftestsignal.cpp b/application/editormodeselftestsignal.cpp index aab2d651..53dbf476 100644 --- a/application/editormodeselftestsignal.cpp +++ b/application/editormodeselftestsignal.cpp @@ -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") diff --git a/application/editormodesignal.cpp b/application/editormodesignal.cpp index c27b8a77..740b1549 100644 --- a/application/editormodesignal.cpp +++ b/application/editormodesignal.cpp @@ -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(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 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 signals; std::unordered_map 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> groups; std::vector keys; for (int i = 0; i < static_cast(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(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 diff --git a/application/editormodesrk.cpp b/application/editormodesrk.cpp index 1db9f353..23a261ec 100644 --- a/application/editormodesrk.cpp +++ b/application/editormodesrk.cpp @@ -630,7 +630,7 @@ int resonator_at(std::vector 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(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(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()); } } diff --git a/application/editormodesrk.h b/application/editormodesrk.h index 742075fd..3c603b82 100644 --- a/application/editormodesrk.h +++ b/application/editormodesrk.h @@ -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 const &Standing, std::vector &Lines, std::vector &Entries, std::vector &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 const &Standing, std::vector 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 diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 7e7a5447..383aeac3 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -453,17 +453,16 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve profile_restore(); } -std::array const &editor_mode::track_modes() +std::array const &editor_mode::track_modes() { - static std::array const modes{{ + static std::array 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::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) diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 9b349d59..f45e585b 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -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) { diff --git a/application/editormodeviewport.cpp b/application/editormodeviewport.cpp index 3f20b92c..a709576c 100644 --- a/application/editormodeviewport.cpp +++ b/application/editormodeviewport.cpp @@ -177,17 +177,16 @@ void editor_mode::work_area_tools(std::vector &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); diff --git a/application/editortool.h b/application/editortool.h index 12213703..7fd1255f 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -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 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 &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 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; diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 4bf74539..7d4ff6b7 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -3570,8 +3570,8 @@ msgstr "krawędź peronu od osi toru" msgid "Signals" msgstr "Sygnalizatory" -msgid "Signals by the track, from the templates of the scenery, with the event the train reads them by" -msgstr "Sygnalizatory przy torze, z szablonów scenerii, z eventem, którym pociąg je odczytuje" +msgid "Click: stop placing" +msgstr "Klik: koniec stawiania" msgid "moves the signal along the track" msgstr "przesuwa sygnalizator wzdłuż toru" @@ -3600,8 +3600,20 @@ msgstr "usuwa zaznaczony sygnalizator razem z jego eventem" msgid "Click: select the signal %s, drag: move it along the track" msgstr "LPM: zaznacz sygnalizator %s, przeciągnij: przesuń go wzdłuż toru" -msgid "Choose the type of the signal in the Tracks window first" -msgstr "Najpierw wybierz typ sygnalizatora w oknie Tory" +msgid "Click: place it by a track, the signalling of the station places its signals of this kind with it too" +msgstr "Klik: stawiaj go przy torze; SRK stacji stawia nim też swoje sygnalizatory tego rodzaju" + +msgid "Stop placing (Esc)" +msgstr "Zakończ stawianie (Esc)" + +msgid "Place by a track" +msgstr "Stawiaj przy torze" + +msgid "stops placing" +msgstr "kończy stawianie" + +msgid "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" +msgstr "Klik przy torze stawia wybrany sygnalizator po tej stronie, wciśnięcie przeciągnięte wzdłuż toru obraca go do pociągów jadących w tę stronę. SRK stacji stawia swoje sygnalizatory każdego rodzaju szablonem wybranym tutaj" msgid "Semaphore" msgstr "Semafor" @@ -5981,8 +5993,8 @@ msgstr "Rozjazd krzyżowy pojedynczy (Rkp) albo podwójny (Rkpd) na skrzyżowani msgid "Crossover, double, scissors, ladder" msgstr "Przejście rozjazdowe, podwójne przejście rozjazdowe, nożyce, ulica zwrotnicowa" -msgid "Turntable, signals, signalling, kilometrage, objects, trainset, profile, speed, joints, gauge" -msgstr "Obrotnica, sygnalizatory, SRK, kilometraż, obiekty, skład, profil, prędkość, styki, skrajnia" +msgid "Turntable, signalling and signals, kilometrage, objects, trainset, profile, speed, joints, gauge" +msgstr "Obrotnica, SRK i sygnalizatory, kilometraż, obiekty, skład, profil, prędkość, styki, skrajnia" msgid "No switch drive models" msgstr "Brak modeli napędów" @@ -7499,8 +7511,8 @@ msgstr "B: słupki hektometrowe całej linii naraz" msgid "Signals and posts" msgstr "Sygnalizatory i słupki" -msgid "The signals tool: the type of the signal in the panel, from the templates of the scenery" -msgstr "Narzędzie Sygnalizatory: typ sygnalizatora w panelu, z szablonów scenerii" +msgid "The signalling tool: click a type of signal in its Signals, from the templates of the scenery" +msgstr "Narzędzie SRK: kliknij typ sygnalizatora w sekcji Sygnalizatory, z szablonów scenerii" msgid "A click by a track places the signal on that side" msgstr "Klik przy torze stawia sygnalizator po tej stronie" @@ -7520,8 +7532,8 @@ msgstr "Postaw sygnalizator" msgid "Place the hectometre posts of a line" msgstr "Postaw słupki hektometrowe linii" -msgid "The signals tool (H)" -msgstr "Narzędzie Sygnalizatory (H)" +msgid "The signalling tool (I)" +msgstr "Narzędzie SRK (I)" msgid "\nPaths off the route brought to its height:" msgstr "\nTory obok trasy doprowadzone do jej wysokości:" @@ -10281,8 +10293,8 @@ msgstr "Wyłączone: tor kładziony od innego toru albo dołączany do niego jes msgid "Signalling" msgstr "SRK" -msgid "The signalling of a station: LMB on a switch lays its station out as a schematic" -msgstr "Sterowanie ruchem na stacji: LPM na rozjazd rozkłada jego stację w schemat" +msgid "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" +msgstr "Sygnalizatory i sterowanie ruchem na stacjach: LPM na rozjazd rozkłada jego stację w schemat, sygnalizatory stawia się ręcznie z szablonów w panelu" msgid "Click: the station of this switch" msgstr "Klik: stacja tego rozjazdu" @@ -10464,8 +10476,8 @@ msgstr "Tor %s ma teraz numer %s" msgid "Set the signals" msgstr "Ustaw semafory" -msgid "Places the signals of the station, as on the lines of PKP PLK, and names them after its code: an entry signal 300 m out on 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" -msgstr "Stawia semafory stacji jak na liniach PKP PLK i nazywa je od jej skrótu: wjazdowy 300 m przed stacją na każdej linii, A, B... od początku km, Z, Y... od końca, a odcinek od 15 m za nim do stacji to bpd_ita; wyjazdowy 15 m przed stykiem na końcu każdego toru, z którego wyprawia się pociągi, F1, G2 w stronę początku, R1, P2 w stronę końca; tarcza manewrowa Tm1, Tm2... na końcu krótszego toru. Semafor, który już stoi w miejscu któregoś z nich, dostaje tylko jego nazwę, nic się nie dubluje. Modele semaforów te wybrane w narzędziu Sygnalizatory. Jeden krok cofania" +msgid "Places the signals of the station, as on the lines of PKP PLK, and names them after its code: an entry signal 300 m out on 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. Each kind with its template chosen in Signals below. One step of undo" +msgstr "Stawia semafory stacji jak na liniach PKP PLK i nazywa je od jej skrótu: wjazdowy 300 m przed stacją na każdej linii, A, B... od początku km, Z, Y... od końca, a odcinek od 15 m za nim do stacji to bpd_ita; wyjazdowy 15 m przed stykiem na końcu każdego toru, z którego wyprawia się pociągi, F1, G2 w stronę początku, R1, P2 w stronę końca; tarcza manewrowa Tm1, Tm2... na końcu krótszego toru. Semafor, który już stoi w miejscu któregoś z nich, dostaje tylko jego nazwę, nic się nie dubluje. Każdy rodzaj szablonem wybranym niżej w Sygnalizatorach. Jeden krok cofania" msgid "Give the station its code first: the signals are named after it" msgstr "Najpierw wpisz skrót stacji: od niego biorą się nazwy semaforów" @@ -11038,8 +11050,8 @@ msgstr "Daje każdemu zaznaczonemu semaforowi szablon tego samego rodzaju i moco msgid "The lights go by the routes of the trains: 40 km/h through the switches 1:9, 60 through 1:12 and 1:14, 100 through 1:18.5, by their radii, for these a lower orange light, with an orange strip for 60, a green one for 100, Ie-1; an upper orange one where the next signal can show stop; the white one of the substitute signal. A signal telling of the next one standing nearer than the braking distance slows the trains with it at stop, Ie-4 § 8 ust. 5" msgstr "Światła wynikają z przebiegów pociągów: 40 km/h przez rozjazdy 1:9, 60 przez 1:12 i 1:14, 100 przez 1:18,5, liczone z ich promieni; dla nich dolne światło pomarańczowe, z pomarańczowym pasem dla 60 i zielonym dla 100, Ie-1; górne pomarańczowe, gdy następny semafor może pokazać stój; białe dla sygnału zastępczego. Semafor zapowiadający następny, który stoi bliżej niż droga hamowania, zmniejsza prędkość pociągów, gdy tamten pokazuje stój, Ie-4 § 8 ust. 5" -msgid "No template has the lights %s need, they got the one chosen in the Signals tool: see Signal heads" -msgstr "Żaden szablon nie ma świateł potrzebnych dla %s, dostały ten wybrany w narzędziu Sygnalizatory: zobacz Komory semaforów" +msgid "No template has the lights %s need, they got the one chosen in Signals: see Signal heads" +msgstr "Żaden szablon nie ma świateł potrzebnych dla %s, dostały ten wybrany w Sygnalizatorach: zobacz Komory semaforów" msgid "No main signal within %.0f m ahead of it to tell of" msgstr "Na %.0f m przed nią nie ma semafora do zapowiadania" @@ -11278,8 +11290,8 @@ msgstr "ze scenerii" msgid "Block signals stand along the line, none of them switched by a block section of the editor: the line keeps the block of the scenery as it stands" msgstr "Na szlaku stoją semafory SBL, ale żadnego nie przełącza odstęp edytora: szlak zostaje z blokadą scenerii, tak jak stoi" -msgid "The block of the line, both tracks of a double-track one alike. Automatic (SBL): block signals with block sections, no 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" -msgstr "Blokada szlaku, na obu torach dwutorowego taka sama. Samoczynna (SBL): semafory SBL z odstępami, bez tarczy ostrzegawczej, trzy- albo czterostawna; szablony stojących semaforów SBL zostają podmienione na te o właściwej liczbie stawności. Półsamoczynna: tarcza ostrzegawcza ToA w drodze hamowania przed semaforem wjazdowym. Auto: samoczynna, gdy na szlaku stoją semafory SBL (stawność jak u nich) albo szlak jest dwutorowy (stawność jak u semafora SBL wybranego w narzędziu Sygnalizatory), inaczej półsamoczynna" +msgid "The block of the line, both tracks of a double-track one alike. Automatic (SBL): block signals with block sections, no 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 Signals below, else the semi-automatic one" +msgstr "Blokada szlaku, na obu torach dwutorowego taka sama. Samoczynna (SBL): semafory SBL z odstępami, bez tarczy ostrzegawczej, trzy- albo czterostawna; szablony stojących semaforów SBL zostają podmienione na te o właściwej liczbie stawności. Półsamoczynna: tarcza ostrzegawcza ToA w drodze hamowania przed semaforem wjazdowym. Auto: samoczynna, gdy na szlaku stoją semafory SBL (stawność jak u nich) albo szlak jest dwutorowy (stawność jak u semafora SBL wybranego niżej w Sygnalizatorach), inaczej półsamoczynna" msgid "Draw the block boundaries" msgstr "Rysuj granice odstępów"