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

feat(editor): the toolbar as buttons with icons and names, the chosen tool standing out as the chosen field of work does, the names left out when the row is too narrow for them; the fields of work with icons of their own (surroundings, tracks, roads, terrain) and both rows captioned at their left end (work modes, tools); the tools of the toolbar and of the left edge of the 3d view from one list; Polish translation

This commit is contained in:
2026-10-09 12:49:45 +02:00
parent 1bf687b4c9
commit 3e3317d26f
10 changed files with 299 additions and 203 deletions

View File

@@ -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<int>(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<char const *, nodebank_panel::edit_mode> 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<int>(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()

View File

@@ -38,6 +38,8 @@ http://mozilla.org/MPL/2.0/.
#include <vector>
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<editor_tool_button> &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);

View File

@@ -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<int>(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<int>(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()
{

View File

@@ -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<int>(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)};

View File

@@ -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 <functional>
#include <vector>
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<tool_button> &Buttons)
{
// the tools of the field of work, as in the toolbar
std::vector<tool_button> 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<int>(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<tool_button> 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<tool_button> 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<int>(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

View File

@@ -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 <cstdio>
#include <limits>
@@ -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;
}
}