galaxy 1.0.0
Real-Time C++23 Game Programming Framework. Built on data-driven design principles and agile software engineering.
Loading...
Searching...
No Matches
Editor.cpp
Go to the documentation of this file.
1
7
8#include <draw.h>
9#include <imgui/notify/ImGuiNotify.hpp>
10
13
14#include "Editor.hpp"
15
16namespace galaxy
17{
19 : m_hotkeys {"EditorMenuBarHotkeyPopup"}
20 {
21 ui::imgui_init("editor.ini");
22 }
23
28
29 void Editor::handle_events(const SDL_Event& events) noexcept
30 {
31 if (events.type == SDL_EVENT_KEY_DOWN)
32 {
33 if (events.key.scancode == Settings::editor_key())
34 {
35 m_show = !m_show;
36 }
37 }
38
40 }
41
43 {
44 if (m_show)
45 {
47 draw();
49 }
50 }
51
53 {
54 begin_dock();
55
56 // if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && m_viewport.m_editor_cam_enabled)
57 //{
58 // m_use_hand_cursor = false;
59 // ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
60 // }
61
62 // if (m_use_hand_cursor)
63 //{
64 // ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
65 // }
66
68 // short_cuts();
69
70 // ui::imgui_popup("New##MenuBarPopup", m_show_new, [&]() {
71 // ui::imgui_confirm("Create a new project?\nUnsaved progress will be lost.", [&]() {
72 // new_project();
73 // });
74 // });
75
76 // ui::imgui_popup("Exit##MenuBarPopup", m_show_exit, [&]() {
77 // ui::imgui_confirm("Are you sure you want to exit?\nUnsaved progress will be lost.", [&]() {
78 // exit();
79 // });
80 // });
81
82 m_hotkeys.draw([&]() {
83 ImGui::Text("[LEFT SHIFT] - Enable Docking.\n");
84 });
85
86 // m_log_console.render();
87 // m_lua_console.render();
88 // m_code_editor.render();
89 // m_asset_panel.render(m_code_editor);
90
91 // m_scene_panel.render(m_project, m_tasks, m_selected);
93
94 // m_entity_panel.render(m_tasks, m_selected);
95 // m_viewport.render(m_state, m_editor_camera);
97
98 // render_exporter();
99 end_dock();
100 }
101
103 {
104 ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
105 }
106
108 {
109 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f);
110 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
111 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.10f, 0.10f, 0.10f, 1.00f));
112 ImGui::RenderNotifications();
113 ImGui::PopStyleVar(2);
114 ImGui::PopStyleColor(1);
115 }
116
118 {
119 if (ImGui::BeginMainMenuBar())
120 {
121 // if (ImGui::BeginMenu("File"))
122 //{
123 // // m_show_new = ImGui::MenuItem("New", "Ctrl+N");
124
125 // if (ImGui::MenuItem("Open", "Ctrl+O"))
126 // {
127 // // show_loadproject();
128 // }
129
130 // if (ImGui::MenuItem("Save", "Ctrl+S"))
131 // {
132 // // save_project();
133 // }
134
135 // if (ImGui::MenuItem("Save As", "Ctrl+Shift+S"))
136 // {
137 // // save_project(true);
138 // }
139
140 // if (ImGui::MenuItem("Export", "Ctrl+E"))
141 // {
142 // // export_project();
143 // }
144
145 // // m_show_exit = ImGui::MenuItem("Exit");
146
147 // ImGui::EndMenu();
148 //}
149
150 // if (ImGui::BeginMenu("Panels"))
151 //{
152
153 // if (ImGui::MenuItem("Toggle Entity Panel", "Ctrl+Alt+E"))
154 // {
155 // // m_entity_panel.m_show = !m_entity_panel.m_show;
156 // }
157
158 // if (ImGui::MenuItem("Toggle Viewport", "Ctrl+Alt+V"))
159 // {
160 // // m_viewport.m_show = !m_viewport.m_show;
161 // }
162
163 // if (ImGui::MenuItem("Toggle Asset Panel", "Ctrl+Alt+A"))
164 // {
165 // // m_asset_panel.m_show = !m_asset_panel.m_show;
166 // }
167
168 // if (ImGui::MenuItem("Toggle Logs", "Ctrl+Alt+L"))
169 // {
170 // // m_log_console.m_show = !m_log_console.m_show;
171 // }
172
173 // if (ImGui::MenuItem("Toggle Lua Terminal", "Ctrl+Alt+T"))
174 // {
175 // // m_lua_console.m_show = !m_lua_console.m_show;
176 // }
177
178 // if (ImGui::MenuItem("Toggle Code Editor", "Ctrl+Alt+C"))
179 // {
180 // // m_code_editor.m_show = !m_code_editor.m_show;
181 // }
182
183 // ImGui::EndMenu();
184 //}
185
186 // if (ImGui::BeginMenu("Tools"))
187 //{
188 // // static auto s_basedir = GALAXY_ROOT_DIR / GALAXY_EDITOR_DATA_DIR;
189 // // static auto s_ltdk = s_basedir / "tools/ldtk/ldtk.exe";
190 // // static auto s_bfxr = s_basedir / "tools/bfxr/Bfxr.exe";
191
192 // if (ImGui::MenuItem("LDTK"))
193 // {
194 // // m_ldtk.create(s_ltdk.string());
195 // }
196
197 // if (ImGui::MenuItem("BFXR"))
198 // {
199 // // m_bfxr.create(s_bfxr.string());
200 // }
201
202 // ImGui::EndMenu();
203 //}
204
205 m_hotkeys.draw_menuitem("Hotkeys");
206
207 // draw_game_controls();
208
209 ImGui::EndMainMenuBar();
210 }
211 }
212
213 /*Editor::Editor(const std::string& name)
214 : Scene {name}
215 , m_state {EditorState::EDITOR}
216 , m_padding {4, 4}
217 {
218 // Reset our editor dispatcher.
219 // We dont want anything bound.
220 m_dispatcher = {};
221 m_current_project_path = "";
222
223 auto& sink = GALAXY_ADD_SINK(EditorSink);
224 m_log_console.set_sink(&sink);
225
226 // clang-format off
227 m_autosave.repeat(true);
228 m_autosave.set([&]() {
229 save_project(false);
230 }, 300000);
231 // clang-format on
232
233 m_settings_panel.load(core::ServiceLocator<core::Config>::ref().raw());
234 }
235
236 Editor::~Editor()
237 {
238 unload();
239 }
240
241 void Editor::load()
242 {
243 auto& win = core::ServiceLocator<core::Window>::ref();
244 glfwSetWindowCloseCallback(win.handle(), [](GLFWwindow* window) {
245 glfwSetWindowShouldClose(window, GLFW_FALSE);
246 });
247
248 new_project();
249 }
250
251 void Editor::unload()
252 {
253 m_ldtk.terminate();
254 m_bfxr.terminate();
255 }
256
257 void Editor::update()
258 {
259 graphics::Renderer::ref().flush();
260
261 for (auto& task : m_tasks)
262 {
263 task();
264 }
265
266 m_tasks.clear();
267
268 switch (m_state)
269 {
270 case EditorState::EDITOR:
271 {
272 m_autosave.update();
273 m_project.only_update_rendering();
274
275 if (m_viewport.m_viewport_focused && m_viewport.m_viewport_hovered)
276 {
277 if (ImGui::IsMouseDown(ImGuiMouseButton_Left))
278 {
279 auto [mx, my] = ImGui::GetMousePos();
280 mx -= m_viewport.m_viewport_bounds[0].x;
281 my -= m_viewport.m_viewport_bounds[0].y;
282
283 const auto size = m_viewport.m_viewport_bounds[1] - m_viewport.m_viewport_bounds[0];
284 my = size.y - my;
285
286 if (mx >= 0 && my >= 0 && mx < size.x && my < size.y)
287 {
288 auto& fb = m_viewport.m_framebuffer.fbo();
289
290 const auto entity = fb.read_storagebuffer(static_cast<int>(mx), static_cast<int>(my));
291 if (entity == -1)
292 {
293 m_selected.entity = entt::null;
294 m_selected.scene = nullptr;
295 }
296 else
297 {
298 m_selected.entity = static_cast<entt::entity>(static_cast<std::uint32_t>(entity));
299 m_selected.scene = m_project.current();
300 }
301 }
302 }
303
304 if (ImGui::IsMouseDown(ImGuiMouseButton_Right) && m_viewport.m_editor_cam_enabled)
305 {
306 m_use_hand_cursor = true;
307
308 m_imgui_mouse_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
309 m_editor_camera.translate(m_imgui_mouse_delta.x, m_imgui_mouse_delta.y);
310 ImGui::ResetMouseDragDelta(ImGuiMouseButton_Right);
311 }
312 }
313 }
314 break;
315
316 case EditorState::GAME:
317 {
318 m_project.update();
319
320 // We need to use galaxy event handling here since imgui is disabled.
321 if (input::Input::key_down(input::Keys::KEY_PAUSE))
322 {
323 set_state_editor();
324 }
325 }
326 break;
327
328 case EditorState::GAME_IN_EDITOR:
329 {
330 m_project.update();
331
332 if (m_viewport.m_viewport_focused && m_viewport.m_viewport_hovered)
333 {
334 set_input_game();
335 }
336 else
337 {
338 set_input_editor();
339 }
340 }
341 break;
342 }
343 }
344
345 void Editor::render()
346 {
347 switch (m_state)
348 {
349 case EditorState::EDITOR:
350 case EditorState::GAME_IN_EDITOR:
351 {
352 if (m_project.current())
353 {
354 m_viewport.set_active();
355
356 if (m_state == EditorState::EDITOR && m_viewport.m_editor_cam_enabled)
357 {
358 graphics::Renderer::ref().submit_camera(m_editor_camera);
359 }
360 else
361 {
362 graphics::Renderer::ref().submit_camera(m_project.current()->m_camera);
363 }
364
365 graphics::Renderer::ref().draw();
366
367 auto& nui = core::ServiceLocator<ui::NuklearUI>::ref();
368
369 nui.new_frame();
370 m_project.current()->update_ui();
371 nui.render();
372 }
373
374 auto& w = core::ServiceLocator<core::Window>::ref();
375 glBindFramebuffer(GL_FRAMEBUFFER, 0);
376 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
377 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
378 draw_editor();
379 glfwSwapBuffers(w.handle());
380 }
381 break;
382
383 case EditorState::GAME:
384 {
385 // Screen-space post processing only available on full game.
386 // TODO: Allow on any screen.
387 m_project.render();
388 }
389 break;
390 }
391 }
392
393 void Editor::exit()
394 {
395 core::ServiceLocator<core::Window>::ref().close();
396 }
397
398 void Editor::new_project()
399 {
400 m_project.clear();
401 m_backup = m_project.serialize();
402 m_current_project_path = "";
403 core::ServiceLocator<core::Window>::ref().append_title(" | Untitled");
404
405 m_autosave.stop();
406 m_autosave.start();
407 }
408
409 void Editor::load_project(const std::string& path)
410 {
411 std::ifstream ifs {path, std::ifstream::in | std::ifstream::binary | std::ifstream::ate};
412
413 if (ifs.good())
414 {
415 meta::vector<char> buffer;
416
417 const auto size = ifs.tellg();
418 buffer.resize(size);
419
420 ifs.seekg(0, std::ifstream::beg);
421 ifs.read(&buffer[0], size);
422 ifs.close();
423
424 m_current_project_path = std::filesystem::path(path);
425
426 auto data = std::string(buffer.begin(), buffer.end());
427 // data = math::decode_zlib(data);
428 // data = math::decode_base64(data);
429
430 auto json = json::read_raw(data);
431
432 if (!json.empty())
433 {
434 core::ServiceLocator<core::Window>::ref().append_title(" | " + m_current_project_path.filename().stem().string());
435 m_backup = json.at("app_data");
436
437 m_tasks.emplace_back([&]() {
438 // Sets from backup.
439 m_project.deserialize(m_backup);
440 set_state_editor();
441 });
442 }
443 else
444 {
445 GALAXY_LOG(GALAXY_ERROR, "Failed to parse json from memory after decompression for: {0}.", path);
446 ui::imgui_notify_error("Failed to open project.");
447 }
448 }
449 else
450 {
451 ifs.close();
452
453 GALAXY_LOG(GALAXY_ERROR, "Failed to open project file: {0}.", path);
454 ui::imgui_notify_error("Failed to open project.");
455 }
456 }
457
458 void Editor::save_project(bool save_as)
459 {
460 auto& fs = core::ServiceLocator<fs::VirtualFileSystem>::ref();
461
462 if (m_current_project_path.empty() || save_as)
463 {
464 auto sp_opt = fs.open_save_dialog("untitled.scproj", {"*.scproj"});
465 if (!sp_opt.empty())
466 {
467 m_current_project_path = sp_opt;
468 }
469 }
470
471 if (!m_current_project_path.empty())
472 {
473 std::ofstream ofs {m_current_project_path, std::ofstream::out | std::ofstream::trunc | std::ofstream::binary};
474
475 if (ofs.good())
476 {
477 auto& config = core::ServiceLocator<core::Config>::ref();
478 config.raw(m_settings_panel.save());
479 config.save();
480
481 nlohmann::json out_json = "{\"settings\":{},\"app_data\":{}}"_json;
482
483 out_json["app_data"] = m_project.serialize();
484 out_json["settings"] = m_settings_panel.save();
485
486 auto data = out_json.dump(4);
487
488 // data = math::encode_base64(data);
489 // data = math::encode_zlib(data);
490
491 ofs.write(data.data(), data.size());
492
493 core::ServiceLocator<core::Window>::ref().append_title("| " + m_current_project_path.filename().stem().string());
494 ui::imgui_notify_info("Saved project.");
495 }
496 else
497 {
498 GALAXY_LOG(GALAXY_ERROR, "Failed to save project.");
499 ui::imgui_notify_error("Failed to save project.");
500 }
501
502 ofs.close();
503 }
504 }
505
506 void Editor::export_project()
507 {
508 save_project();
509
510 m_show_export = true;
511
512 auto& tp = core::ServiceLocator<BS::thread_pool>::ref();
513 tp.detach_task([&]() {
514 const std::filesystem::path path = GALAXY_EDITOR_DATA_DIR + "export/";
515 if (!std::filesystem::exists(path))
516 {
517 std::filesystem::create_directories(path);
518 }
519
520 std::ofstream app_data;
521 std::ofstream app_config;
522
523 auto data = m_project.serialize().dump(4);
524 data = math::encode_base64(data);
525 data = math::encode_zlib(data);
526
527 app_data.open(path / GALAXY_APPDATA, std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
528 if (app_data.good())
529 {
530 app_data.write(data.data(), data.size());
531 }
532
533 app_data.close();
534
535 auto config_data = m_settings_panel.save().dump(4);
536 strutils::replace_first(config_data, "\"use_loose_assets\": true", "\"use_loose_assets\": false");
537
538 app_config.open(path / "config.json", std::ofstream::out | std::ofstream::trunc);
539 if (app_config.good())
540 {
541 app_config.write(config_data.data(), config_data.size());
542 }
543 else
544 {
545 GALAXY_LOG(GALAXY_ERROR, "Failed to export project settings.");
546 }
547
548 app_config.close();
549
550 const auto zip_path = path / GALAXY_ASSET_PACK;
551 std::filesystem::remove(zip_path);
552
553 struct zip_t* zip = zip_open(zip_path.string().c_str(), ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
554 recursively_zip_assets(zip, GALAXY_ASSET_DIR);
555 zip_close(zip);
556
557 m_show_export = false;
558 });
559 }
560
561 void Editor::draw_game_controls()
562 {
563 ImGui::SetCursorPosX(ImGui::GetWindowWidth() / 2);
564
565 // We dont need to parse EditorState::GAME here.
566 switch (m_state)
567 {
568 case EditorState::EDITOR:
569 {
570 if (ImGui::Button(ICON_MDI_PLAY, {24, 24}))
571 {
572 set_state_game_and_editor();
573 }
574 }
575 break;
576
577 case EditorState::GAME_IN_EDITOR:
578 {
579 if (ImGui::Button(ICON_MDI_STOP, {24, 24}))
580 {
581 set_state_editor();
582 }
583 }
584 break;
585 }
586
587 // Now draw full game mode play button control.
588 if (ImGui::Button(ICON_MDI_BUG_PLAY, {24, 24}))
589 {
590 if (!m_project.empty())
591 {
592 set_state_game();
593 }
594 else
595 {
596 ui::imgui_notify_warning("No active scene.");
597 }
598 }
599 }
600
601 void Editor::short_cuts()
602 {
603 m_show_new = ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_N);
604
605 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_E))
606 {
607 export_project();
608 }
609
610 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_O))
611 {
612 show_loadproject();
613 }
614
615 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Alt | ImGuiKey_L))
616 {
617 m_log_console.m_show = !m_log_console.m_show;
618 }
619
620 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S))
621 {
622 save_project();
623 }
624
625 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_S))
626 {
627 save_project(true);
628 }
629
630 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Alt | ImGuiKey_V))
631 {
632 m_viewport.m_show = !m_viewport.m_show;
633 }
634
635 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Alt | ImGuiKey_T))
636 {
637 m_lua_console.m_show = !m_lua_console.m_show;
638 }
639
640 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Alt | ImGuiKey_C))
641 {
642 m_code_editor.m_show = !m_code_editor.m_show;
643 }
644
645 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Alt | ImGuiKey_A))
646 {
647 m_asset_panel.m_show = !m_asset_panel.m_show;
648 }
649
650 if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiMod_Alt | ImGuiKey_E))
651 {
652 m_entity_panel.m_show = !m_entity_panel.m_show;
653 }
654
655 // static bool open = true;
656 // ImGui::ShowDemoWindow(&open);
657 }
658
659 void Editor::show_loadproject()
660 {
661 const auto path = core::ServiceLocator<fs::VirtualFileSystem>::ref().open_file_dialog({"*.scproj"}, "../editor_data/projects");
662 if (!path.empty())
663 {
664 load_project(path);
665 }
666 else
667 {
668 ui::imgui_notify_warning("No project file selected.");
669 }
670 }
671
672 void Editor::render_exporter() const
673 {
674 if (m_show_export)
675 {
676 ui::imgui_center_next_window();
677 if (ImGui::Begin(
678 "Exporting...",
679 nullptr,
680 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings |
681 ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove
682 ))
683 {
684 ImGuiContext& g = *GImGui;
685 ImGuiWindow* window = ImGui::GetCurrentWindow();
686
687 ImGuiStyle& style = g.Style;
688 ImVec2 size = ImGui::CalcItemSize(ImVec2(300, 20), ImGui::CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f);
689 ImVec2 pos = window->DC.CursorPos;
690 ImRect bb(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
691 ImGui::ItemSize(size);
692 if (!ImGui::ItemAdd(bb, 0))
693 return;
694
695 const float speed = g.FontSize * 0.05f;
696 const float phase = ImFmod((float)g.Time * speed, 1.0f);
697 const float width_normalized = 0.2f;
698 float t0 = phase * (1.0f + width_normalized) - width_normalized;
699 float t1 = t0 + width_normalized;
700
701 ImGui::RenderFrame(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
702 bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
703 ImGui::RenderRectFilledRangeH(window->DrawList, bb, ImGui::GetColorU32(ImGuiCol_PlotHistogram), t0, t1, style.FrameRounding);
704 }
705
706 ImGui::End();
707 }
708 }
709
710 void Editor::recursively_zip_assets(zip_t* zip, const std::filesystem::path& path)
711 {
712 for (const auto& entry : std::filesystem::directory_iterator(path))
713 {
714 const auto entry_path = std::filesystem::path(entry);
715
716 if (entry.is_directory())
717 {
718 recursively_zip_assets(zip, entry_path);
719 }
720 else
721 {
722 zip_entry_open(zip, entry_path.relative_path().string().c_str());
723 zip_entry_fwrite(zip, entry_path.string().c_str());
724 zip_entry_close(zip);
725 }
726 }
727 }
728
729 void Editor::set_state_game()
730 {
731 set_input_game();
732 m_state = EditorState::GAME;
733
734 m_tasks.push_back([&]() {
735 if (m_state == EditorState::GAME_IN_EDITOR)
736 {
737 m_project.deserialize(m_backup);
738 }
739 else
740 {
741 m_backup = m_project.serialize();
742 }
743 });
744 }
745
746 void Editor::set_state_editor()
747 {
748 set_input_editor();
749 m_state = EditorState::EDITOR;
750
751 m_tasks.push_back([&]() {
752 m_project.deserialize(m_backup);
753 });
754 }
755
756 void Editor::set_state_game_and_editor()
757 {
758 m_backup = m_project.serialize();
759 m_state = EditorState::GAME_IN_EDITOR;
760 }
761
762 void Editor::set_input_game()
763 {
764 ImGui_ImplGlfw_ToggleInput(false);
765 core::ServiceLocator<ui::NuklearUI>::ref().toggle_input(true);
766 core::ServiceLocator<core::Window>::ref().set_dispatcher(&m_project.current()->m_dispatcher);
767 core::ServiceLocator<core::Window>::ref().get_input<input::Cursor>().use_custom_else_pointer();
768 }
769
770 void Editor::set_input_editor()
771 {
772 ImGui_ImplGlfw_ToggleInput(true);
773 core::ServiceLocator<ui::NuklearUI>::ref().toggle_input(false);
774 core::ServiceLocator<core::Window>::ref().set_dispatcher(nullptr);
775 ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
776 }*/
777} // namespace galaxy
void draw() override
Draw config panel.
Editor()
Constructor.
Definition Editor.cpp:18
void handle_events(const SDL_Event &events) noexcept
Process events in the editor.
Definition Editor.cpp:29
~Editor()
Destructor.
Definition Editor.cpp:24
ConfigPanel m_config_panel
Config panel for manipulating engine settings.
Definition Editor.hpp:71
void end_dock()
Finish rendering the editor dock.
Definition Editor.cpp:107
void render()
Render the editor to the screen.
Definition Editor.cpp:42
ScenePanel m_scene_panel
Scene panel for managing scenes.
Definition Editor.hpp:76
void draw() override
Draw the ImGui widgets to ImGui buffers.
Definition Editor.cpp:52
void begin_dock()
Start rendering the editor dock.
Definition Editor.cpp:102
void draw_menubar()
Draw the editor menubar.
Definition Editor.cpp:117
Popup m_hotkeys
Hotkey info.
Definition Editor.hpp:81
bool m_show
Should the panel be shown.
Definition Panel.hpp:71
void draw_menuitem(const char *text) noexcept
Draw a menu button to show the popup.
Definition Popup.cpp:29
void draw(std::move_only_function< void(void)> &&func)
Show popup.
Definition Popup.cpp:34
void draw() override
Draw scene panel.
void imgui_handle_events(const SDL_Event &events)
Handle events in ImGui.
void imgui_new_frame()
Begin setting up ImGui pass.
void imgui_exit()
Destroy imgui context.
void imgui_init(const char *ini)
Initialize ImGui context with galaxy.
void imgui_render()
Render imgui.
Application.hpp galaxy.
static auto editor_key() noexcept -> SDL_Scancode
Editor debug toggle key.
Definition Settings.cpp:343