Unravel Engine C++ Reference
Loading...
Searching...
No Matches
header_panel.cpp
Go to the documentation of this file.
1#include "header_panel.h"
2#include "../panel.h"
3#include "../panels_defs.h"
4
7#include <editor/shortcuts.h>
8
11#include <engine/ecs/ecs.h>
12#include <engine/events.h>
17#include <exception>
19#include <version/version.h>
20
21#include <imgui/imgui.h>
22#include <imgui/imgui_internal.h>
24
25namespace unravel
26{
27
28namespace
29{
30auto get_debug_mode_size() -> float
31{
32 return 120.0f;
33}
34void draw_debug_mode()
35{
36 bool debugger_attached = script_system::is_debugger_attached();
37 bool debug_mode = script_system::get_script_debug_mode();
38 const char* modes[] = {ICON_MDI_BUG_CHECK " Debug", ICON_MDI_BUG " Release"};
39 const char* debug_mode_preview = modes[int(!debug_mode)];
40 ImGui::SetNextItemWidth(get_debug_mode_size());
41
42 if(debugger_attached)
43 {
44 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 1.0f, 0.0f, 1.0f));
45 }
46 if(ImGui::BeginCombo("###DebugMode", debug_mode_preview))
47 {
48 if(ImGui::Selectable(modes[0]))
49 {
50 if(!debug_mode)
51 {
54 }
55 }
56 ImGui::SetItemTooltipEx("%s",
57 "Debug mode enales C# debugging\n"
58 "but reduces C# performance.\n"
59 "Switching to Debug mode will recompile\n"
60 "and reload all scripts.");
61
62 if(ImGui::Selectable(modes[1]))
63 {
64 if(debug_mode)
65 {
68 }
69 }
70 ImGui::SetItemTooltipEx("%s",
71 "Release mode disables C# debugging\n"
72 "but improves C# performance.\n"
73 "Switching to Release mode will recompile\n"
74 "and reload all scripts.");
75
76 ImGui::EndCombo();
77 }
78
79 const char* debug_mode_tooltip = debug_mode ? "Debugger Enabled" : "Debugger Disabled";
80
81 ImGui::SetItemTooltipEx("%s", debug_mode_tooltip);
82 if(debugger_attached)
83 {
84 ImGui::SetItemTooltipEx("%s", "Debugger Attached");
85 ImGui::PopStyleColor();
86 }
87}
88} // namespace
89
90header_panel::header_panel(imgui_panels* parent) : parent_(parent)
91{
92}
93
94void header_panel::draw_menubar_child(rtti::context& ctx)
95{
96 ImGuiWindowFlags header_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar |
97 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
98 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_MenuBar;
99 ImGui::BeginChild("HEADER_menubar", ImVec2(0, ImGui::GetFrameHeight()), false, header_flags);
100
101 // Draw menu bar.
102 if(ImGui::BeginMenuBar())
103 {
104 if(ImGui::BeginMenu("File"))
105 {
106 if(ImGui::MenuItem("New Scene", ImGui::GetKeyCombinationName(shortcuts::new_scene).c_str()))
107 {
109 }
110
111 if(ImGui::MenuItem("Open Scene", ImGui::GetKeyCombinationName(shortcuts::open_scene).c_str()))
112 {
114 }
115
116 if(ImGui::MenuItem("Save Scene...", ImGui::GetKeyCombinationName(shortcuts::save_scene).c_str()))
117 {
119 }
120
121 if(ImGui::MenuItem("Save Scene As", ImGui::GetKeyCombinationName(shortcuts::save_scene_as).c_str()))
122 {
124 }
125
126 if(ImGui::MenuItem("Reload Project", nullptr))
127 {
129 }
130
131 if(ImGui::MenuItem("Close Project", nullptr))
132 {
134 }
135 ImGui::EndMenu();
136 }
137
138 if(ImGui::BeginMenu("Edit"))
139 {
140
141 auto& em = ctx.get_cached<editing_manager>();
142 auto undo = em.can_undo() ? "Undo*" : "Undo";
143 auto redo = em.can_redo() ? "Redo*" : "Redo";
144 if(ImGui::MenuItem(undo, ImGui::GetKeyCombinationName(shortcuts::undo).c_str()))
145 {
146 em.undo();
147 }
148
149 if(ImGui::MenuItem(redo, ImGui::GetKeyCombinationName(shortcuts::redo).c_str()))
150 {
151 em.redo();
152 }
153
154 if(ImGui::MenuItem("Undo History", ImGui::GetKeyCombinationName(shortcuts::undo_history).c_str()))
155 {
156 parent_->get_undo_redo_panel().show(true);
157 }
158
159 if(ImGui::MenuItem("Editor Settings..."))
160 {
161 parent_->get_editor_settings_panel().show(true);
162 }
163
164 if(ImGui::MenuItem("Project Settings..."))
165 {
166 parent_->get_project_settings_panel().show(true, "{}");
167 }
168
169 ImGui::EndMenu();
170 }
171
172 if(ImGui::BeginMenu("Deploy"))
173 {
174 if(ImGui::MenuItem("Deploy Project"))
175 {
176 parent_->get_deploy_panel().show(true);
177 }
178
179 ImGui::EndMenu();
180 }
181
182 if(ImGui::BeginMenu("Developer"))
183 {
184
185 if(ImGui::BeginMenu("Crash"))
186 {
187 if(ImGui::MenuItem("Abort"))
188 {
189 std::abort();
190 }
191 if(ImGui::MenuItem("Terminate"))
192 {
193 std::terminate();
194 }
195 if(ImGui::MenuItem("Segmentation Fault"))
196 {
197 *(volatile int*)0 = 0;
198 }
199 ImGui::EndMenu();
200 }
201
202 if(ImGui::BeginMenu("Recompile"))
203 {
204 if(ImGui::MenuItem("Shaders"))
205 {
207 }
208
209 if(ImGui::MenuItem("Textures"))
210 {
212 }
213
214 if(ImGui::MenuItem("UI", ImGui::GetKeyCombinationName(shortcuts::recompile_ui).c_str()))
215 {
217 }
218
219 if(ImGui::MenuItem("Scripts"))
220 {
222 }
223
224 if(ImGui::MenuItem("All"))
225 {
227 }
228 ImGui::EndMenu();
229 }
230
231 ImGui::EndMenu();
232 }
233 if(ImGui::BeginMenu("Windows"))
234 {
235 if(ImGui::MenuItem("Style"))
236 {
237 parent_->get_style_panel().show(true);
238 }
239 if(ImGui::MenuItem("Animation"))
240 {
241 parent_->get_animation_panel().show(true);
242 }
243 ImGui::EndMenu();
244 }
245
246 if(ImGui::BeginMenu("Help"))
247 {
248 if(ImGui::MenuItem("About"))
249 {
250 show_about_window_ = true;
251 }
252
253 ImGui::EndMenu();
254 }
255
256 ImGui::EndMenuBar();
257 }
258
259 if(!ImGui::IsAnyItemActive())
260 {
261 if(ImGui::IsCombinationKeyPressed(shortcuts::new_scene))
262 {
264 }
265 else if(ImGui::IsCombinationKeyPressed(shortcuts::open_scene))
266 {
268 }
269 else if(ImGui::IsCombinationKeyPressed(shortcuts::save_scene_as))
270 {
272 }
273 else if(ImGui::IsCombinationKeyPressed(shortcuts::save_scene))
274 {
276 }
277
278 else if(ImGui::IsCombinationKeyPressed(shortcuts::redo, true))
279 {
280 ctx.get_cached<editing_manager>().redo();
281 }
282 else if(ImGui::IsCombinationKeyPressed(shortcuts::redo_alt, true))
283 {
284 ctx.get_cached<editing_manager>().redo();
285 }
286 else if(ImGui::IsCombinationKeyPressed(shortcuts::undo, true))
287 {
288 ctx.get_cached<editing_manager>().undo();
289 }
290 else if(ImGui::IsCombinationKeyPressed(shortcuts::undo_history))
291 {
292 parent_->get_undo_redo_panel().show(true);
293 }
294 else if(ImGui::IsCombinationKeyPressed(shortcuts::recompile_ui))
295 {
297 }
298 }
299 ImGui::EndChild();
300}
301
302void header_panel::draw_play_toolbar(rtti::context& ctx, float header_size)
303{
304 auto& ev = ctx.get_cached<events>();
305
306 float width = ImGui::GetContentRegionAvail().x;
307
308 auto window_pos = ImGui::GetWindowPos();
309 auto window_size = ImGui::GetWindowSize();
310 // Add a poly background for the logo.
311 const ImVec2 logo_bounds = ImVec2(500, header_size * 0.5f);
312 const ImVec2 logo_pos = ImVec2(window_pos.x + window_size.x * 0.5f - logo_bounds.x * 0.5f, window_pos.y);
313
314 ImVec2 points[5] = {ImVec2(logo_pos.x, logo_pos.y),
315 ImVec2(logo_pos.x + 20, logo_pos.y + logo_bounds.y + 4),
316 ImVec2(logo_pos.x + logo_bounds.x - 20, logo_pos.y + logo_bounds.y + 4),
317 ImVec2(logo_pos.x + logo_bounds.x, logo_pos.y),
318 ImVec2(logo_pos.x, logo_pos.y)};
319
320 const ImU32 poly_background = ImGui::GetColorU32(ImGuiCol_MenuBarBg);
321 auto poly_background_border_color = poly_background;
322
323 if(ev.is_playing)
324 {
325 poly_background_border_color = ImGui::GetColorU32(ImVec4(0.0f, 0.5f, 0.0f, 0.5f));
326 }
327 if(ev.is_paused)
328 {
329 poly_background_border_color = ImGui::GetColorU32(ImVec4(0.6f, 0.3f, 0.0f, 0.5f));
330 }
331
332 ImGui::GetWindowDrawList()->AddConvexPolyFilled(&points[0], 5, poly_background_border_color);
333 // ImGui::GetWindowDrawList()->AddPolyline(&points[0], 4, poly_background_border_color, 0, 3);
334 // ImGui::GetWindowDrawList()->AddRectFilledMultiColor(logo_pos,
335 // logo_pos + logo_bounds,
336 // poly_background_border_color,
337 // poly_background_border_color,
338 // poly_background,
339 // poly_background);
340
341 auto& pm = ctx.get_cached<project_manager>();
342 auto logo = fmt::format("{}", pm.get_name());
343 auto logo_size = ImGui::CalcTextSize(logo.c_str());
344 // Add animated logo.
345 const ImVec2 logo_min = ImVec2(logo_pos.x + logo_bounds.x * 0.5f - logo_size.x * 0.5f,
346 logo_pos.y + (logo_bounds.y - logo_size.y) * 0.5f);
347 const ImVec2 logo_max = ImVec2(logo_min.x + logo_size.x, logo_min.y + logo_size.y);
348 auto logo_border_color = ImGui::GetColorU32(ImGuiCol_Text);
349 ImGui::GetWindowDrawList()->AddText(logo_min, logo_border_color, logo.c_str());
350
351 const auto& style = ImGui::GetStyle();
352 auto frame_padding = style.FramePadding;
353 auto item_spacing = style.ItemSpacing;
354 ImGui::AlignedItem(0.5f,
355 width,
356 ImGui::CalcTextSize(ICON_MDI_PLAY ICON_MDI_PAUSE ICON_MDI_SKIP_NEXT).x + frame_padding.x * 6 +
357 item_spacing.x * 3,
358 [&]()
359 {
360 ImGuiKeyChord key_chord = ev.is_playing ? shortcuts::play_toggle : shortcuts::play_toggle;
361 bool play_pressed = ImGui::IsKeyChordPressed(key_chord);
362
363 auto& scripting = ctx.get_cached<script_system>();
364 bool has_errors = scripting.has_compilation_errors();
365 ImGui::BeginDisabled(has_errors);
366 ImGui::BeginGroup();
367
368 play_pressed |= ImGui::Button(ev.is_playing ? ICON_MDI_STOP : ICON_MDI_PLAY);
369
370 if(has_errors)
371 {
372 play_pressed = false;
373 }
374 ImGui::SetItemTooltipEx("%s", ImGui::GetKeyChordName(key_chord));
375 if(play_pressed)
376 {
377 ev.toggle_play_mode(ctx);
378 ImGui::FocusWindow(ImGui::FindWindowByName(ev.is_playing ? GAME_VIEW : SCENE_VIEW));
379 }
380
381 ImGui::SameLine();
382 if(ImGui::Button(ICON_MDI_PAUSE))
383 {
384 bool was_playing = ev.is_playing;
385 ev.toggle_pause(ctx);
386 }
387
388 ImGui::SameLine();
389 ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
390 if(ImGui::Button(ICON_MDI_SKIP_NEXT))
391 {
392 ev.skip_next_frame(ctx);
393 }
394 ImGui::PopItemFlag();
395 ImGui::SameLine();
396
397 ImGui::BeginDisabled(ev.is_playing);
398 draw_debug_mode();
399 ImGui::EndDisabled();
400 ImGui::SameLine();
401
402 auto& sim = ctx.get_cached<simulation>();
403
404 auto time_scale = sim.get_time_scale();
405 ImGui::SetNextItemWidth(100);
406 if(ImGui::SliderFloat("###Time Scale", &time_scale, 0.0f, 1.0f))
407 {
408 sim.set_time_scale(time_scale);
409 }
410 ImGui::SetItemTooltipEx("%s", "Time scale.");
411 ImGui::SameLine();
412 auto& rend = ctx.get_cached<renderer>();
413 auto vsync = rend.get_vsync();
414 if(ImGui::Checkbox("Vsync", &vsync))
415 {
416 rend.set_vsync(vsync);
417 }
418
419 ImGui::EndGroup();
420 ImGui::EndDisabled();
421
422 if(has_errors)
423 {
424 ImGui::SetItemTooltipEx(
425 "%s",
426 "All compiler errors must be fixed before you can enter Play Mode!");
427 }
428 });
429}
430
431void header_panel::on_frame_ui_render(rtti::context& ctx, float header_size)
432{
433 ImGuiWindowFlags header_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBringToFrontOnFocus |
434 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
435 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDecoration;
436 ImGuiViewport* viewport = ImGui::GetMainViewport();
437
438 ImGui::SetNextWindowPos(ImVec2(viewport->WorkPos.x, viewport->WorkPos.y));
439 ImGui::SetNextWindowSize(ImVec2(viewport->WorkSize.x, header_size));
440 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
441 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
442
443 ImGui::SetNextWindowViewport(viewport->ID);
444
445 bool open = ImGui::Begin("HEADER", nullptr, header_flags);
446
447 ImGui::PopStyleVar();
448 ImGui::PopStyleVar();
449
450 if(open)
451 {
452 // ImGui::WindowTimeBlock block(ImGui::GetFont(ImGui::Font::Mono));
453
454 // Draw a sep. child for the menu bar.
455 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetColorU32(ImGuiCol_MenuBarBg));
456 draw_menubar_child(ctx);
457 ImGui::NewLine();
458 draw_play_toolbar(ctx, header_size);
459 ImGui::PopStyleColor();
460 }
461
462 ImGui::End();
463
464 // Draw the about window (will only be visible if show_about_window_ is true)
465 draw_about_window(ctx);
466}
467
468void header_panel::draw_about_window(rtti::context& ctx)
469{
470 if (!show_about_window_)
471 return;
472
473 if(!ImGui::IsPopupOpen("About Unravel Engine"))
474 {
475 ImGui::OpenPopup("About Unravel Engine");
476 }
477
478 auto viewport = ImGui::GetMainViewport();
479 ImGui::SetNextWindowSize(ImVec2(viewport->WorkSize * 0.30f), ImGuiCond_Always);
480 ImGui::SetNextWindowPos(ImVec2(viewport->WorkSize.x * 0.5f, viewport->WorkSize.y * 0.5f), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
481 if (ImGui::BeginPopupModal("About Unravel Engine", &show_about_window_, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize| ImGuiWindowFlags_NoMove))
482 {
483 // Logo and title
484 const float title_scale = 1.5f;
485 ImGui::PushFont(GetFont(ImGui::Font::Bold), GetFont(ImGui::Font::Bold)->LegacySize * title_scale); // Use default font
486 ImGui::TextColored(ImVec4(0.4f, 0.6f, 1.0f, 1.0f), "Unravel Engine");
487 ImGui::PopFont();
488
489 // Version information
490 ImGui::Text("Version %s", version::get_full().c_str());
491 ImGui::Separator();
492
493 // Engine description
494 ImGui::TextWrapped(
495 "Unravel Engine is a modern, high-performance game engine designed for creating "
496 "interactive 3D and 2D applications. It features a component-based architecture, "
497 "powerful rendering capabilities, and an intuitive editor interface.");
498
499 ImGui::Spacing();
500 ImGui::Spacing();
501
502 // Features
503 const float section_scale = 1.2f;
504 ImGui::PushFont(ImGui::GetFont(), ImGui::GetFont()->LegacySize * section_scale);
505 ImGui::Text("Key Features");
506 ImGui::PopFont();
507
508 ImGui::Columns(2);
509 ImGui::BulletText("Entity-Component-System");
510 ImGui::BulletText("PBR Rendering");
511 ImGui::BulletText("C# Scripting");
512 ImGui::BulletText("Physics Integration");
513 ImGui::NextColumn();
514 ImGui::BulletText("Real-time Editor");
515 ImGui::BulletText("Asset Management");
516 ImGui::BulletText("Cross-platform Support");
517 ImGui::BulletText("Extensible Architecture");
518 ImGui::Columns(1);
519
520 ImGui::Spacing();
521 ImGui::Spacing();
522
523 // Build information
524 ImGui::PushFont(ImGui::GetFont(), ImGui::GetFont()->LegacySize * section_scale);
525 ImGui::Text("Build Information");
526 ImGui::PopFont();
527
528 ImGui::Text("Build Date: %s", __DATE__);
529 ImGui::Text("Build Time: %s", __TIME__);
530#ifdef _DEBUG
531 ImGui::Text("Configuration: Debug");
532#else
533 ImGui::Text("Configuration: Release");
534#endif
535
536 ImGui::Spacing();
537 ImGui::Spacing();
538
539 // Copyright notice
540 ImGui::Separator();
541 ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
542 "Copyright © %d. All rights reserved.", 2025);
543
544
545 ImGui::EndPopup();
546 }
547}
548
549
550
551} // namespace unravel
header_panel(imgui_panels *parent)
auto get_project_settings_panel() -> project_settings_panel &
Definition panel.cpp:146
auto get_deploy_panel() -> deploy_panel &
Definition panel.cpp:141
auto get_editor_settings_panel() -> editor_settings_panel &
Definition panel.cpp:151
auto get_style_panel() -> style_panel &
Definition panel.cpp:171
auto get_animation_panel() -> animation_panel &
Definition panel.cpp:176
auto get_undo_redo_panel() -> undo_redo_panel &
Definition panel.cpp:181
#define ICON_MDI_BUG_CHECK
#define ICON_MDI_PAUSE
#define ICON_MDI_SKIP_NEXT
#define ICON_MDI_PLAY
#define ICON_MDI_BUG
void PushFont(Font::Enum _font)
Definition imgui.cpp:617
ImFont * GetFont(Font::Enum _font)
Definition imgui.cpp:623
const ImGuiKeyCombination undo_history
Definition shortcuts.h:48
const ImGuiKeyCombination new_scene
Definition shortcuts.h:15
const ImGuiKeyCombination undo
Definition shortcuts.h:45
const ImGuiKeyCombination redo
Definition shortcuts.h:46
const ImGuiKeyCombination recompile_ui
Definition shortcuts.h:49
const ImGuiKeyCombination redo_alt
Definition shortcuts.h:47
const ImGuiKeyCombination save_scene_as
Definition shortcuts.h:18
const ImGuiKeyCombination open_scene
Definition shortcuts.h:16
const ImGuiKeyCombination save_scene
Definition shortcuts.h:17
std::string get_full()
Definition version.cpp:20
float x
auto get_cached() -> T &
Definition context.hpp:49
static auto close_project(rtti::context &ctx) -> bool
static auto open_scene(rtti::context &ctx) -> bool
static auto save_scene(rtti::context &ctx) -> bool
static auto new_scene(rtti::context &ctx) -> bool
static auto reload_project(rtti::context &ctx) -> bool
static auto save_scene_as(rtti::context &ctx) -> bool
static auto is_debugger_attached() -> bool
static auto get_script_debug_mode() -> bool
static void set_script_debug_mode(bool debug)
static void set_needs_recompile(const std::string &protocol, bool now=false)