135 lines
4.2 KiB
C++
135 lines
4.2 KiB
C++
#include "scene_hierarchy_panel.hpp"
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <cstdio>
|
|
#include <sophia/components.hpp>
|
|
#include <string>
|
|
|
|
void SceneHierarchyPanel::onUpdate(const sophia::FrameInfo&) {
|
|
drawSceneSelector();
|
|
ImGui::Separator();
|
|
|
|
if (!context.hasScene()) {
|
|
ImGui::TextDisabled("No scene open");
|
|
return;
|
|
}
|
|
|
|
sophia::Scene& scene = context.getScene();
|
|
sophia::Entity& selected = context.selected;
|
|
|
|
if (ImGui::Button("+ Sprite")) {
|
|
selected = scene.createEntity("Sprite");
|
|
selected.addComponent<sophia::SpriteComponent>();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("+ Cube")) {
|
|
selected = scene.createEntity("Cube");
|
|
selected.addComponent<sophia::MeshComponent>(
|
|
sophia::MeshComponent{.handle = cubeMesh});
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Flat for now: there is no parent/child component yet.
|
|
scene.forEachEntity([&selected](sophia::Entity entity) {
|
|
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf |
|
|
ImGuiTreeNodeFlags_NoTreePushOnOpen |
|
|
ImGuiTreeNodeFlags_SpanAvailWidth;
|
|
if (entity == selected) {
|
|
flags |= ImGuiTreeNodeFlags_Selected;
|
|
}
|
|
|
|
const std::string& name = entity.getComponent<sophia::TagComponent>().tag;
|
|
ImGui::PushID(static_cast<int>(static_cast<sophia::u32>(entity)));
|
|
ImGui::TreeNodeEx(name.c_str(), flags);
|
|
if (ImGui::IsItemClicked()) {
|
|
selected = entity;
|
|
}
|
|
ImGui::PopID();
|
|
});
|
|
}
|
|
|
|
namespace {
|
|
// See SceneHierarchyPanel::renameFocusFrames.
|
|
constexpr int RENAME_FOCUS_FRAMES = 2;
|
|
} // namespace
|
|
|
|
void SceneHierarchyPanel::drawSceneSelector() {
|
|
// Leave room for the button: its label, its padding, and the gap to it.
|
|
constexpr const char* ADD_SCENE_LABEL = "Add Scene";
|
|
const ImGuiStyle& style = ImGui::GetStyle();
|
|
ImGui::SetNextItemWidth(-(ImGui::CalcTextSize(ADD_SCENE_LABEL).x +
|
|
style.FramePadding.x * 2.f + style.ItemSpacing.x));
|
|
|
|
const std::string* current = context.getSceneName(context.getSceneHandle());
|
|
bool startRename = false;
|
|
|
|
if (renamingScene && current) {
|
|
drawSceneRename();
|
|
} else {
|
|
renamingScene = false;
|
|
if (ImGui::BeginCombo("##scene", current ? current->c_str() : "No scene")) {
|
|
for (const EditorContext::OpenScene& open : context.getOpenScenes()) {
|
|
ImGui::PushID(static_cast<int>(open.handle.index));
|
|
bool isCurrent = open.handle == context.getSceneHandle();
|
|
if (ImGui::Selectable(open.name.c_str(), isCurrent) && !isCurrent) {
|
|
context.setScene(open.handle);
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
|
|
if (current && ImGui::BeginPopupContextItem("##sceneMenu")) {
|
|
startRename = ImGui::MenuItem("Rename", "F2");
|
|
ImGui::EndPopup();
|
|
}
|
|
startRename = startRename || (current && ImGui::IsWindowFocused() &&
|
|
!ImGui::GetIO().WantTextInput &&
|
|
ImGui::IsKeyPressed(ImGuiKey_F2));
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(ADD_SCENE_LABEL)) {
|
|
std::string name = "Untitled " + std::to_string(++this->untitledSceneCount);
|
|
context.setScene(context.createScene(std::move(name)));
|
|
startRename = false;
|
|
}
|
|
|
|
if (startRename) {
|
|
renamingScene = true;
|
|
renameFocusFrames = RENAME_FOCUS_FRAMES;
|
|
std::snprintf(renameBuffer.data(), renameBuffer.size(), "%s",
|
|
current->c_str());
|
|
}
|
|
}
|
|
|
|
void SceneHierarchyPanel::drawSceneRename() {
|
|
if (renameFocusFrames == RENAME_FOCUS_FRAMES) {
|
|
ImGui::SetKeyboardFocusHere();
|
|
}
|
|
|
|
ImGui::InputText("##sceneName", renameBuffer.data(), renameBuffer.size(),
|
|
ImGuiInputTextFlags_AutoSelectAll);
|
|
|
|
bool active = ImGui::IsItemActive();
|
|
if (active) {
|
|
renameFocusFrames = 0;
|
|
} else if (renameFocusFrames > 0) {
|
|
renameFocusFrames--;
|
|
return;
|
|
}
|
|
|
|
// Escape reverts the text before deactivating, so committing whatever is
|
|
// left when the field closes covers Enter, clicking away and Escape alike.
|
|
if (!active) {
|
|
std::string name = renameBuffer.data();
|
|
// Keep the old name rather than leave the scene nameless.
|
|
if (name.find_first_not_of(' ') != std::string::npos) {
|
|
context.renameScene(context.getSceneHandle(), std::move(name));
|
|
}
|
|
renamingScene = false;
|
|
}
|
|
}
|