mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-20 13:54:53 +02:00
Labels: - every button, field, tooltip and message of the editor reread for a beginner: short nouns for the fields with the sentence in the tooltip, the less used options under More settings, one glossary (field of work, template, track, grade), sides told from directions, degrees and per mille as signs, messages built from one format string - plural forms through STR_P; the last entry of a po file is read too, and the Polish plural rule is in the header - lang/editor_pl.po: the code pass entries merged, 391 entries no code uses and 3 duplicates dropped - roads: the notes and errors naming a road translated; the build options and the layout fold the texture length, kerb sizes, traffic share, roundabout part, height over the ground and junction speed limit under More settings, the lanes start folded Grade lines fitted to the terrain model (NMT), a line or the whole scenery at once: - fit_ground: a robust fit on nodes 5 m apart; the ground parting from the line (a bridge, a viaduct, a wall) is gone past rather than followed, the bends keep off the switches as far as their curves reach, a run of bends the same way is one point of intersection, a curve with no room is as sharp as the room lets it be - the ends of the line and the other paths of its switches leading into lines fitted already bound the line by what the tracks between can climb; ends meeting in the plan, laid apart in height by hand, are joined before the run - apply_profile: the paths are split where split_path takes the parameter (a straight was cut in the wrong place, into pieces of a few centimetres the game takes for zero length), no piece shorter than the margin; the paths off the ends of the route are brought to its height - Bytom: 1600 lines, grade breaks at the joints 89 -> 20; Tarnowskie Gory: 848 lines, 223 -> 3; no step over 2 cm
767 lines
24 KiB
C++
767 lines
24 KiB
C++
/*
|
||
This Source Code Form is subject to the
|
||
terms of the Mozilla Public License, v.
|
||
2.0. If a copy of the MPL was not
|
||
distributed with this file, You can
|
||
obtain one at
|
||
http://mozilla.org/MPL/2.0/.
|
||
*/
|
||
|
||
#include "stdafx.h"
|
||
#include "application/uilayer.h"
|
||
|
||
#include <utility>
|
||
|
||
#include "utilities/Globals.h"
|
||
#include "rendering/renderer.h"
|
||
#include "utilities/Logs.h"
|
||
#include "simulation/simulation.h"
|
||
#include "utilities/translation.h"
|
||
#include "application/application.h"
|
||
#include "application/editormode.h"
|
||
#include "application/editoruilayer.h"
|
||
|
||
#include "imgui/imgui_impl_glfw.h"
|
||
#include "imgui/imgui_internal.h"
|
||
|
||
GLFWwindow *ui_layer::m_window{nullptr};
|
||
GLFWwindow *ui_layer::m_keywindow{nullptr};
|
||
ImGuiIO *ui_layer::m_imguiio{nullptr};
|
||
GLint ui_layer::m_textureunit{GL_TEXTURE0};
|
||
bool ui_layer::m_cursorvisible;
|
||
int ui_layer::m_palette{0};
|
||
ImFont *ui_layer::font_default{nullptr};
|
||
ImFont *ui_layer::font_mono{nullptr};
|
||
ImFont *ui_layer::font_loading{nullptr};
|
||
ImFont *ui_layer::font_bold{nullptr};
|
||
|
||
ui_panel::ui_panel(std::string Identifier, bool const Isopen) : is_open(Isopen), m_name(std::move(Identifier)) {}
|
||
|
||
void ui_panel::render()
|
||
{
|
||
if (false == is_open)
|
||
return;
|
||
|
||
int flags = window_flags;
|
||
if (flags == -1)
|
||
flags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoCollapse;
|
||
if (size.x > 0)
|
||
flags |= ImGuiWindowFlags_NoResize;
|
||
if (no_title_bar)
|
||
flags |= ImGuiWindowFlags_NoTitleBar;
|
||
|
||
if (pos.x != -1 && pos.y != -1)
|
||
ImGui::SetNextWindowPos(ImVec2(pos.x, pos.y), ImGuiCond_Always);
|
||
if (size.x > 0)
|
||
ImGui::SetNextWindowSize(ImVec2S(size.x, size.y), ImGuiCond_Always);
|
||
else if (size_min.x == -1)
|
||
ImGui::SetNextWindowSize(ImVec2(0, 0), ImGuiCond_FirstUseEver);
|
||
|
||
if (size_min.x > 0)
|
||
ImGui::SetNextWindowSizeConstraints(ImVec2S(size_min.x, size_min.y), ImVec2S(size_max.x, size_max.y));
|
||
|
||
auto const panelname{(title.empty() ? m_name : title) + "###" + m_name};
|
||
if (ImGui::Begin(panelname.c_str(), &is_open, flags))
|
||
{
|
||
render_contents();
|
||
|
||
popups.remove_if([](const std::unique_ptr<ui::popup> &popup) { return popup->render(); });
|
||
}
|
||
|
||
ImGui::End();
|
||
}
|
||
|
||
void ui_panel::render_contents()
|
||
{
|
||
for (auto const &line : text_lines)
|
||
{
|
||
ImGui::TextColored(ImVec4(line.color.r, line.color.g, line.color.b, line.color.a), line.data.c_str());
|
||
}
|
||
}
|
||
|
||
void ui_panel::register_popup(std::unique_ptr<ui::popup> &&popup)
|
||
{
|
||
popups.push_back(std::move(popup));
|
||
}
|
||
|
||
void ui_expandable_panel::render_contents()
|
||
{
|
||
ImGui::Checkbox(STR_C("expand"), &is_expanded);
|
||
ui_panel::render_contents();
|
||
}
|
||
|
||
void ui_log_panel::render_contents()
|
||
{
|
||
ImGui::PushFont(ui_layer::font_mono);
|
||
|
||
for (const std::string &s : log_scrollback)
|
||
ImGui::TextUnformatted(s.c_str());
|
||
if (ImGui::GetScrollY() == ImGui::GetScrollMaxY())
|
||
ImGui::SetScrollHereY(1.0f);
|
||
|
||
ImGui::PopFont();
|
||
}
|
||
|
||
ui_layer::ui_layer()
|
||
{
|
||
if (Global.loading_log)
|
||
add_external_panel(&m_logpanel);
|
||
m_logpanel.size = {700, 400};
|
||
}
|
||
|
||
ui_layer::~ui_layer() {}
|
||
|
||
bool ui_layer::key_callback(int key, int scancode, int action, int mods)
|
||
{
|
||
ImGui_ImplGlfw_KeyCallback(m_keywindow != nullptr ? m_keywindow : m_window, key, scancode, action, mods);
|
||
return m_imguiio->WantCaptureKeyboard;
|
||
}
|
||
|
||
bool ui_layer::char_callback(unsigned int c)
|
||
{
|
||
ImGui_ImplGlfw_CharCallback(m_window, c);
|
||
return m_imguiio->WantCaptureKeyboard;
|
||
}
|
||
|
||
bool ui_layer::scroll_callback(double xoffset, double yoffset)
|
||
{
|
||
ImGui_ImplGlfw_ScrollCallback(m_window, xoffset, yoffset);
|
||
// a line a second in the log of where a scroll went, for a wheel or a touchpad that seems to do nothing
|
||
static double logged{-10.0};
|
||
if (auto const now{glfwGetTime()}; now - logged > 1.0)
|
||
{
|
||
logged = now;
|
||
auto const *hovered{ImGui::GetCurrentContext() != nullptr ? ImGui::GetCurrentContext()->HoveredWindow : nullptr};
|
||
WriteLog("scroll " + std::to_string(xoffset) + " " + std::to_string(yoffset) + ", taken by the ui: " + (m_imguiio->WantCaptureMouse ? "yes" : "no") + ", over: " + (hovered != nullptr ? hovered->Name : "the view"));
|
||
}
|
||
return m_imguiio->WantCaptureMouse;
|
||
}
|
||
|
||
bool ui_layer::mouse_button_callback(int button, int action, int mods)
|
||
{
|
||
ImGui_ImplGlfw_MouseButtonCallback(m_window, button, action, mods);
|
||
return m_imguiio->WantCaptureMouse;
|
||
}
|
||
|
||
// the glfw backend of imgui works with events since 1.87: position, hover and focus of the window are passed to it as they come
|
||
void ui_layer::cursor_pos_callback(double x, double y)
|
||
{
|
||
if (ImGui::GetCurrentContext() == nullptr)
|
||
return;
|
||
ImGui_ImplGlfw_CursorPosCallback(m_window, x, y);
|
||
}
|
||
|
||
void ui_layer::cursor_enter_callback(int entered)
|
||
{
|
||
if (ImGui::GetCurrentContext() == nullptr)
|
||
return;
|
||
ImGui_ImplGlfw_CursorEnterCallback(m_window, entered);
|
||
}
|
||
|
||
void ui_layer::focus_callback(int focused)
|
||
{
|
||
if (ImGui::GetCurrentContext() == nullptr)
|
||
return;
|
||
ImGui_ImplGlfw_WindowFocusCallback(m_window, focused);
|
||
}
|
||
|
||
void ui_layer::viewport_key_callback(GLFWwindow *Window, int key, int scancode, int action, int mods)
|
||
{
|
||
// imgui gets the key with the window it came from (the state of modifiers is read from it), the simulator gets it unless imgui takes it
|
||
m_keywindow = Window;
|
||
Application.on_key(key, scancode, action, mods);
|
||
m_keywindow = nullptr;
|
||
}
|
||
|
||
namespace
|
||
{
|
||
// window creation of the glfw backend, wrapped to give the new windows the key callback of the simulator
|
||
void (*imgui_create_window)(ImGuiViewport *Viewport){nullptr};
|
||
|
||
void create_viewport_window(ImGuiViewport *Viewport)
|
||
{
|
||
imgui_create_window(Viewport);
|
||
if (Viewport->PlatformHandle != nullptr)
|
||
glfwSetKeyCallback(static_cast<GLFWwindow *>(Viewport->PlatformHandle), ui_layer::viewport_key_callback);
|
||
}
|
||
} // namespace
|
||
|
||
void ui_layer::imgui_colors(int const Palette)
|
||
{
|
||
m_palette = Palette;
|
||
auto const rgb = [](int const Color, float const Alpha = 1.0f) {
|
||
return ImVec4(((Color >> 16) & 0xff) / 255.0f, ((Color >> 8) & 0xff) / 255.0f, (Color & 0xff) / 255.0f, Alpha);
|
||
};
|
||
auto const editor{Palette == 1};
|
||
// palette shared with the MaSzyna starter: flat dark panels, thin borders, green accents; the one of the editor
|
||
// has amber accents on cooler grey panels
|
||
int const panel{editor ? 0x1f2226 : 0x1e2327}; // window background
|
||
int const base{editor ? 0x1b1d21 : 0x15171b}; // darker background, between panels
|
||
int const header{editor ? 0x2c3036 : 0x2a3036}; // section headers, hovered items
|
||
int const border{editor ? 0x3a3f47 : 0x3a424a};
|
||
int const field{editor ? 0x15171a : 0x121517}; // input fields, combos
|
||
int const accent{editor ? 0xb07a1c : 0x177f00}; // selection, active elements
|
||
int const accentbright{editor ? 0xe5a73a : 0x41c400}; // marks: checkmarks, active tab underline
|
||
int const accentdim{editor ? 0x5a4319 : 0x164b0e};
|
||
|
||
ImVec4 *colors = ImGui::GetStyle().Colors;
|
||
|
||
colors[ImGuiCol_Text] = rgb(editor ? 0xe6e8eb : 0xffffff);
|
||
colors[ImGuiCol_TextDisabled] = rgb(editor ? 0x8a929c : 0xa0a0a2);
|
||
colors[ImGuiCol_WindowBg] = rgb(panel, Global.UIBgOpacity); // ui.bg.opacity from config file
|
||
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||
colors[ImGuiCol_PopupBg] = rgb(base, 0.98f);
|
||
colors[ImGuiCol_Border] = rgb(border);
|
||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||
colors[ImGuiCol_FrameBg] = rgb(field);
|
||
colors[ImGuiCol_FrameBgHovered] = rgb(header);
|
||
colors[ImGuiCol_FrameBgActive] = rgb(border);
|
||
colors[ImGuiCol_TitleBg] = rgb(base);
|
||
colors[ImGuiCol_TitleBgActive] = rgb(header);
|
||
colors[ImGuiCol_TitleBgCollapsed] = rgb(base, 0.75f);
|
||
colors[ImGuiCol_MenuBarBg] = rgb(panel);
|
||
colors[ImGuiCol_ScrollbarBg] = rgb(base, 0.60f);
|
||
colors[ImGuiCol_ScrollbarGrab] = rgb(border);
|
||
colors[ImGuiCol_ScrollbarGrabHovered] = rgb(0x5a636c);
|
||
colors[ImGuiCol_ScrollbarGrabActive] = rgb(0x858585);
|
||
colors[ImGuiCol_CheckMark] = rgb(accentbright);
|
||
colors[ImGuiCol_SliderGrab] = rgb(accent);
|
||
colors[ImGuiCol_SliderGrabActive] = rgb(accentbright);
|
||
colors[ImGuiCol_Button] = rgb(header);
|
||
colors[ImGuiCol_ButtonHovered] = rgb(border);
|
||
colors[ImGuiCol_ButtonActive] = rgb(accent);
|
||
// shared by section headers and selected list items
|
||
colors[ImGuiCol_Header] = rgb(header);
|
||
colors[ImGuiCol_HeaderHovered] = rgb(accentdim);
|
||
colors[ImGuiCol_HeaderActive] = rgb(accent);
|
||
colors[ImGuiCol_Separator] = rgb(border);
|
||
colors[ImGuiCol_SeparatorHovered] = rgb(accent);
|
||
colors[ImGuiCol_SeparatorActive] = rgb(accentbright);
|
||
colors[ImGuiCol_ResizeGrip] = rgb(border, 0.50f);
|
||
colors[ImGuiCol_ResizeGripHovered] = rgb(accent);
|
||
colors[ImGuiCol_ResizeGripActive] = rgb(accentbright);
|
||
colors[ImGuiCol_Tab] = rgb(header);
|
||
colors[ImGuiCol_TabHovered] = rgb(border);
|
||
colors[ImGuiCol_TabActive] = rgb(accent); // also the line under the tab bar
|
||
colors[ImGuiCol_TabUnfocused] = rgb(header);
|
||
colors[ImGuiCol_TabUnfocusedActive] = rgb(accentdim);
|
||
// imgui 1.90+ marks the selected tab with a line over it as well, in the same colours
|
||
colors[ImGuiCol_TabSelectedOverline] = rgb(accent);
|
||
colors[ImGuiCol_TabDimmedSelectedOverline] = rgb(accentdim);
|
||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||
colors[ImGuiCol_TextSelectedBg] = rgb(accent, 0.60f);
|
||
colors[ImGuiCol_DragDropTarget] = rgb(accentbright, 0.90f);
|
||
colors[ImGuiCol_NavHighlight] = rgb(accentbright);
|
||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||
colors[ImGuiCol_ModalWindowDimBg] = rgb(base, 0.60f);
|
||
// where a dragged window would be docked, and an empty dock node
|
||
colors[ImGuiCol_DockingPreview] = rgb(accentbright, 0.70f);
|
||
colors[ImGuiCol_DockingEmptyBg] = rgb(base);
|
||
}
|
||
|
||
void ui_layer::imgui_style()
|
||
{
|
||
imgui_colors(0);
|
||
|
||
// flat, square elements with thin borders
|
||
auto &style = ImGui::GetStyle();
|
||
style.WindowRounding = 0.0f;
|
||
style.ChildRounding = 0.0f;
|
||
style.FrameRounding = 0.0f;
|
||
style.PopupRounding = 0.0f;
|
||
style.ScrollbarRounding = 0.0f;
|
||
style.GrabRounding = 0.0f;
|
||
style.TabRounding = 0.0f;
|
||
style.WindowBorderSize = 1.0f;
|
||
style.ChildBorderSize = 1.0f;
|
||
style.PopupBorderSize = 1.0f;
|
||
style.FrameBorderSize = 1.0f;
|
||
style.TabBorderSize = 0.0f;
|
||
|
||
style.ScaleAllSizes(Global.ui_scale);
|
||
}
|
||
|
||
bool ui_layer::init(GLFWwindow *Window)
|
||
{
|
||
m_window = Window;
|
||
|
||
IMGUI_CHECKVERSION();
|
||
ImGui::CreateContext();
|
||
m_imguiio = &ImGui::GetIO();
|
||
// imgui.ini is read with the first frame, which may be of another mode than the editor
|
||
editor_ui::register_settings();
|
||
|
||
m_imguiio->ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
|
||
// m_imguiio->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||
// m_imguiio->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||
|
||
static const ImWchar ranges[] =
|
||
{
|
||
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||
0x0100, 0x017F, // Latin Extended-A
|
||
0x2070, 0x2079, // superscript
|
||
0x2500, 0x256C, // box drawings
|
||
0,
|
||
};
|
||
|
||
ImFontConfig config;
|
||
// config.FontDataOwnedByAtlas = false; // Avoid duplicate release of font data
|
||
|
||
if (FileExists("fonts/dejavusans.ttf")) {
|
||
// Load basic font first
|
||
font_default = m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusans.ttf", Global.ui_fontsize, &config, &ranges[0]);
|
||
|
||
// Add support for Chinese character ranges
|
||
if (font_default) {
|
||
ImFontConfig chinese_config;
|
||
chinese_config.MergeMode = true;
|
||
chinese_config.PixelSnapH = true;
|
||
chinese_config.OversampleH = 1;
|
||
chinese_config.OversampleV = 1;
|
||
// Use NotoSansSC-Regular.ttf font file
|
||
if (FileExists("fonts/NotoSansSC-Regular.ttf")) {
|
||
const ImWchar* chinese_ranges = m_imguiio->Fonts->GetGlyphRangesChineseFull();
|
||
m_imguiio->Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.ttf", Global.ui_fontsize, &chinese_config, chinese_ranges);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (FileExists("fonts/dejavusansmono.ttf")) {
|
||
ImFontConfig mono_config;
|
||
mono_config.GlyphRanges = &ranges[0]; // Basic Latin characters
|
||
font_mono = m_imguiio->Fonts->AddFontFromFileTTF("fonts/dejavusansmono.ttf", Global.ui_fontsize, &mono_config, &ranges[0]);
|
||
|
||
// Add Chinese character support for monospace font as well
|
||
if (font_mono) {
|
||
ImFontConfig chinese_mono_config;
|
||
chinese_mono_config.OversampleH = 1;
|
||
chinese_mono_config.OversampleV = 1;
|
||
chinese_mono_config.MergeMode = true;
|
||
chinese_mono_config.PixelSnapH = true;
|
||
|
||
// Use NotoSansSC-Regular.ttf for monospace font as well
|
||
if (FileExists("fonts/NotoSansSC-Regular.ttf")) {
|
||
const ImWchar* chinese_ranges = m_imguiio->Fonts->GetGlyphRangesChineseFull();
|
||
m_imguiio->Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.ttf", Global.ui_fontsize, &chinese_mono_config, chinese_ranges);
|
||
}
|
||
}
|
||
}
|
||
for (auto const *bold : {"fonts/dejavusans-bold.ttf", "fonts/DejaVuSans-Bold.ttf"})
|
||
{
|
||
if (font_bold == nullptr && FileExists(bold))
|
||
{
|
||
ImFontConfig bold_config;
|
||
font_bold = m_imguiio->Fonts->AddFontFromFileTTF(bold, Global.ui_fontsize * 1.25f, &bold_config, &ranges[0]);
|
||
}
|
||
}
|
||
if (FileExists("fonts/bahnschrift.ttf")) {
|
||
ImFontConfig loading_config;
|
||
font_loading = m_imguiio->Fonts->AddFontFromFileTTF("fonts/bahnschrift.ttf", 48, &loading_config, &ranges[0]);
|
||
|
||
// Add Chinese character support for loading font as well
|
||
if (font_loading) {
|
||
ImFontConfig chinese_loading_config;
|
||
chinese_loading_config.OversampleH = 1;
|
||
chinese_loading_config.OversampleV = 1;
|
||
chinese_loading_config.MergeMode = true;
|
||
chinese_loading_config.PixelSnapH = true;
|
||
|
||
// Use NotoSansMonoCJKsc-Regular.ttf for loading font
|
||
if (FileExists("fonts/NotoSansMonoCJKsc-Regular.ttf")) {
|
||
const ImWchar* chinese_ranges = m_imguiio->Fonts->GetGlyphRangesChineseFull();
|
||
m_imguiio->Fonts->AddFontFromFileTTF("fonts/NotoSansMonoCJKsc-Regular.ttf", 48, &chinese_loading_config, chinese_ranges);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!font_default && !font_mono)
|
||
font_default = font_mono = m_imguiio->Fonts->AddFontDefault();
|
||
else if (!font_default)
|
||
font_default = font_mono;
|
||
else if (!font_mono)
|
||
font_mono = font_default;
|
||
if (!font_loading)
|
||
font_loading = font_default;
|
||
|
||
imgui_style();
|
||
|
||
ImGui_ImplGlfw_InitForOpenGL(m_window, false);
|
||
|
||
if (!GfxRenderer->GetImguiRenderer() || !GfxRenderer->GetImguiRenderer()->Init())
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// panels can be docked together, and dragged out of the simulator window into windows of their own (e.g. on another monitor)
|
||
// when both backends can do it. glfw can't place windows under wayland. both flags have to be set before the first frame
|
||
m_imguiio->ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||
auto const viewportsupport{(m_imguiio->BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (m_imguiio->BackendFlags & ImGuiBackendFlags_RendererHasViewports) &&
|
||
glfwGetPlatform() != GLFW_PLATFORM_WAYLAND};
|
||
if (Global.ui_viewports && viewportsupport)
|
||
{
|
||
m_imguiio->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
||
auto &platformio{ImGui::GetPlatformIO()};
|
||
imgui_create_window = platformio.Platform_CreateWindow;
|
||
platformio.Platform_CreateWindow = create_viewport_window;
|
||
}
|
||
WriteLog(std::string("ui: panels in separate windows ") + ((m_imguiio->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ? "enabled" : (viewportsupport ? "disabled in settings" : "not supported here")));
|
||
|
||
return true;
|
||
}
|
||
|
||
void ui_layer::shutdown()
|
||
{
|
||
ImGui::EndFrame();
|
||
|
||
GfxRenderer->GetImguiRenderer()->Shutdown();
|
||
ImGui_ImplGlfw_Shutdown();
|
||
ImGui::DestroyContext();
|
||
}
|
||
|
||
bool ui_layer::on_key(int const Key, int const Action)
|
||
{
|
||
if (Action == GLFW_PRESS)
|
||
{
|
||
if (Key == GLFW_KEY_PRINT_SCREEN)
|
||
{
|
||
Application.queue_screenshot();
|
||
return true;
|
||
}
|
||
|
||
if (Key == GLFW_KEY_F9)
|
||
{
|
||
m_logpanel.is_open = !m_logpanel.is_open;
|
||
return true;
|
||
}
|
||
|
||
if (Key == GLFW_KEY_F10)
|
||
{
|
||
m_quit_active = !m_quit_active;
|
||
return true;
|
||
}
|
||
|
||
if (m_quit_active)
|
||
{
|
||
if (Key == GLFW_KEY_Y)
|
||
{
|
||
Application.queue_quit(false);
|
||
return true;
|
||
}
|
||
else if (Key == GLFW_KEY_N)
|
||
{
|
||
m_quit_active = false;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool ui_layer::on_cursor_pos(double const Horizontal, double const Vertical)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
bool ui_layer::on_mouse_button(int const Button, int const Action)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
void ui_layer::on_window_resize(int w, int h)
|
||
{
|
||
for (auto *panel : m_panels)
|
||
panel->on_window_resize(w, h);
|
||
}
|
||
|
||
void ui_layer::update()
|
||
{
|
||
for (auto *panel : m_panels)
|
||
panel->update();
|
||
|
||
for (auto it = m_ownedpanels.rbegin(); it != m_ownedpanels.rend(); it++)
|
||
{
|
||
(*it)->update();
|
||
if (!(*it)->is_open)
|
||
m_ownedpanels.erase(std::next(it).base());
|
||
}
|
||
}
|
||
|
||
void ui_layer::render()
|
||
{
|
||
// the frame may have been started by the ui of another mode, when the mode changes; the windows docked in the dockspace
|
||
// of this one would come undocked if they were drawn in a frame without it
|
||
if (m_dockspaceframe != ImGui::GetFrameCount())
|
||
{
|
||
render_dockspace();
|
||
m_dockspaceframe = ImGui::GetFrameCount();
|
||
}
|
||
render_background();
|
||
render_panels();
|
||
render_tooltip();
|
||
render_menu();
|
||
render_quit_widget();
|
||
render_hierarchy();
|
||
// template method implementation
|
||
render_();
|
||
|
||
render_internal();
|
||
}
|
||
|
||
void ui_layer::render_internal()
|
||
{
|
||
ImGui::Render();
|
||
GfxRenderer->GetImguiRenderer()->Render();
|
||
|
||
if (m_imguiio->ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||
{
|
||
// panels outside of the main window are drawn in their own windows, whose gl contexts are shared with the main one
|
||
auto *context{glfwGetCurrentContext()};
|
||
ImGui::UpdatePlatformWindows();
|
||
ImGui::RenderPlatformWindowsDefault();
|
||
glfwMakeContextCurrent(context);
|
||
}
|
||
}
|
||
|
||
void ui_layer::begin_ui_frame()
|
||
{
|
||
// the colours follow the ui of the mode drawn
|
||
if (palette() != m_palette)
|
||
imgui_colors(palette());
|
||
begin_ui_frame_internal();
|
||
render_dockspace();
|
||
m_dockspaceframe = ImGui::GetFrameCount();
|
||
}
|
||
|
||
void ui_layer::begin_ui_frame_internal()
|
||
{
|
||
GfxRenderer->GetImguiRenderer()->BeginFrame();
|
||
ImGui_ImplGlfw_NewFrame();
|
||
ImGui::NewFrame();
|
||
}
|
||
|
||
void ui_layer::render_quit_widget()
|
||
{
|
||
if (!m_quit_active)
|
||
return;
|
||
|
||
ImGui::SetNextWindowSize(ImVec2(0, 0));
|
||
ImGui::Begin(STR_C("Quit"), &m_quit_active, ImGuiWindowFlags_NoResize);
|
||
ImGui::TextUnformatted(STR_C("Quit simulation?"));
|
||
if (ImGui::Button(STR_C("Yes")))
|
||
Application.queue_quit(false);
|
||
|
||
ImGui::SameLine();
|
||
if (ImGui::Button(STR_C("No")))
|
||
m_quit_active = false;
|
||
ImGui::End();
|
||
}
|
||
|
||
void ui_layer::render_hierarchy(){
|
||
if(!m_editor_hierarchy)
|
||
return;
|
||
|
||
// fitted to the contents when it first appears; no auto resize, which a docked window can't do
|
||
ImGui::SetNextWindowSize(ImVec2(0, 0), ImGuiCond_FirstUseEver);
|
||
ImGui::Begin((std::string(STR_C("Scene Hierarchy")) + "###scenehierarchy").c_str(), &m_editor_hierarchy);
|
||
ImGui::Text(STR_C("Registered nodes: %zu"), scene::Hierarchy.size());
|
||
ImGui::BeginChild("hierarchy_list", ImVec2(500, 300), true);
|
||
|
||
for (auto &entry : scene::Hierarchy)
|
||
{
|
||
const std::string &uuid = entry.first;
|
||
scene::basic_node *node = entry.second;
|
||
|
||
if (node)
|
||
{
|
||
char buf[512];
|
||
std::snprintf(buf, sizeof(buf), "%s | %s (%.1f, %.1f, %.1f)",
|
||
node->name().c_str(),
|
||
uuid.c_str(),
|
||
node->location().x,
|
||
node->location().y,
|
||
node->location().z);
|
||
|
||
if (ImGui::Selectable(buf, false))
|
||
{
|
||
// Focus camera on selected node
|
||
auto const node_pos = node->location();
|
||
auto const camera_offset = glm::dvec3(0.0, 10.0, -20.0);
|
||
editor_mode::set_focus_active(false);
|
||
TCamera &camera = editor_mode::get_camera();
|
||
camera.Pos = node_pos + camera_offset;
|
||
|
||
}
|
||
|
||
if (ImGui::IsItemHovered())
|
||
{
|
||
ImGui::SetTooltip(STR_C("UUID: %s\nNode type: %s"), uuid.c_str(), typeid(*node).name());
|
||
}
|
||
}
|
||
}
|
||
|
||
ImGui::EndChild();
|
||
ImGui::End();
|
||
|
||
}
|
||
|
||
void ui_layer::set_cursor(int const Mode)
|
||
{
|
||
glfwSetInputMode(m_window, GLFW_CURSOR, Mode);
|
||
m_cursorvisible = Mode != GLFW_CURSOR_DISABLED;
|
||
}
|
||
|
||
void ui_layer::set_progress(std::string const &Text)
|
||
{
|
||
m_progresstext = Text;
|
||
}
|
||
|
||
void ui_layer::set_progress(float const progress, float const subtaskprogress)
|
||
{
|
||
m_progress = progress * 0.01f;
|
||
m_subtaskprogress = subtaskprogress * 0.01f;
|
||
}
|
||
|
||
void ui_layer::set_background(std::string const &Filename)
|
||
{
|
||
m_background = Filename.empty() ? null_handle : GfxRenderer->Fetch_Texture(Filename);
|
||
}
|
||
|
||
void ui_layer::clear_panels()
|
||
{
|
||
m_panels.clear();
|
||
m_ownedpanels.clear();
|
||
}
|
||
|
||
void ui_layer::add_owned_panel(ui_panel *Panel)
|
||
{
|
||
for (auto &panel : m_ownedpanels)
|
||
if (panel->name() == Panel->name())
|
||
{
|
||
delete Panel;
|
||
return;
|
||
}
|
||
|
||
Panel->is_open = true;
|
||
m_ownedpanels.emplace_back(Panel);
|
||
}
|
||
|
||
void ui_layer::render_panels()
|
||
{
|
||
for (auto *panel : m_panels)
|
||
panel->render();
|
||
for (auto &panel : m_ownedpanels)
|
||
panel->render();
|
||
|
||
if (m_imgui_demo)
|
||
ImGui::ShowDemoWindow(&m_imgui_demo);
|
||
}
|
||
|
||
void ui_layer::render_tooltip()
|
||
{
|
||
if (!m_cursorvisible || m_imguiio->WantCaptureMouse || m_tooltip.empty())
|
||
return;
|
||
|
||
ImGui::BeginTooltip();
|
||
ImGui::TextUnformatted(m_tooltip.c_str());
|
||
ImGui::EndTooltip();
|
||
}
|
||
|
||
void ui_layer::render_menu_contents()
|
||
{
|
||
if (ImGui::BeginMenu(STR_C("General")))
|
||
{
|
||
bool flag = DebugModeFlag;
|
||
if (ImGui::MenuItem(STR_C("Debug mode"), nullptr, &flag))
|
||
{
|
||
command_relay relay;
|
||
relay.post(user_command::debugtoggle, 0.0, 0.0, GLFW_RELEASE, 0);
|
||
}
|
||
ImGui::MenuItem(STR_C("Quit"), "F10", &m_quit_active);
|
||
ImGui::EndMenu();
|
||
}
|
||
if (ImGui::BeginMenu(STR_C("Tools")))
|
||
{
|
||
static bool log = Global.iWriteLogEnabled & 1;
|
||
|
||
ImGui::MenuItem(STR_C("Logging to log.txt"), nullptr, &log);
|
||
if (log)
|
||
Global.iWriteLogEnabled |= 1;
|
||
else
|
||
Global.iWriteLogEnabled &= ~1;
|
||
|
||
if (ImGui::MenuItem(STR_C("Screenshot"), "PrtScr"))
|
||
Application.queue_screenshot();
|
||
ImGui::EndMenu();
|
||
}
|
||
if (ImGui::BeginMenu(STR_C("Windows")))
|
||
{
|
||
ImGui::MenuItem(STR_C("Log"), "F9", &m_logpanel.is_open);
|
||
if (DebugModeFlag)
|
||
{
|
||
ImGui::MenuItem(STR_C("ImGui Demo"), nullptr, &m_imgui_demo);
|
||
bool ret = ImGui::MenuItem(STR_C("Headlight config"), nullptr, GfxRenderer->Debug_Ui_State(std::nullopt));
|
||
|
||
GfxRenderer->Debug_Ui_State(ret);
|
||
}
|
||
if(EditorModeFlag){
|
||
ImGui::MenuItem(STR_C("Scene Hierarchy"), nullptr, &m_editor_hierarchy);
|
||
bool change_history_enabled = editor_mode::change_history();
|
||
if (ImGui::MenuItem(STR_C("Change History"), nullptr, &change_history_enabled))
|
||
{
|
||
editor_mode::set_change_history(change_history_enabled);
|
||
}
|
||
bool settings_open = editor_mode::settings_open();
|
||
if (ImGui::MenuItem(STR_C("Editor Settings"), nullptr, &settings_open))
|
||
{
|
||
editor_mode::set_settings_open(settings_open);
|
||
}
|
||
}
|
||
render_windows_menu();
|
||
ImGui::EndMenu();
|
||
}
|
||
}
|
||
|
||
void ui_layer::render_menu()
|
||
{
|
||
glm::dvec2 mousepos = Global.cursor_pos;
|
||
|
||
if (m_suppress_menu || (false == m_menu_always && !((Global.ControlPicking && mousepos.y < 50.0f) || m_imguiio->WantCaptureMouse)))
|
||
return;
|
||
|
||
if (ImGui::BeginMainMenuBar())
|
||
{
|
||
render_menu_contents();
|
||
ImGui::EndMainMenuBar();
|
||
}
|
||
}
|
||
|
||
void ui_layer::render_background()
|
||
{
|
||
if (m_background == 0)
|
||
return;
|
||
|
||
ImVec2 display_size = ImGui::GetIO().DisplaySize;
|
||
ITexture &tex = GfxRenderer->Texture(m_background);
|
||
tex.create();
|
||
|
||
float tex_w = (float)tex.get_width();
|
||
float tex_h = (float)tex.get_height();
|
||
|
||
// skalowanie "cover" – wypełnia cały ekran, zachowując proporcje
|
||
float scale_factor = display_size.x / display_size.y > tex_w / tex_h ? display_size.x / tex_w : display_size.y / tex_h;
|
||
|
||
ImVec2 image_size(tex_w * scale_factor, tex_h * scale_factor);
|
||
|
||
// wyśrodkowanie obrazka
|
||
ImVec2 start_position((display_size.x - image_size.x) * 0.5f, (display_size.y - image_size.y) * 0.5f);
|
||
ImVec2 end_position(start_position.x + image_size.x, start_position.y + image_size.y);
|
||
|
||
// obrazek jest odwrócony w pionie – odwracamy UV
|
||
ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())->AddImage((ImTextureID)(intptr_t)(tex.get_id()), start_position, end_position, ImVec2(0, 1), ImVec2(1, 0));
|
||
}
|