changes for ImGui
This commit is contained in:
@@ -3,10 +3,13 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sophia/asset_registry.hpp>
|
||||
#include <sophia/camera_2d.hpp>
|
||||
#include <sophia/canvas.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/types.hpp>
|
||||
#include <sophia/ui/debug_ui.hpp>
|
||||
#include <sophia/ui/panel_registry.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -27,7 +30,7 @@ class Application {
|
||||
Application(const Application&) = delete;
|
||||
Application& operator=(const Application&) = delete;
|
||||
|
||||
Application(const Config& config);
|
||||
explicit Application(const Config& config);
|
||||
virtual ~Application();
|
||||
|
||||
void run();
|
||||
@@ -40,15 +43,15 @@ class Application {
|
||||
virtual void onRender() {}
|
||||
virtual void onDestroy() {}
|
||||
|
||||
AssetRegistry& assets() { return this->assetRegistry; }
|
||||
PanelRegistry& panels() { return this->panelRegistry; }
|
||||
|
||||
DebugUI& debugUI() { return this->_debugUI; }
|
||||
|
||||
Canvas canvas;
|
||||
Camera2D camera;
|
||||
std::vector<std::string> args;
|
||||
|
||||
// Creates a mesh that persists across frames, for callers that want to
|
||||
// draw the same geometry many times with different transforms (e.g.
|
||||
// instancing) instead of resubmitting vertices every frame via Canvas.
|
||||
MeshHandle createMesh(std::vector<CanvasVertex> vertices,
|
||||
std::vector<u32> indices);
|
||||
void drawMesh(MeshHandle handle, const glm::mat4& transform);
|
||||
|
||||
private:
|
||||
@@ -59,6 +62,11 @@ class Application {
|
||||
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
AssetRegistry assetRegistry;
|
||||
PanelRegistry panelRegistry;
|
||||
|
||||
DebugUI _debugUI;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/ui/panel_registry.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class DebugUI {
|
||||
public:
|
||||
DebugUI(const DebugUI&) = delete;
|
||||
DebugUI& operator=(const DebugUI&) = delete;
|
||||
|
||||
explicit DebugUI(PanelRegistry& registry);
|
||||
~DebugUI() = default;
|
||||
|
||||
void show();
|
||||
void hide();
|
||||
|
||||
void showPanelRegistry(bool show = true);
|
||||
void showFrameStats(bool show = true);
|
||||
|
||||
private:
|
||||
PanelRegistry& registry;
|
||||
|
||||
PanelHandle panelRegistryHandle;
|
||||
PanelHandle frameStatsHandle;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class Panel {
|
||||
public:
|
||||
Panel(const Panel&) = delete;
|
||||
Panel& operator=(const Panel&) = delete;
|
||||
|
||||
Panel(Panel&&) noexcept = default;
|
||||
Panel& operator=(Panel&&) noexcept = default;
|
||||
|
||||
explicit Panel(std::string name = {}) : name{std::move(name)} {}
|
||||
virtual ~Panel() = default;
|
||||
|
||||
virtual void onUpdate(const FrameInfo& frameInfo) = 0;
|
||||
|
||||
const std::string& getName() const { return name; }
|
||||
|
||||
bool isVisible() const { return this->visible; }
|
||||
void setVisible(bool visible) { this->visible = visible; }
|
||||
void toggleVisible() { this->visible = !this->visible; }
|
||||
|
||||
protected:
|
||||
bool visible = true;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <string_view>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class PanelRegistry {
|
||||
public:
|
||||
PanelRegistry(const PanelRegistry&) = delete;
|
||||
PanelRegistry& operator=(const PanelRegistry&) = delete;
|
||||
|
||||
PanelRegistry() = default;
|
||||
~PanelRegistry() = default;
|
||||
|
||||
PanelHandle add(std::unique_ptr<Panel> panel);
|
||||
|
||||
template <typename T, typename... Args>
|
||||
PanelHandle emplace(Args&&... args) {
|
||||
return add(std::make_unique<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
const Panel& get(PanelHandle handle) const;
|
||||
Panel& get(PanelHandle handle) {
|
||||
return const_cast<Panel&>(std::as_const(*this).get(handle));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& get(PanelHandle handle) const {
|
||||
const Panel& panel = get(handle);
|
||||
SOPHIA_ASSERT(dynamic_cast<const T*>(&panel) != nullptr,
|
||||
"PanelHandle does not refer to a", typeid(T).name());
|
||||
return static_cast<const T&>(panel);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& get(PanelHandle handle) {
|
||||
return const_cast<T&>(std::as_const(*this).template get<T>(handle));
|
||||
}
|
||||
|
||||
void remove(PanelHandle handle);
|
||||
bool isValid(PanelHandle handle) const;
|
||||
|
||||
template <typename T>
|
||||
const T* find() const {
|
||||
for (const Slot& slot : slots) {
|
||||
if (slot.panel) {
|
||||
if (const T* panel = dynamic_cast<const T*>(slot.panel.get())) {
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* find() {
|
||||
return const_cast<T*>(std::as_const(*this).template find<T>());
|
||||
}
|
||||
|
||||
const Panel* find(std::string_view name) const {
|
||||
for (const Slot& slot : slots) {
|
||||
if (slot.panel && slot.panel->getName() == name) {
|
||||
return slot.panel.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Panel* find(std::string_view name) {
|
||||
return const_cast<Panel*>(std::as_const(*this).find(name));
|
||||
}
|
||||
|
||||
u32 size() const;
|
||||
u32 visibleCount() const;
|
||||
|
||||
std::vector<std::string_view> names() const {
|
||||
std::vector<std::string_view> result;
|
||||
result.reserve(slots.size());
|
||||
for (const Slot& slot : slots) {
|
||||
if (slot.panel) {
|
||||
result.push_back(slot.panel->getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void update(const FrameInfo& frameInfo);
|
||||
|
||||
private:
|
||||
struct Slot {
|
||||
std::unique_ptr<Panel> panel;
|
||||
u32 generation = 0;
|
||||
};
|
||||
|
||||
std::vector<Slot> slots;
|
||||
std::vector<u32> freeList;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "imgui_renderer.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
#include <imgui_impl_vulkan.h>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
ImGuiRenderer::ImGuiRenderer(Window& window, Device& device,
|
||||
vk::Format colorAttachmentFormat,
|
||||
u32 minImageCount) {
|
||||
// 1. Construct ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplGlfw_InitForVulkan(window.getGLFWwindow(), true);
|
||||
|
||||
ImGui_ImplVulkan_InitInfo initInfo = {};
|
||||
device.populateImGuiInitInfo(initInfo);
|
||||
initInfo.DescriptorPoolSize =
|
||||
IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE + 5;
|
||||
initInfo.MinImageCount = minImageCount;
|
||||
initInfo.ImageCount = minImageCount;
|
||||
|
||||
VkFormat rawColorAttachmentFormat =
|
||||
static_cast<VkFormat>(colorAttachmentFormat);
|
||||
initInfo.UseDynamicRendering = true;
|
||||
initInfo.PipelineInfoMain.PipelineRenderingCreateInfo.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
|
||||
initInfo.PipelineInfoMain.PipelineRenderingCreateInfo.colorAttachmentCount =
|
||||
1;
|
||||
initInfo.PipelineInfoMain.PipelineRenderingCreateInfo
|
||||
.pColorAttachmentFormats = &rawColorAttachmentFormat;
|
||||
|
||||
ImGui_ImplVulkan_Init(&initInfo);
|
||||
|
||||
// 2. Enable dark theme
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
// 3. Enable docking / keyboard
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
|
||||
/**
|
||||
* When viewports are enabled we tweak WindowRounding/WindowBg so
|
||||
* platform windows can look identical to regular ones.
|
||||
*/
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
style.WindowRounding = 0.0f;
|
||||
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiRenderer::~ImGuiRenderer() {
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
void ImGuiRenderer::beginFrame() {
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(),
|
||||
ImGuiDockNodeFlags_PassthruCentralNode);
|
||||
}
|
||||
|
||||
void ImGuiRenderer::endFrame(vk::CommandBuffer commandBuffer) {
|
||||
ImGui::Render();
|
||||
ImDrawData* mainDrawData = ImGui::GetDrawData();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
ImGui_ImplVulkan_RenderDrawData(mainDrawData, commandBuffer);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/types.hpp>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class ImGuiRenderer {
|
||||
public:
|
||||
ImGuiRenderer(const ImGuiRenderer&) = delete;
|
||||
ImGuiRenderer& operator=(const ImGuiRenderer&) = delete;
|
||||
|
||||
ImGuiRenderer(Window& window, Device& device,
|
||||
vk::Format colorAttachmentFormat, u32 minImageCount);
|
||||
~ImGuiRenderer();
|
||||
|
||||
void beginFrame();
|
||||
void endFrame(vk::CommandBuffer commandBuffer);
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -45,6 +45,9 @@ class RenderContext {
|
||||
vk::Format getImageFormat() const {
|
||||
return this->swapchain->getImageFormat();
|
||||
}
|
||||
u32 getImageCount() const {
|
||||
return static_cast<u32>(this->swapchain->imageCount());
|
||||
}
|
||||
|
||||
private:
|
||||
void recreateSwapchain();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#include <sophia/ui/debug_ui.hpp>
|
||||
|
||||
#include "panels/frame_stats_panel.hpp"
|
||||
#include "panels/panel_registry_panel.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
DebugUI::DebugUI(PanelRegistry& registry) : registry{registry} {
|
||||
// #ifndef NDEBUG
|
||||
this->panelRegistryHandle = registry.emplace<PanelRegistryPanel>(registry);
|
||||
this->frameStatsHandle = registry.emplace<FrameStatsPanel>();
|
||||
// #endif // !NDEBUG
|
||||
}
|
||||
|
||||
void DebugUI::show() {
|
||||
showPanelRegistry(true);
|
||||
showFrameStats(true);
|
||||
}
|
||||
|
||||
void DebugUI::hide() {
|
||||
showPanelRegistry(false);
|
||||
showFrameStats(false);
|
||||
}
|
||||
|
||||
void DebugUI::showPanelRegistry(bool show) {
|
||||
if (registry.isValid(panelRegistryHandle)) {
|
||||
registry.get(panelRegistryHandle).setVisible(show);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugUI::showFrameStats(bool show) {
|
||||
if (registry.isValid(frameStatsHandle)) {
|
||||
registry.get(frameStatsHandle).setVisible(show);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <sophia/ui/panel_registry.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
namespace {
|
||||
constexpr u32 GENERATION_MASK = (1u << 12) - 1;
|
||||
} // namespace
|
||||
|
||||
PanelHandle PanelRegistry::add(std::unique_ptr<Panel> panel) {
|
||||
if (!freeList.empty()) {
|
||||
u32 index = freeList.back();
|
||||
freeList.pop_back();
|
||||
slots[index].panel = std::move(panel);
|
||||
return PanelHandle{index, slots[index].generation};
|
||||
}
|
||||
|
||||
slots.push_back(Slot{std::move(panel), 0});
|
||||
return PanelHandle{static_cast<u32>(slots.size() - 1), 0};
|
||||
}
|
||||
|
||||
const Panel& PanelRegistry::get(PanelHandle handle) const {
|
||||
SOPHIA_ASSERT(handle.isValid(), "invalid PanelHandle");
|
||||
SOPHIA_ASSERT(handle.index < slots.size(),
|
||||
"PanelHandle index out of range:", handle.index);
|
||||
|
||||
const Slot& slot = slots[handle.index];
|
||||
SOPHIA_ASSERT(slot.generation == handle.generation,
|
||||
"stale PanelHandle (generation mismatch):", handle.index);
|
||||
SOPHIA_ASSERT(slot.panel != nullptr,
|
||||
"PanelHandle refers to a removed panel:", handle.index);
|
||||
return *slot.panel;
|
||||
}
|
||||
|
||||
void PanelRegistry::remove(PanelHandle handle) {
|
||||
SOPHIA_ASSERT(handle.isValid(), "invalid PanelHandle");
|
||||
SOPHIA_ASSERT(handle.index < slots.size(),
|
||||
"PanelHandle index out of range:", handle.index);
|
||||
|
||||
Slot& slot = slots[handle.index];
|
||||
SOPHIA_ASSERT(slot.generation == handle.generation,
|
||||
"stale PanelHandle (generation mismatch):", handle.index);
|
||||
SOPHIA_ASSERT(slot.panel != nullptr,
|
||||
"PanelHandle already removed:", handle.index);
|
||||
|
||||
slot.panel.reset();
|
||||
slot.generation = (slot.generation + 1) & GENERATION_MASK;
|
||||
freeList.push_back(handle.index);
|
||||
}
|
||||
|
||||
bool PanelRegistry::isValid(PanelHandle handle) const {
|
||||
if (!handle.isValid() || handle.index >= slots.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Slot& slot = slots[handle.index];
|
||||
return slot.generation == handle.generation && slot.panel != nullptr;
|
||||
}
|
||||
|
||||
u32 PanelRegistry::size() const {
|
||||
u32 count = 0;
|
||||
for (const Slot& slot : slots) {
|
||||
if (slot.panel) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 PanelRegistry::visibleCount() const {
|
||||
u32 count = 0;
|
||||
for (const Slot& slot : slots) {
|
||||
if (slot.panel && slot.panel->isVisible()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void PanelRegistry::update(const FrameInfo& frameInfo) {
|
||||
for (auto& slot : slots) {
|
||||
if (slot.panel && slot.panel->isVisible()) {
|
||||
slot.panel->onUpdate(frameInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "ecs_inspector_panel.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <sophia/components.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
void EcsInspectorPanel::onUpdate(const FrameInfo&) {
|
||||
ImGui::Begin("ECS Inspector");
|
||||
|
||||
entityScratch.clear();
|
||||
for (entt::entity entity : registry.storage<entt::entity>()) {
|
||||
entityScratch.push_back(entity);
|
||||
}
|
||||
|
||||
ImGui::BeginChild("EntityList", ImVec2(0, 200), true);
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(static_cast<int>(entityScratch.size()));
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
entt::entity entity = entityScratch[static_cast<size_t>(i)];
|
||||
const TagComponent* tag = registry.try_get<TagComponent>(entity);
|
||||
std::string label = tag ? tag->tag : "Entity";
|
||||
|
||||
ImGui::PushID(static_cast<int>(entt::to_integral(entity)));
|
||||
if (ImGui::Selectable(label.c_str(), selected == entity)) {
|
||||
selected = entity;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (registry.valid(selected)) {
|
||||
if (auto* transform = registry.try_get<TransformComponent>(selected)) {
|
||||
ImGui::DragFloat3("Position", &transform->position.x, 0.1f);
|
||||
ImGui::DragFloat3("Scale", &transform->scale.x, 0.1f);
|
||||
ImGui::Text("Rotation: %.2f, %.2f, %.2f, %.2f", transform->rotation.w,
|
||||
transform->rotation.x, transform->rotation.y,
|
||||
transform->rotation.z);
|
||||
}
|
||||
|
||||
if (auto* mesh = registry.try_get<MeshComponent>(selected)) {
|
||||
ImGui::Text("Mesh: index=%u generation=%u", mesh->handle.index,
|
||||
mesh->handle.generation);
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("No entity selected");
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class EcsInspectorPanel : public Panel {
|
||||
public:
|
||||
explicit EcsInspectorPanel(entt::registry& registry)
|
||||
: Panel{"ECS Inspector"}, registry{registry} {}
|
||||
|
||||
void onUpdate(const FrameInfo& frameInfo) override;
|
||||
|
||||
private:
|
||||
entt::registry& registry;
|
||||
entt::entity selected = entt::null;
|
||||
std::vector<entt::entity> entityScratch;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class FrameStatsPanel : public Panel {
|
||||
public:
|
||||
FrameStatsPanel() : Panel{"Frame Stats"} {}
|
||||
|
||||
void onUpdate(const FrameInfo& frameInfo) override {
|
||||
frameCount++;
|
||||
accumulatedTime += frameInfo.deltaTime;
|
||||
|
||||
if (frameInfo.elapsedTime >= nextRefreshTime) {
|
||||
displayedFps = static_cast<double>(frameCount) / accumulatedTime;
|
||||
displayedFrameTimeMs =
|
||||
(accumulatedTime / static_cast<double>(frameCount)) * 1000.0;
|
||||
|
||||
frameCount = 0;
|
||||
accumulatedTime = 0.0;
|
||||
nextRefreshTime = frameInfo.elapsedTime + REFRESH_INTERVAL;
|
||||
}
|
||||
|
||||
ImGui::Begin("Frame Stats");
|
||||
ImGui::Text("FPS: %.1f", displayedFps);
|
||||
ImGui::Text("Frame time: %.3f ms", displayedFrameTimeMs);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REFRESH_INTERVAL = 1.0;
|
||||
|
||||
double accumulatedTime = 0.0;
|
||||
u32 frameCount = 0;
|
||||
double nextRefreshTime = 0.0;
|
||||
double displayedFps = 0.0;
|
||||
double displayedFrameTimeMs = 0.0;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "panel_registry_panel.hpp"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
void PanelRegistryPanel::onUpdate(const FrameInfo&) {
|
||||
ImGui::Begin("PanelRegistry");
|
||||
ImGui::Text("%u panels (%u visible)", registry.size(),
|
||||
registry.visibleCount());
|
||||
ImGui::Separator();
|
||||
|
||||
for (std::string_view name : registry.names()) {
|
||||
Panel* panel = registry.find(name);
|
||||
if (!panel || panel == this) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool visible = panel->isVisible();
|
||||
if (ImGui::Checkbox(name.data(), &visible)) {
|
||||
panel->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <sophia/ui/panel_registry.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class PanelRegistryPanel : public Panel {
|
||||
public:
|
||||
explicit PanelRegistryPanel(PanelRegistry& registry)
|
||||
: Panel{"PanelRegistry"}, registry{registry} {}
|
||||
|
||||
void onUpdate(const FrameInfo& frameInfo) override;
|
||||
|
||||
private:
|
||||
PanelRegistry& registry;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
Reference in New Issue
Block a user