Files
sophia/editor/panels/scene_hierarchy_panel.cpp

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;
}
}