diff --git a/application/editormode.cpp b/application/editormode.cpp index 0d2b6acc..c7d36c8b 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -29,6 +29,7 @@ extern char **environ; #include "editor/editorGroundMesh.hpp" #include "editor/editorFormat.hpp" #include "editor/editorGeometry.hpp" +#include "editor/editorIcons.hpp" #include "utilities/translation.h" #include "utilities/Globals.h" #include "simulation/simulation.h" @@ -3792,13 +3793,14 @@ void editor_mode::render_workspaces() { char const *label; work_area which; + editor_icons::icon picture; char const *tooltip; }; area const areas[] = { - {STR_C("Surroundings"), work_area::surroundings, STR_C("Models around the line: select, insert, brush, area fill, copy to the node bank")}, - {STR_C("Tracks"), work_area::tracks, STR_C("Tracks and switches, signals, objects along the track, checks of the line")}, - {STR_C("Roads"), work_area::roads, STR_C("Roads and junctions, level crossings, the points where the traffic comes and goes")}, - {STR_C("Terrain"), work_area::terrain, STR_C("Terrain patches and chunks, sculpting, streaming, orthophoto")}, + {STR_C("Surroundings"), work_area::surroundings, editor_icons::icon::work_surroundings, STR_C("Models around the line: select, insert, brush, area fill, copy to the node bank")}, + {STR_C("Tracks"), work_area::tracks, editor_icons::icon::work_tracks, STR_C("Tracks and switches, signals, objects along the track, checks of the line")}, + {STR_C("Roads"), work_area::roads, editor_icons::icon::work_roads, STR_C("Roads and junctions, level crossings, the points where the traffic comes and goes")}, + {STR_C("Terrain"), work_area::terrain, editor_icons::icon::work_terrain, STR_C("Terrain patches and chunks, sculpting, streaming, orthophoto")}, }; char const *const keys[] = {"F2", "F3", "F4", "F5"}; auto const current{current_work_area()}; @@ -3809,11 +3811,14 @@ void editor_mode::render_workspaces() auto const fontsize{ui_layer::font_bold != nullptr ? ui_layer::font_bold->FontSize : ImGui::GetFontSize() * 1.25f}; auto const height{ImGui::GetWindowHeight()}; auto const padding{style.FramePadding.x * 3.0f}; - ImGui::SetCursorPos(ImVec2(style.ItemSpacing.x, 0.0f)); + // the icon of the field of work before its name + auto const iconside{editor_icons::size()}; + auto const spacing{style.ItemInnerSpacing.x * 1.5f}; + render_toolbar_caption(STR_C("Work modes")); for (auto const &entry : areas) { auto const text{font->CalcTextSizeA(fontsize, FLT_MAX, 0.0f, entry.label)}; - ImVec2 const size{text.x + padding * 2.0f, height}; + ImVec2 const size{text.x + iconside + spacing + padding * 2.0f, height}; auto const position{ImGui::GetCursorScreenPos()}; ImGui::PushID(static_cast(entry.which)); if (ImGui::InvisibleButton("##area", size)) @@ -3826,8 +3831,9 @@ void editor_mode::render_workspaces() list->AddRectFilled(position, end, ImGui::GetColorU32(chosen ? ImGuiCol_Header : ImGuiCol_HeaderHovered)); if (chosen) list->AddRectFilled(ImVec2(position.x, end.y - 3.0f), end, ImGui::GetColorU32(ImGuiCol_CheckMark)); - ImVec2 const textposition{position.x + padding, position.y + (height - text.y) * 0.5f}; + ImVec2 const textposition{position.x + padding + iconside + spacing, position.y + (height - text.y) * 0.5f}; auto const color{ImGui::GetColorU32(chosen ? ImGuiCol_Text : ImGuiCol_TextDisabled)}; + editor_icons::draw(list, entry.picture, ImVec2(position.x + padding + iconside * 0.5f, position.y + height * 0.5f), color); list->AddText(font, fontsize, textposition, color, entry.label); if (ui_layer::font_bold == nullptr) list->AddText(font, fontsize, ImVec2(textposition.x + 1.0f, textposition.y), color, entry.label); @@ -3837,69 +3843,10 @@ void editor_mode::render_workspaces() } } -void editor_mode::render_terrain_toolbar() -{ - // what the left button does on the terrain: picks the models as it does elsewhere, raises and lowers, evens out, adds chunks - struct tool - { - char const *label; - bool chosen; - bool sculpt; - bool smooth; - bool chunks; - char const *tooltip; - }; - tool const tools[] = { - {STR_C("Select"), false == m_terrain_sculpt && false == m_chunk_edit, false, m_terrain_brush_smooth, false, STR_C("LMB picks the models, as in the surroundings")}, - {STR_C("Sculpt"), m_terrain_sculpt && false == m_terrain_brush_smooth, true, false, false, STR_C("LMB raises the terrain under the brush, Shift+LMB lowers it")}, - {STR_C("Smooth"), m_terrain_sculpt && m_terrain_brush_smooth, true, true, false, STR_C("LMB evens the terrain out under the brush")}, - {STR_C("Chunks"), m_chunk_edit, false, m_terrain_brush_smooth, true, STR_C("LMB adds a chunk next to the clicked one, Shift+LMB deletes it")}, - }; - for (auto const &entry : tools) - { - if (ImGui::MenuItem(entry.label, nullptr, entry.chosen)) - { - m_terrain_sculpt = entry.sculpt; - m_terrain_brush_smooth = entry.smooth; - m_chunk_edit = entry.chunks; - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", entry.tooltip); - } - ImGui::Separator(); - auto const orthophoto{m_orthophoto.enabled()}; - if (ImGui::MenuItem(STR_C("Orthophoto"), nullptr, orthophoto)) - m_orthophoto.enabled(false == orthophoto); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("Aerial imagery of geoportal.gov.pl under the scenery, laid out by the origin of the scenery")); - ImGui::MenuItem(STR_C("Orthophoto settings..."), nullptr, &m_orthophoto_window); -} - void editor_mode::render_toolbar() { - auto const area{current_work_area()}; - switch (area) - { - case work_area::surroundings: - { - // edit modes of the node bank - std::pair const modes[] = { - {STR_C("Select"), nodebank_panel::MODIFY}, {STR_C("Insert"), nodebank_panel::ADD}, {STR_C("Brush"), nodebank_panel::BRUSH}, {STR_C("Area fill"), nodebank_panel::FILL}, {STR_C("Copy to bank"), nodebank_panel::COPY}}; - for (std::size_t index = 0; index < std::size(modes); ++index) - { - auto const &mode{modes[index]}; - if (ImGui::MenuItem(mode.first, nullptr, ui()->mode() == mode.second)) - choose_edit_mode(mode.second); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s (%d)", mode.first, static_cast(index + 1)); - } - break; - } - case work_area::tracks: render_track_toolbar(); break; - case work_area::roads: render_road_toolbar(); break; - case work_area::terrain: render_terrain_toolbar(); break; - } // the modes of the gizmo and the top view are over the 3d view, see render_viewport_overlays() + render_toolbar_tools(); } void editor_mode::render_edit_menu() diff --git a/application/editormode.h b/application/editormode.h index edc45daa..6c7b9776 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -38,6 +38,8 @@ http://mozilla.org/MPL/2.0/. #include class TAnimModel; +// a tool of a field of work of the editor, a button in the toolbar and over the 3d view +struct editor_tool_button; class TTrack; namespace ui { @@ -280,8 +282,6 @@ class editor_mode : public application_mode, private editor_track::observer void render_terrain_ui(); // the terrain as a field of work of its own: the terrain tools and the orthophoto in the tool options window void render_terrain_tool_options(); - // tools of the terrain in the toolbar: picking, sculpting, smoothing, chunks, orthophoto - void render_terrain_toolbar(); // opens or leaves the terrain; leaving it puts the sculpting and the chunk editing away, so the mouse doesn't stay with them void terrain_workspace(bool const Open); // creates a large terrain as a grid of adjacent chunks (each its own editable patch) @@ -389,6 +389,12 @@ class editor_mode : public application_mode, private editor_track::observer // world and the top view in its top right corner void render_viewport_overlays(); void render_viewport_tools(); + // the tools of the field of work, in the order of the toolbar + void work_area_tools(std::vector &Buttons); + // the tools of the field of work in the toolbar, as icons with their names + void render_toolbar_tools(); + // what the row of the toolbar holds, written at its left end: the fields of work, the tools + static void render_toolbar_caption(char const *Text); void render_viewport_gizmo(); void render_view_axes(); // the camera looking along an axis of the world (0: x, 1: y, 2: z) from its positive or negative side @@ -491,7 +497,7 @@ class editor_mode : public application_mode, private editor_track::observer void render_gizmo(); // gizmo settings, drawn in the toolset window void render_gizmo_options(); - // edit modes, editor windows and gizmo settings in the toolbar under the menu + // the tools of the field of work in the toolbar under the menu void render_toolbar(); // an edit mode of the node bank as the field of work, from the toolbar or its key (1-5) void choose_edit_mode(nodebank_panel::edit_mode const Mode); @@ -915,8 +921,6 @@ class editor_mode : public application_mode, private editor_track::observer // the track tools in the tool options window, and the selected path in the inspector void render_track_tool_options(); void render_track_selection(); - // tools of the track mode in the toolbar, in groups: building, by the track, checks - void render_track_toolbar(); void show_track_tab(track_tab const Tab); bool track_analysis_tab() const { return m_track_tab == track_tab::profile || m_track_tab == track_tab::speed || m_track_tab == track_tab::joints || m_track_tab == track_tab::infra; } bool track_shortcut(int const Key); @@ -1828,8 +1832,7 @@ class editor_mode : public application_mode, private editor_track::observer char islandtext[128]{"none"}; }; void render_road_tool_options(); - // tools of the roads in the toolbar, and the change of the tool - void render_road_toolbar(); + // the change of the road tool void road_choose_tool(int const Tool); void render_road_selection(); bool render_road_layout(road_node::state &State); diff --git a/application/editormoderoad.cpp b/application/editormoderoad.cpp index 8c800d90..a07da795 100644 --- a/application/editormoderoad.cpp +++ b/application/editormoderoad.cpp @@ -2264,40 +2264,6 @@ void editor_mode::road_choose_tool(int const Tool) } } -void editor_mode::render_road_toolbar() -{ - auto &tool{m_roadtool}; - struct entry - { - char const *label; - char const *tooltip; - }; - entry const tools[] = {{"Select", "LMB: a road piece, a junction, a point where pieces meet, a level crossing or a traffic point"}, - {"Build", "LMB: start a road, then each next point; on a loose end, the side of a road or a junction it's joined to them"}, - {"Place", "Level crossings, and the points where vehicles appear on the roads or are taken off them"}}; - for (int index = 0; index < static_cast(std::size(tools)); ++index) - { - if (ImGui::MenuItem(tools[index].label, nullptr, tool.tool == index)) - road_choose_tool(index); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", tools[index].tooltip); - } - if (tool.tool == 2) - { - // what the place tool puts - ImGui::Separator(); - char const *const kinds[] = {"Level crossing", "Spawn point", "Removal point", "Pedestrian crossing"}; - for (int index = 0; index < static_cast(std::size(kinds)); ++index) - { - if (ImGui::MenuItem(kinds[index], nullptr, tool.placekind == index)) - tool.placekind = index; - } - } - ImGui::Separator(); - if (ImGui::MenuItem("Show lanes", nullptr, tool.lanes)) - tool.lanes = !tool.lanes; -} - // the road tools and what the next pieces get, in the tool options window; the selection is drawn in the inspector by render_road_selection() void editor_mode::render_road_tool_options() { diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 0172fbe9..0dc68a75 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -814,45 +814,6 @@ void editor_mode::render_track_selection() render_path_ui(); } -void editor_mode::render_track_toolbar() -{ - auto const &modes{track_modes()}; - auto const vehicle{m_track_tab == track_tab::lineside && m_vehicle.open}; - // a tool chosen again gives way to the select one - auto const tool = [&](track_tab const Tab) { - auto const found{std::find_if(modes.begin(), modes.end(), [&](track_mode const &Mode) { return Mode.tab == Tab; })}; - if (found == modes.end()) - return; - auto const chosen{m_track_tab == Tab && false == (Tab == track_tab::lineside && vehicle)}; - auto const label{std::string{STR_C(found->label)} + "###tracktool" + std::to_string(static_cast(found - modes.begin()))}; - if (ImGui::MenuItem(label.c_str(), nullptr, chosen)) - show_track_tab(chosen ? track_tab::path : Tab); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s [%s]", STR_C(found->tooltip), found->key); - }; - // building the track, what goes by it, the checks - for (auto const tab : {track_tab::path, track_tab::lay, track_tab::turnout, track_tab::straights, track_tab::route, track_tab::turntable}) - tool(tab); - ImGui::Separator(); - tool(track_tab::signals); - tool(track_tab::lineside); - // the vehicle to drive is one of the objects along the track - if (ImGui::MenuItem(STR_C("Vehicle"), nullptr, vehicle)) - { - show_track_tab(vehicle ? track_tab::path : track_tab::lineside); - m_vehicle.expand = false == vehicle; - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("A vehicle to drive, put on the selected track; it isn't written to the scenery files")); - ImGui::Separator(); - for (auto const tab : {track_tab::profile, track_tab::speed, track_tab::joints, track_tab::infra}) - tool(tab); - if (ImGui::MenuItem(STR_C("Structure gauge"), nullptr, m_gauge.open)) - m_gauge.open = !m_gauge.open; - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)")); -} - void editor_mode::render_track_guide() { auto const hints{track_key_hints(false)}; diff --git a/application/editormodeviewport.cpp b/application/editormodeviewport.cpp index 17434652..7c9a3491 100644 --- a/application/editormodeviewport.cpp +++ b/application/editormodeviewport.cpp @@ -8,7 +8,7 @@ http://mozilla.org/MPL/2.0/. */ // over the 3d view: the tools of the field of work along its left edge, the modes of the gizmo at its top, the axes of the -// world and the top view in its top right corner +// world and the top view in its top right corner. the same tools in the toolbar under the menu, with their names #include "stdafx.h" #include "application/editormode.h" @@ -25,14 +25,9 @@ http://mozilla.org/MPL/2.0/. #include #include -namespace +struct editor_tool_button { - -using editor_icons::icon; - -struct tool_button -{ - icon picture; + editor_icons::icon picture; // icon::count if it has none: then it's only in the toolbar, as its name char const *label; char const *key; // nullptr if there's none char const *tooltip; // nullptr if the label says enough @@ -42,6 +37,12 @@ struct tool_button bool enabled{true}; }; +namespace +{ + +using editor_icons::icon; +using tool_button = editor_tool_button; + float scale() { return std::max(1.0f, Global.ui_scale); @@ -69,6 +70,27 @@ bool begin_overlay(char const *Name, ImVec2 const &Position, ImVec2 const &Size) return open; } +// the name of the tool with its key, and what it does +void button_tooltip(tool_button const &Button) +{ + std::string tip{Button.label}; + if (Button.key != nullptr) + tip += std::string{" ["} + Button.key + "]"; + if (Button.tooltip != nullptr) + tip += std::string{"\n"} + Button.tooltip; + ImGui::SetTooltip("%s", tip.c_str()); +} + +// the first letters of the label in place of the icon, if the renderer can't show images +void icon_or_letters(ImDrawList *List, tool_button const &Button, ImVec2 const &Center, ImU32 const Color) +{ + if (editor_icons::draw(List, Button.picture, Center, Color)) + return; + char const text[]{Button.label[0], (Button.label[0] != '\0' ? Button.label[1] : '\0'), '\0'}; + auto const size{ImGui::CalcTextSize(text)}; + List->AddText(ImVec2(Center.x - size.x * 0.5f, Center.y - size.y * 0.5f), Color, text); +} + // the button with the icon at the place; the label stands in for the icon if the renderer can't show images void icon_button(tool_button const &Button, ImVec2 const &Position, int const Id) { @@ -88,22 +110,9 @@ void icon_button(tool_button const &Button, ImVec2 const &Position, int const Id if (Button.chosen) list->AddRect(Position, end, ImGui::GetColorU32(ImGuiCol_CheckMark), rounding, 0, 1.5f * scale()); auto const color{Button.enabled ? (Button.chosen || hovered ? IM_COL32(255, 255, 255, 255) : IM_COL32(215, 220, 225, 235)) : IM_COL32(150, 150, 150, 110)}; - ImVec2 const center{Position.x + side * 0.5f, Position.y + side * 0.5f}; - if (false == editor_icons::draw(list, Button.picture, center, color)) - { - char const text[]{Button.label[0], (Button.label[0] != '\0' ? Button.label[1] : '\0'), '\0'}; - auto const size{ImGui::CalcTextSize(text)}; - list->AddText(ImVec2(center.x - size.x * 0.5f, center.y - size.y * 0.5f), color, text); - } + icon_or_letters(list, Button, ImVec2(Position.x + side * 0.5f, Position.y + side * 0.5f), color); if (hovered) - { - std::string tip{Button.label}; - if (Button.key != nullptr) - tip += std::string{" ["} + Button.key + "]"; - if (Button.tooltip != nullptr) - tip += std::string{"\n"} + Button.tooltip; - ImGui::SetTooltip("%s", tip.c_str()); - } + button_tooltip(Button); if (clicked && Button.enabled && Button.action) Button.action(); } @@ -122,13 +131,9 @@ void editor_mode::render_viewport_overlays() render_view_axes(); } -void editor_mode::render_viewport_tools() +void editor_mode::work_area_tools(std::vector &Buttons) { - // the tools of the field of work, as in the toolbar - std::vector buttons; - buttons.reserve(16); - auto const area{current_work_area()}; - switch (area) + switch (current_work_area()) { case work_area::surroundings: { @@ -145,7 +150,7 @@ void editor_mode::render_viewport_tools() {icon::area_fill, STR_C("Area fill"), "4", nodebank_panel::FILL}, {icon::copy_to_bank, STR_C("Copy to bank"), "5", nodebank_panel::COPY}}; for (auto const &entry : modes) - buttons.push_back({entry.picture, entry.label, entry.key, nullptr, ui()->mode() == entry.which, [this, which = entry.which]() { choose_edit_mode(which); }}); + Buttons.push_back({entry.picture, entry.label, entry.key, nullptr, ui()->mode() == entry.which, [this, which = entry.which]() { choose_edit_mode(which); }}); break; } case work_area::tracks: @@ -156,9 +161,9 @@ void editor_mode::render_viewport_tools() auto const found{std::find_if(modes.begin(), modes.end(), [&](track_mode const &Mode) { return Mode.tab == Tab; })}; if (found == modes.end()) return; - // a tool chosen again gives way to the select one, as in the toolbar + // a tool chosen again gives way to the select one auto const chosen{m_track_tab == Tab && false == (Tab == track_tab::lineside && vehicle)}; - buttons.push_back({Picture, STR_C(found->label), found->key, STR_C(found->tooltip), chosen, [this, Tab, chosen]() { show_track_tab(chosen ? track_tab::path : Tab); }, Group}); + Buttons.push_back({Picture, STR_C(found->label), found->key, STR_C(found->tooltip), chosen, [this, Tab, chosen]() { show_track_tab(chosen ? track_tab::path : Tab); }, Group}); }; add(track_tab::path, icon::select, false); add(track_tab::lay, icon::lay_track, false); @@ -168,7 +173,7 @@ void editor_mode::render_viewport_tools() add(track_tab::turntable, icon::turntable, false); add(track_tab::signals, icon::signal, true); add(track_tab::lineside, icon::objects, false); - buttons.push_back({icon::vehicle, STR_C("Vehicle"), nullptr, STR_C("A vehicle to drive, put on the selected track; it isn't written to the scenery files"), vehicle, [this, vehicle]() { + Buttons.push_back({icon::vehicle, STR_C("Vehicle"), nullptr, STR_C("A vehicle to drive, put on the selected track; it isn't written to the scenery files"), vehicle, [this, vehicle]() { show_track_tab(vehicle ? track_tab::path : track_tab::lineside); m_vehicle.expand = false == vehicle; }}); @@ -176,22 +181,29 @@ void editor_mode::render_viewport_tools() add(track_tab::speed, icon::speed, false); add(track_tab::joints, icon::joints, false); add(track_tab::infra, icon::infra, false); - buttons.push_back({icon::gauge, STR_C("Structure gauge"), nullptr, STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)"), m_gauge.open, + Buttons.push_back({icon::gauge, STR_C("Structure gauge"), nullptr, STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)"), m_gauge.open, [this]() { m_gauge.open = !m_gauge.open; }}); break; } case work_area::roads: { auto &tool{m_roadtool}; - buttons.push_back({icon::select, STR_C("Select"), nullptr, STR_C("LMB: a road piece, a junction, a point where pieces meet, a level crossing or a traffic point"), tool.tool == 0, [this]() { road_choose_tool(0); }}); - buttons.push_back({icon::road, STR_C("Build"), nullptr, STR_C("LMB: start a road, then each next point; on a loose end, the side of a road or a junction it's joined to them"), tool.tool == 1, [this]() { road_choose_tool(1); }}); - buttons.push_back({icon::place, STR_C("Place"), nullptr, STR_C("Level crossings, and the points where vehicles appear on the roads or are taken off them"), tool.tool == 2, [this]() { road_choose_tool(2); }}); - buttons.push_back({icon::lanes, STR_C("Show lanes"), nullptr, STR_C("The lanes of the roads drawn as the paths the vehicles take"), tool.lanes, [&tool]() { tool.lanes = !tool.lanes; }, true}); + Buttons.push_back({icon::select, STR_C("Select"), nullptr, STR_C("LMB: a road piece, a junction, a point where pieces meet, a level crossing or a traffic point"), tool.tool == 0, [this]() { road_choose_tool(0); }}); + Buttons.push_back({icon::road, STR_C("Build"), nullptr, STR_C("LMB: start a road, then each next point; on a loose end, the side of a road or a junction it's joined to them"), tool.tool == 1, [this]() { road_choose_tool(1); }}); + Buttons.push_back({icon::place, STR_C("Place"), nullptr, STR_C("Level crossings, and the points where vehicles appear on the roads or are taken off them"), tool.tool == 2, [this]() { road_choose_tool(2); }}); + if (tool.tool == 2) + { + // what the place tool puts + char const *const kinds[] = {STR_C("Level crossing"), STR_C("Spawn point"), STR_C("Removal point"), STR_C("Pedestrian crossing")}; + for (int index = 0; index < static_cast(std::size(kinds)); ++index) + Buttons.push_back({icon::count, kinds[index], nullptr, nullptr, tool.placekind == index, [&tool, index]() { tool.placekind = index; }, index == 0}); + } + Buttons.push_back({icon::lanes, STR_C("Show lanes"), nullptr, STR_C("The lanes of the roads drawn as the paths the vehicles take"), tool.lanes, [&tool]() { tool.lanes = !tool.lanes; }, true}); break; } case work_area::terrain: { - // what the left button does on the terrain, as in the toolbar + // what the left button does on the terrain auto const choose = [this](bool const Sculpt, bool const Smooth, bool const Chunks) { return [this, Sculpt, Smooth, Chunks]() { m_terrain_sculpt = Sculpt; @@ -199,16 +211,26 @@ void editor_mode::render_viewport_tools() m_chunk_edit = Chunks; }; }; - buttons.push_back({icon::select, STR_C("Select"), nullptr, STR_C("LMB picks the models, as in the surroundings"), false == m_terrain_sculpt && false == m_chunk_edit, choose(false, m_terrain_brush_smooth, false)}); - buttons.push_back({icon::sculpt, STR_C("Sculpt"), nullptr, STR_C("LMB raises the terrain under the brush, Shift+LMB lowers it"), m_terrain_sculpt && false == m_terrain_brush_smooth, choose(true, false, false)}); - buttons.push_back({icon::smooth, STR_C("Smooth"), nullptr, STR_C("LMB evens the terrain out under the brush"), m_terrain_sculpt && m_terrain_brush_smooth, choose(true, true, false)}); - buttons.push_back({icon::chunks, STR_C("Chunks"), nullptr, STR_C("LMB adds a chunk next to the clicked one, Shift+LMB deletes it"), m_chunk_edit, choose(false, m_terrain_brush_smooth, true)}); + Buttons.push_back({icon::select, STR_C("Select"), nullptr, STR_C("LMB picks the models, as in the surroundings"), false == m_terrain_sculpt && false == m_chunk_edit, choose(false, m_terrain_brush_smooth, false)}); + Buttons.push_back({icon::sculpt, STR_C("Sculpt"), nullptr, STR_C("LMB raises the terrain under the brush, Shift+LMB lowers it"), m_terrain_sculpt && false == m_terrain_brush_smooth, choose(true, false, false)}); + Buttons.push_back({icon::smooth, STR_C("Smooth"), nullptr, STR_C("LMB evens the terrain out under the brush"), m_terrain_sculpt && m_terrain_brush_smooth, choose(true, true, false)}); + Buttons.push_back({icon::chunks, STR_C("Chunks"), nullptr, STR_C("LMB adds a chunk next to the clicked one, Shift+LMB deletes it"), m_chunk_edit, choose(false, m_terrain_brush_smooth, true)}); auto const orthophoto{m_orthophoto.enabled()}; - buttons.push_back({icon::orthophoto, STR_C("Orthophoto"), nullptr, STR_C("Aerial imagery of geoportal.gov.pl under the scenery, laid out by the origin of the scenery"), orthophoto, + Buttons.push_back({icon::orthophoto, STR_C("Orthophoto"), nullptr, STR_C("Aerial imagery of geoportal.gov.pl under the scenery, laid out by the origin of the scenery"), orthophoto, [this, orthophoto]() { m_orthophoto.enabled(false == orthophoto); }, true}); + Buttons.push_back({icon::count, STR_C("Orthophoto settings..."), nullptr, nullptr, m_orthophoto_window, [this]() { m_orthophoto_window = !m_orthophoto_window; }}); break; } } +} + +void editor_mode::render_viewport_tools() +{ + // the tools of the field of work which have icons, as in the toolbar + std::vector buttons; + buttons.reserve(24); + work_area_tools(buttons); + std::erase_if(buttons, [](tool_button const &Button) { return Button.picture == icon::count; }); if (buttons.empty()) return; @@ -244,6 +266,122 @@ void editor_mode::render_viewport_tools() ImGui::End(); } +void editor_mode::render_toolbar_caption(char const *Text) +{ + // as wide in both rows, so the buttons of the rows start one under the other. the rows are drawn with no spacing of items + auto const margin{std::round(ImGui::GetFontSize() * 0.75f)}; + auto const width{std::max(ImGui::CalcTextSize(STR_C("Work modes")).x, ImGui::CalcTextSize(STR_C("Tools")).x) + 2.0f * margin}; + auto const position{ImGui::GetWindowPos()}; + auto const height{ImGui::GetWindowHeight()}; + auto *list{ImGui::GetWindowDrawList()}; + auto const text{ImGui::CalcTextSize(Text)}; + list->AddText(ImVec2(position.x + margin, std::round(position.y + (height - text.y) * 0.5f)), ImGui::GetColorU32(ImGuiCol_TextDisabled), Text); + list->AddLine(ImVec2(position.x + width, position.y + height * 0.2f), ImVec2(position.x + width, position.y + height * 0.8f), ImGui::GetColorU32(ImGuiCol_Separator)); + ImGui::SetCursorScreenPos(position); + ImGui::Dummy(ImVec2(width + margin * 0.5f, height)); + ImGui::SameLine(0.0f, 0.0f); +} + +void editor_mode::render_toolbar_tools() +{ + render_toolbar_caption(STR_C("Tools")); + std::vector buttons; + buttons.reserve(24); + work_area_tools(buttons); + if (buttons.empty()) + return; + + auto const s{scale()}; + auto const iconside{editor_icons::size()}; + auto const height{ImGui::GetWindowHeight()}; + auto const margin{3.0f * s}; // over and under the buttons + auto const pad{7.0f * s}; // inside the button, at its ends + auto const spacing{5.0f * s}; // between the icon and the name + auto const gap{3.0f * s}; + auto const groupgap{15.0f * s}; + auto const buttonheight{height - 2.0f * margin}; + auto const left{ImGui::GetCursorScreenPos().x}; + auto const top{ImGui::GetWindowPos().y + margin}; + auto const right{ImGui::GetWindowPos().x + ImGui::GetWindowWidth() - 4.0f * s}; + // the names next to the icons if there's room for them; if there isn't, the name of the chosen tool only, or none + enum class names + { + all, + chosen, + none + }; + auto const named = [](tool_button const &Button, names const Names) { + return Button.picture == icon::count || Names == names::all || (Names == names::chosen && Button.chosen); + }; + auto const width = [&](tool_button const &Button, names const Names) { + if (false == named(Button, Names)) + return std::max(buttonheight, iconside + pad); + auto result{2.0f * pad + ImGui::CalcTextSize(Button.label).x}; + if (Button.picture != icon::count) + result += iconside + spacing; + return result; + }; + auto const total = [&](names const Names) { + auto result{0.0f}; + for (std::size_t index = 0; index < buttons.size(); ++index) + result += width(buttons[index], Names) + (index == 0 ? 0.0f : buttons[index].group ? groupgap : gap); + return result; + }; + auto shown{names::none}; + for (auto const option : {names::all, names::chosen}) + if (left + total(option) <= right) + { + shown = option; + break; + } + + auto *list{ImGui::GetWindowDrawList()}; + auto const rounding{3.0f * s}; + auto x{left}; + for (std::size_t index = 0; index < buttons.size(); ++index) + { + auto const &button{buttons[index]}; + if (index > 0) + { + x += button.group ? groupgap : gap; + if (button.group) + list->AddLine(ImVec2(std::round(x - groupgap * 0.5f), top + buttonheight * 0.15f), ImVec2(std::round(x - groupgap * 0.5f), top + buttonheight * 0.85f), ImGui::GetColorU32(ImGuiCol_Separator)); + } + auto const size{ImVec2(width(button, shown), buttonheight)}; + ImVec2 const position{x, top}; + ImGui::SetCursorScreenPos(position); + ImGui::PushID(static_cast(index)); + auto const clicked{ImGui::InvisibleButton("##tool", size)}; + ImGui::PopID(); + auto const hovered{ImGui::IsItemHovered()}; + ImVec2 const end{position.x + size.x, position.y + size.y}; + // the chosen one stands out as the chosen field of work does, the others as buttons + auto const background{button.chosen ? ImGui::GetColorU32(ImGuiCol_Header) : hovered && button.enabled ? ImGui::GetColorU32(ImGuiCol_HeaderHovered) : ImGui::GetColorU32(ImGuiCol_FrameBg)}; + list->AddRectFilled(position, end, background, rounding); + if (button.chosen) + list->AddRectFilled(ImVec2(position.x, end.y - 2.0f * s), end, ImGui::GetColorU32(ImGuiCol_CheckMark), rounding, ImDrawFlags_RoundCornersBottom); + auto const color{button.enabled ? ImGui::GetColorU32(ImGuiCol_Text, button.chosen || hovered ? 1.0f : 0.85f) : ImGui::GetColorU32(ImGuiCol_TextDisabled)}; + auto const center{position.y + size.y * 0.5f}; + if (named(button, shown)) + { + auto textx{position.x + pad}; + if (button.picture != icon::count) + { + editor_icons::draw(list, button.picture, ImVec2(textx + iconside * 0.5f, center), color); + textx += iconside + spacing; + } + list->AddText(ImVec2(textx, std::round(center - ImGui::GetFontSize() * 0.5f)), color, button.label); + } + else + icon_or_letters(list, button, ImVec2(position.x + size.x * 0.5f, center), color); + if (hovered) + button_tooltip(button); + if (clicked && button.enabled && button.action) + button.action(); + x = end.x; + } +} + void editor_mode::render_viewport_gizmo() { // the gizmo moves the models and the tracks; on the terrain the mouse is the brush's diff --git a/application/editoruilayer.cpp b/application/editoruilayer.cpp index ee203dda..7a7c4332 100644 --- a/application/editoruilayer.cpp +++ b/application/editoruilayer.cpp @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "utilities/translation.h" #include "imgui/imgui_internal.h" #include "editor/editorSettings.hpp" +#include "editor/editorIcons.hpp" #include #include @@ -602,16 +603,16 @@ void editor_ui::render_() ImGui::End(); ImGui::PopStyleVar(2); } - // toolbar under them: the tools of the field of work - if (ImGui::BeginViewportSideBar("##editortoolbar", viewport, ImGuiDir_Up, ImGui::GetFrameHeight(), flags)) - { - if (ImGui::BeginMenuBar()) - { - if (m_toolbaroptions) - m_toolbaroptions(); - ImGui::EndMenuBar(); - } - } + // toolbar under them: the tools of the field of work, as buttons with their icons, high enough for them. + // the buttons are laid out by hand, the padding is given back for their tooltips + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_MenuBarBg)); + auto const toolbarheight{std::max(ImGui::GetFrameHeight(), editor_icons::size() + 12.0f * std::max(1.0f, Global.ui_scale))}; + auto const toolbar{ImGui::BeginViewportSideBar("##editortoolbar", viewport, ImGuiDir_Up, toolbarheight, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollWithMouse)}; + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + if (toolbar && m_toolbaroptions) + m_toolbaroptions(); ImGui::End(); render_tool_options(); render_inspector(); @@ -1094,4 +1095,4 @@ float editor_ui::rot_val() bool editor_ui::rot_from_last() { return m_functionspanel.rot_from_last; -} \ No newline at end of file +} \ No newline at end of file diff --git a/editor/editorIcons.hpp b/editor/editorIcons.hpp index 3734d465..91c9a5f3 100644 --- a/editor/editorIcons.hpp +++ b/editor/editorIcons.hpp @@ -50,6 +50,11 @@ enum class icon infra, gauge, turntable, + // the fields of work + work_surroundings, + work_tracks, + work_roads, + work_terrain, count }; diff --git a/editor/icons/editor_icons.png b/editor/icons/editor_icons.png index 80b7013b..50878092 100644 Binary files a/editor/icons/editor_icons.png and b/editor/icons/editor_icons.png differ diff --git a/editor/icons/editor_icons.py b/editor/icons/editor_icons.py index 9b035485..9ef66aa1 100644 --- a/editor/icons/editor_icons.py +++ b/editor/icons/editor_icons.py @@ -321,10 +321,53 @@ def turntable(d): line(d, [(32 + math.cos(r) * 24, 32 + math.sin(r) * 24), (32 + math.cos(r) * 31, 32 + math.sin(r) * 31)], W - 4) +# the fields of work of the editor, in the row over the toolbar + + +def work_surroundings(d): + # a tree and a house by it + poly(d, [(18, 4), (32, 30), (4, 30)]) + poly(d, [(18, 14), (34, 44), (2, 44)]) + line(d, [(18, 44), (18, 60)], W - 2) + poly(d, [(36, 34), (49, 22), (62, 34)]) + rect(d, 39, 34, 59, 60) + + +def work_tracks(d): + # the track going away, rails on sleepers + line(d, [(8, 60), (26, 4)], W - 4) + line(d, [(56, 60), (38, 4)], W - 4) + for y, half in ((54, 28), (40, 22), (28, 17), (18, 13), (10, 10)): + line(d, [(32 - half, y), (32 + half, y)], W - 8 if y > 20 else W - 10) + + +def work_roads(d): + # a car seen from its side + poly(d, [(4, 44), (4, 34), (14, 30), (22, 18), (44, 18), (52, 30), (60, 34), (60, 44)]) + circle(d, 17, 46, 8) + circle(d, 47, 46, 8) + + +def work_terrain(d): + # the mesh of the ground over a hill + def at(u, v): + # u across 0..1, v from the back (0) to the front (1) + half = 18 + 12 * v + x = 32 + (u - 0.5) * 2 * half + hill = 18 * math.exp(-((u - 0.5) ** 2) / 0.07 - ((v - 0.35) ** 2) / 0.15) + return (x, 24 + 34 * v - hill) + steps = [i / 12 for i in range(13)] + for v in (0.0, 0.33, 0.66, 1.0): + line(d, [at(u, v) for u in steps], W - 6) + for u in (0.0, 0.33, 0.66, 1.0): + line(d, [at(u, v) for v in steps], W - 6) + + ICONS = [select, insert, brush, area_fill, copy_to_bank, translate, rotate, scale, local_space, ortho, perspective, sculpt, smooth, chunks, orthophoto, road, place, lanes, lay_track, switch, straight, curve, signal, objects, - vehicle, profile, speed, joints, infra, gauge, turntable] + vehicle, profile, speed, joints, infra, gauge, turntable, work_surroundings, + work_tracks, work_roads, work_terrain] def cutouts(name, d): @@ -341,6 +384,18 @@ def cutouts(name, d): rect(d, 10, 27, 20, 35) rect(d, 26, 27, 36, 35) rect(d, 42, 27, 50, 35) + elif name == "work_surroundings": + # the door and the gap between the crowns of the tree + rect(d, 46, 46, 52, 60) + line(d, [(9, 31), (27, 31)], 8) + elif name == "work_roads": + # the windows, and the gaps around the wheels + poly(d, [(25, 22), (31, 22), (31, 30), (19, 30)]) + poly(d, [(35, 22), (42, 22), (47, 30), (35, 30)]) + circle(d, 17, 46, 11, outline=True, width=12) + circle(d, 47, 46, 11, outline=True, width=12) + circle(d, 17, 46, 3) + circle(d, 47, 46, 3) def main(): diff --git a/lang/editor_pl.po b/lang/editor_pl.po index b043f4e2..442756c6 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -5061,3 +5061,23 @@ msgstr "Upuść go na zestaw modeli albo na listę pędzla lub wypełniania obsz msgid "or drag models here from the node bank" msgstr "albo przeciągnij tu modele z banku obiektów" + +# scenery editor: the toolbar with icons, the rows captioned + +msgid "Work modes" +msgstr "Tryby pracy" + +msgid "Tools" +msgstr "Narzędzia" + +msgid "Level crossing" +msgstr "Przejazd kolejowy" + +msgid "Spawn point" +msgstr "Punkt pojawiania" + +msgid "Removal point" +msgstr "Punkt znikania" + +msgid "Pedestrian crossing" +msgstr "Przejście dla pieszych"