Adds basic editor window classes

Adds the initial structure for the InspectorWindow and
SceneExplorer editor windows, providing a foundation for
implementing editor functionality.
Also adds file browser and other editor windows
This commit is contained in:
OusmBlueNinja
2025-06-03 16:09:00 -05:00
parent 19d1b9df55
commit 8232b19666
26 changed files with 564 additions and 279 deletions

View File

@@ -131,6 +131,10 @@ add_executable(Editor ${APP_SOURCES}
src/editor/Windows/Viewport.h
src/editor/Windows/FileBrowser.cpp
src/editor/Windows/FileBrowser.h
src/editor/Windows/InspectorWindow.cpp
src/editor/Windows/InspectorWindow.h
src/editor/Windows/SceneExplorer.cpp
src/editor/Windows/SceneExplorer.h
)
target_include_directories(Editor PRIVATE

View File

@@ -22,8 +22,18 @@
it's 32 chars long and is unique for every loaded asset.
The Engine will auto create everything. Im debating between a unity asset system, where the actual data and metadata
are packed into one file. the other option is to have a system like Hazels, where you import a file and it will
create a file like a Hazel asset that links to an image file or a 3d model.
```yaml
# MyGame.onx
project:
name: MyGame

View File

@@ -7,11 +7,15 @@
int main()
{
OX::Core core{};
using namespace OX;
core.AddLayer(std::make_unique<OX::Editor>(OX_EDITOR_VERSION));
// Note: You must add the layers to the Core BEFORE initializing the core.
Core core;
//core.AddLayer(std::make_unique<OX::DummyLayer>("Dummy Layer"));
core.AddLayer(std::make_unique<Editor>(OX_EDITOR_VERSION));
//core.AddLayer(std::make_unique<OX::DummyLayer>());
core.Init();
core.Run();

View File

@@ -10,7 +10,6 @@
namespace OX
{
void Core::AddLayer(std::unique_ptr<Layer> layer)
{
Logger::LogDebug("Added Layer: '%s'", layer->GetName().c_str());
@@ -24,13 +23,13 @@ namespace OX
if (!window.Init(m_name, 1280, 720))
return;
OX_ASSERT(!m_layers.empty(), "No Layers Attached");
OX_ENSURE(!m_layers.empty(), "No Layers Attached");
for (const auto &layer: m_layers) {
Logger::LogDebug("Initializing Layer: '%s'", layer->GetName().c_str());
layer->Init(*this);
}
std::string layerTitle;
for (size_t i = 0; i < m_layers.size(); ++i) {
if (i > 0)
@@ -39,9 +38,6 @@ namespace OX
}
std::string fullTitle = layerTitle + " - " + m_name;
window.SetWindowTitle(fullTitle);
renderer.Init(800, 600);
@@ -52,7 +48,6 @@ namespace OX
}
void Core::Run()
{
m_running = true;
@@ -73,12 +68,15 @@ namespace OX
void Core::Update()
{
OX_PROFILE_FUNCTION();
AssetManager::Tick();
for (auto &layer: m_layers) {
layer->Update(*this);
}
}
void Core::Draw()
{
OX_PROFILE_FUNCTION();
@@ -92,7 +90,6 @@ namespace OX
}
renderer.EndScene();
@@ -107,11 +104,9 @@ namespace OX
for (auto &layer: m_layers) {
Logger::LogDebug("Shutting down Layer: '%s'", layer->GetName().c_str());
layer->Shutdown(*this);
Logger::LogOk("'%s' Shutdown Complete", layer->GetName().c_str());
}
m_layers.clear();

View File

@@ -4,6 +4,8 @@
#ifndef CORE_H
#define CORE_H
#define OX_ENGINE_VERSION "Onyx Engine (2025.1)"
#include <utility>
#include <vector>
@@ -16,7 +18,6 @@
#include "systems/MACROS.h"
#include "systems/Scene/Scene.h"
#define OX_ENGINE_VERSION "Onyx Engine (2025.1)"
namespace OX
{
@@ -42,6 +43,7 @@ namespace OX
WindowManager &GetWindow() { return window; }
Renderer &GetRenderer() { return renderer; }
Scene &GetScene() { return m_activeScene; }
void AddLayer(std::unique_ptr<Layer> layer);

View File

@@ -4,42 +4,44 @@
#ifndef LAYER_H
#define LAYER_H
#include <cstdint>
#include <string>
#include <utility>
namespace OX
{
class Core;
class Core;
class Layer
{
public:
explicit Layer(std::string name = "New Layer")
: m_name(std::move(name))
class Layer
{
}
public:
explicit Layer(std::string name = "New Layer")
: m_name(std::move(name))
{
}
explicit Layer(const char *name)
: m_name(name)
{
}
explicit Layer(const char *name)
: m_name(name)
{
virtual ~Layer() = default;
}
virtual void Init(Core &core) = 0;
virtual ~Layer() = default;
virtual void Update(Core &core) = 0;
virtual void Init(Core &core) = 0;
virtual void Draw(Core &core) = 0;
virtual void Update(Core &core) = 0;
virtual void Shutdown(Core &core) = 0;
virtual void Draw(Core &core) = 0;
[[nodiscard]] const std::string &GetName() const { return m_name; }
virtual void Shutdown(Core &core) = 0;
protected:
std::string m_name;
[[nodiscard]] const std::string &GetName() const { return m_name; }
protected:
std::string m_name;
};
};
}
#endif // LAYER_H
#endif // LAYER_H

View File

@@ -4,84 +4,89 @@
#include <ctime>
#include <cstdarg>
#include "types/all.h"
namespace OX
{
std::vector<Message> Logger::m_messages;
std::string Logger::m_sectionName = "\033[35mONYX";
inline const char* ToString(MessageType type) {
inline const char *ToString(MessageType type)
{
switch (type) {
case MessageType::Info: return "Info";
case MessageType::Info: return "Info";
case MessageType::Warning: return "Warning";
case MessageType::Error: return "Error";
case MessageType::Debug: return "Debug";
case MessageType::Ok: return "Ok";
case MessageType::Error: return "Error";
case MessageType::Debug: return "Debug";
case MessageType::Ok: return "Ok";
case MessageType::Verbose: return "Verbose";
default: return "Unknown";
default: return "Unknown";
}
}
static std::string GetTimestamp() {
static std::string GetTimestamp()
{
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&t);
std::tm *tm = std::localtime(&t);
char buffer[20];
std::strftime(buffer, sizeof(buffer), "%H:%M:%S", tm);
return buffer;
}
static const char* GetAnsiColor(MessageType type) {
static const char *GetAnsiColor(MessageType type)
{
switch (type) {
case MessageType::Info: return "\033[1;36m"; // Bright cyan
case MessageType::Warning: return "\033[1;33m"; // Bright yellow
case MessageType::Error: return "\033[1;91m"; // Bright red
case MessageType::Debug: return "\033[1;90m"; // Bright gray
case MessageType::Verbose: return "\033[0;37m"; // Standard gray (lighter than debug)
case MessageType::Ok: return "\033[1;32m"; // Bright green
default: return "\033[0m"; // Reset
case MessageType::Info: return "\033[1;36m"; // Bright cyan
case MessageType::Warning: return "\033[1;33m"; // Bright yellow
case MessageType::Error: return "\033[1;91m"; // Bright red
case MessageType::Debug: return "\033[1;90m"; // Bright gray
case MessageType::Verbose: return "\033[0;37m"; // Standard gray (lighter than debug)
case MessageType::Ok: return "\033[1;32m"; // Bright green
default: return "\033[0m"; // Reset
}
}
Color Logger::GetRGBColor(const MessageType type) {
Color Logger::GetRGBColor(const MessageType type)
{
Color color;
switch (type) {
case MessageType::Info:
color = { 0.0f, 0.478f, 1.0f }; // Standard blue (iOS info blue / material blue)
color = {0.0f, 0.478f, 1.0f}; // Standard blue (iOS info blue / material blue)
break;
case MessageType::Warning:
color = { 1.0f, 0.647f, 0.0f }; // Orange / amber (alert warning)
color = {1.0f, 0.647f, 0.0f}; // Orange / amber (alert warning)
break;
case MessageType::Error:
color = { 0.863f, 0.078f, 0.235f }; // Crimson / alert red
color = {0.863f, 0.078f, 0.235f}; // Crimson / alert red
break;
case MessageType::Debug:
color = { 0.56f, 0.56f, 0.58f }; // Slate gray
color = {0.56f, 0.56f, 0.58f}; // Slate gray
break;
case MessageType::Ok:
color = { 0.0f, 0.8f, 0.4f }; // Vibrant green / success
color = {0.0f, 0.8f, 0.4f}; // Vibrant green / success
break;
case MessageType::Verbose:
color = { 0.6f, 0.6f, 0.7f }; // Dim blue-gray
color = {0.6f, 0.6f, 0.7f}; // Dim blue-gray
break;
default:
color = { 1.0f, 1.0f, 1.0f }; // Default white
color = {1.0f, 1.0f, 1.0f}; // Default white
break;
}
return color;
}
void Logger::LogInternal(const std::string& text, MessageType type) {
void Logger::LogInternal(const std::string &text, MessageType type)
{
glm::vec3 color = Logger::GetRGBColor(type);
float r = color.r;
float g = color.g;
float b = color.b;
Message message {
Message message{
GetTimestamp(),
text,
type,
@@ -90,19 +95,19 @@ namespace OX
m_messages.push_back(message);
std::cout << "\033[37m[\033[35mONYX\033[37m]"
<< "\033[37m[\033[36m" << message.timestamp
<< "\033[37m]\033[37m[\033[33m"
<< GetAnsiColor(type)
<< ToString(message.type)
<< "\033[37m] "
<< GetAnsiColor(type)
<< text
<< "\033[0m\n";
std::cout << "\033[37m[\033[0m" << m_sectionName << "\033[37m]\033[0m"
<< "\033[37m[\033[36m" << message.timestamp
<< "\033[37m]\033[37m[\033[33m"
<< GetAnsiColor(type)
<< ToString(message.type)
<< "\033[37m] "
<< GetAnsiColor(type)
<< text
<< "\033[0m\n";
}
void Logger::Logf(MessageType type, const char* format, ...) {
void Logger::Logf(MessageType type, const char *format, ...)
{
char buffer[1024];
va_list args;
@@ -113,7 +118,8 @@ namespace OX
LogInternal(std::string(buffer), type);
}
void Logger::LogInfo(const char* format, ...) {
void Logger::LogInfo(const char *format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
@@ -122,7 +128,8 @@ namespace OX
LogInternal(buffer, MessageType::Info);
}
void Logger::LogWarning(const char* format, ...) {
void Logger::LogWarning(const char *format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
@@ -131,7 +138,8 @@ namespace OX
LogInternal(buffer, MessageType::Warning);
}
void Logger::LogError(const char* format, ...) {
void Logger::LogError(const char *format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
@@ -140,7 +148,8 @@ namespace OX
LogInternal(buffer, MessageType::Error);
}
void Logger::LogDebug(const char* format, ...) {
void Logger::LogDebug(const char *format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
@@ -149,7 +158,8 @@ namespace OX
LogInternal(buffer, MessageType::Debug);
}
void Logger::LogVerbose(const char* format, ...) {
void Logger::LogVerbose(const char *format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
@@ -159,7 +169,8 @@ namespace OX
}
void Logger::LogOk(const char* format, ...) {
void Logger::LogOk(const char *format, ...)
{
va_list args;
va_start(args, format);
char buffer[1024];
@@ -168,23 +179,22 @@ namespace OX
LogInternal(buffer, MessageType::Ok);
}
const std::vector<Message>& Logger::GetMessages() {
const std::vector<Message> &Logger::GetMessages()
{
return m_messages;
}
std::string Logger::MessageTypeToString(const MessageType type) {
std::string Logger::MessageTypeToString(const MessageType type)
{
switch (type) {
case MessageType::Info: return "Info";
case MessageType::Info: return "Info";
case MessageType::Warning: return "Warning";
case MessageType::Error: return "Error";
case MessageType::Debug: return "Debug";
case MessageType::Verbose: return "Verbose";
case MessageType::Ok: return "Ok";
default: return "Unknown";
case MessageType::Error: return "Error";
case MessageType::Debug: return "Debug";
case MessageType::Verbose: return "Verbose";
case MessageType::Ok: return "Ok";
default: return "Unknown";
}
}
}
}

View File

@@ -7,45 +7,56 @@
namespace OX
{
enum class MessageType
{
Info,
Warning,
Error,
Debug,
Verbose,
Ok
};
struct Message
{
std::string timestamp;
std::string text;
MessageType type;
float r, g, b;
};
class Logger
{
public:
static void SetSection(const std::string &sectionName) { m_sectionName = sectionName; }
static void Logf(MessageType type, const char *format, ...);
static void LogInfo(const char *format, ...);
static void LogWarning(const char *format, ...);
static void LogError(const char *format, ...);
static void LogDebug(const char *format, ...);
static void LogVerbose(const char *format, ...);
static void LogOk(const char *format, ...);
static void Clear() { m_messages.clear(); }
static std::string MessageTypeToString(MessageType type);
static Color GetRGBColor(MessageType type);
enum class MessageType {
Info,
Warning,
Error,
Debug,
Verbose,
Ok
};
static const std::vector<Message> &GetMessages();
struct Message {
std::string timestamp;
std::string text;
MessageType type;
float r, g, b;
};
private:
static void LogInternal(const std::string &text, MessageType type);
class Logger {
public:
static void Logf(MessageType type, const char* format, ...);
static void LogInfo(const char* format, ...);
static void LogWarning(const char* format, ...);
static void LogError(const char* format, ...);
static void LogDebug(const char* format, ...);
static void LogVerbose(const char* format, ...);
static void LogOk(const char* format, ...);
static void Clear() {m_messages.clear();}
static std::string MessageTypeToString(MessageType type);
static Color GetRGBColor(MessageType type);
static const std::vector<Message>& GetMessages();
private:
static void LogInternal(const std::string& text, MessageType type);
static std::vector<Message> m_messages;
};
static std::vector<Message> m_messages;
static std::string m_sectionName;
};
}

View File

@@ -2,47 +2,62 @@
// Created by spenc on 5/18/2025.
//
#ifndef MACROS_H
#define MACROS_H
#ifndef OX_MACROS_H
#define OX_MACROS_H
#include <iostream>
#include <cstdlib>
// =====================
// Platform Breakpoints
// =====================
#if defined(_WIN32)
#define OX_DEBUG_BREAK() __debugbreak()
#elif defined(__unix__) || defined(__APPLE__)
#include <signal.h>
#define OX_DEBUG_BREAK() raise(SIGTRAP)
#include <signal.h>
#define OX_DEBUG_BREAK() raise(SIGTRAP)
#else
#define OX_DEBUG_BREAK() ((void)0)
#define OX_DEBUG_BREAK() ((void)0)
#endif
// ========== ASSERT ==========
// In debug builds: breaks and logs.
// In release builds: removed entirely.
// =====================
// ASSERT (debug-only)
// =====================
#ifdef _DEBUG
#define OX_ASSERT(condition, message) \
do { \
if (!(condition)) { \
std::cerr << "\n[ASSERTION] " << #condition << "\n" \
<< "Message: " << message << "\n" \
<< "File: " << __FILE__ << ":" << __LINE__ << "\n"; \
OX_DEBUG_BREAK(); \
} \
} while (0)
#define OX_ASSERT(condition, message) \
do { \
if (!(condition)) { \
std::cerr << "\n\033[1;31m[ASSERTION FAILED]\033[0m " \
<< #condition << "\n" \
<< "Message: " << message << "\n" \
<< "File: " << __FILE__ << ":" << __LINE__ << "\n"; \
OX_DEBUG_BREAK(); \
} \
} while (0)
#else
#define OX_ASSERT(condition, message) ((void)0)
#define OX_ASSERT(condition, message) ((void)0)
#endif
// ========== VERIFY ==========
// In debug builds: asserts.
// In release builds: just evaluates the condition.
// =====================
// VERIFY (assert in debug, evaluates in release)
// =====================
#ifdef _DEBUG
#define OX_VERIFY(condition, message) OX_ASSERT(condition, message)
#else
#define OX_VERIFY(condition, message) ((void)(condition))
#define OX_VERIFY(condition, message) ((void)(condition))
#endif
// =====================
// always runs, logs if fails
// =====================
#define OX_ENSURE(condition, message) \
do { \
if (!(condition)) { \
std::cerr << "\n\033[1;33m[ENSURE FAILED]\033[0m " \
<< #condition << "\n" \
<< "Message: " << message << "\n" \
<< "File: " << __FILE__ << ":" << __LINE__ << "\n"; \
} \
} while (0)
#endif //MACROS_H
#endif // OX_MACROS_H

View File

@@ -1,4 +1,3 @@
#pragma once
#include <string>
@@ -18,7 +17,6 @@ namespace OX
{
}
/// The tag string youll look up by
std::string name;
/// Serialize into YAML: writes { type: "TagComponent", name: "<your tag>" }

View File

@@ -1,4 +1,3 @@
#include "GameObject.h"
#include "Components/TagComponent.h"
@@ -9,6 +8,7 @@ namespace OX
GameObject::GameObject(const std::string &name)
: m_name(name)
{
addComponent(std::make_unique<TagComponent>(name));
}
GameObject::~GameObject() = default;
@@ -26,15 +26,20 @@ namespace OX
std::unique_ptr<GameObject> GameObject::removeChild(GameObject *child)
{
auto it = std::find_if(m_children.begin(), m_children.end(),
[child](const std::unique_ptr<GameObject> &u) { return u.get() == child; });
auto it = std::ranges::find_if(m_children, [&](const auto &u)
{
return u.get() == child;
});
if (it == m_children.end()) return nullptr;
child->m_parent = nullptr;
auto out = std::move(*it);
out->m_parent = nullptr;
m_children.erase(it);
return out;
}
const std::vector<std::unique_ptr<GameObject> > &GameObject::children() const
{
return m_children;
@@ -46,6 +51,45 @@ namespace OX
m_components.push_back(std::move(comp));
}
bool GameObject::IsAncestorOf(const GameObject *other) const
{
if (!other || !other->m_parent) return false;
const GameObject *current = other->m_parent;
int depth = 0;
const int MAX_DEPTH = 1000;
while (current != nullptr && depth < MAX_DEPTH) {
if (current == this) {
return true;
}
current = current->m_parent;
depth++;
}
if (depth >= MAX_DEPTH) {
Logger::LogWarning("Possible circular reference detected in GameObject hierarchy");
}
return false;
}
void GameObject::addExistingChild(GameObject *child)
{
if (!child || child == this || IsAncestorOf(child))
return;
// Remove from old parent
if (GameObject *oldParent = child->parent()) {
std::unique_ptr<GameObject> taken = oldParent->removeChild(child);
if (!taken) return;
taken->m_parent = this;
m_children.push_back(std::move(taken));
}
}
const std::vector<std::unique_ptr<Component> > &GameObject::components() const
{
return m_components;
@@ -72,7 +116,7 @@ namespace OX
if (node["components"]) {
for (auto &cnode: node["components"]) {
std::string type = cnode["type"].as<std::string>();
auto type = cnode["type"].as<std::string>();
if (type == "TagComponent") {
auto name = cnode["name"].as<std::string>();
std::unique_ptr<Component> comp = std::make_unique<TagComponent>(name);

View File

@@ -33,11 +33,16 @@ namespace OX
// Components
void addComponent(std::unique_ptr<Component> comp);
template<typename T>
T *getComponent() const;
bool IsAncestorOf(const GameObject *other) const;
void addExistingChild(GameObject *child);
template<typename T>
std::unique_ptr<Component> removeComponent();
T *GetComponent() const;
template<typename T>
std::unique_ptr<Component> RemoveComponent();
[[nodiscard]] const std::vector<std::unique_ptr<Component> > &components() const;
@@ -54,7 +59,7 @@ namespace OX
};
template<typename T>
T *GameObject::getComponent() const
T *GameObject::GetComponent() const
{
for (auto &c: m_components) {
if (auto ptr = dynamic_cast<T *>(c.get()))
@@ -64,7 +69,7 @@ namespace OX
}
template<typename T>
std::unique_ptr<Component> GameObject::removeComponent()
std::unique_ptr<Component> GameObject::RemoveComponent()
{
auto it = std::find_if(m_components.begin(), m_components.end(),
[](const std::unique_ptr<Component> &c)

View File

@@ -39,7 +39,7 @@ namespace OX
void Scene::registerByTag(GameObject *go)
{
if (auto tc = go->getComponent<TagComponent>()) {
if (auto tc = go->GetComponent<TagComponent>()) {
m_tagIndex[tc->name] = go;
}
for (auto &child: go->children()) {
@@ -49,7 +49,7 @@ namespace OX
void Scene::unregisterByTag(GameObject *go)
{
if (auto tc = go->getComponent<TagComponent>()) {
if (auto tc = go->GetComponent<TagComponent>()) {
m_tagIndex.erase(tc->name);
}
for (auto &child: go->children()) {

View File

@@ -4,15 +4,18 @@
#include "Editor.h"
#include <functional>
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_glfw.h>
#include "types/all.h"
#include "Windows/LoggerWindow.h"
#include "Windows/Viewport.h"
#include "Windows/FileBrowser.h"
#include "types/all.h"
#include "Windows/InspectorWindow.h"
#include "Windows/SceneExplorer.h"
namespace OX
{
@@ -44,6 +47,13 @@ namespace OX
fileBrowser = std::make_shared<FileBrowser>();
primaryViewport = std::make_shared<Viewport>();
inspectorWindow = std::make_shared<InspectorWindow>();
sceneExplorer = std::make_shared<SceneExplorer>();
LoggerWindow::Init();
core.GetScene().createRootObject("Welcome!");
}
void Editor::Update(Core &core)
@@ -85,6 +95,8 @@ namespace OX
LoggerWindow::Draw();
fileBrowser->Draw();
primaryViewport->Draw(core);
inspectorWindow->Draw(core, *this);
sceneExplorer->Draw(core, *this);
ImGui::Begin("Profiler");

View File

@@ -9,11 +9,20 @@
#include "Core.h"
#include "types/all.h"
#undef IM_ASSERT
#define IM_ASSERT(_EXPR) do { OX_ASSERT(_EXPR, "ImGui internal assertion failed"); } while (0)
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_glfw.h>
namespace OX
{
class Viewport;
class FileBrowser;
class InspectorWindow;
class SceneExplorer;
class Editor final : public Layer
{
@@ -28,9 +37,19 @@ namespace OX
void Shutdown(Core &core) override;
//? Getters
GameObject *GetSelected() { return selected; }
void SetSelected(GameObject *obj) { selected = obj; }
private:
std::shared_ptr<Viewport> primaryViewport;
std::shared_ptr<FileBrowser> fileBrowser;
std::shared_ptr<InspectorWindow> inspectorWindow;
std::shared_ptr<SceneExplorer> sceneExplorer;
GameObject *selected;
};
} // OX

View File

@@ -8,6 +8,7 @@ namespace OX
FileBrowser::FileBrowser()
: _lastProgress(0.0f)
{
Logger::LogVerbose("Editor::InspectorWindow");
}
FileBrowser::~FileBrowser() = default;

View File

@@ -9,63 +9,64 @@
#include "systems/assets/Texture2D.h" // for Texture2D
#include "imgui.h"
namespace OX {
namespace OX
{
/// Configuration for look & feel
struct FileBrowserConfig
{
float thumbnailSize = 64.0f;
float padding = 8.0f;
float labelHeight = 20.0f;
ImU32 bgColor = IM_COL32(40,40,40,255);
ImU32 highlightColor = IM_COL32(75,75,75,255);
bool showGrid = true;
bool showThumbnails = true;
bool allowMultiple = false;
// … add more theming or behavioral flags here
float thumbnailSize = 64.0f;
float padding = 8.0f;
float labelHeight = 20.0f;
ImU32 bgColor = IM_COL32(40, 40, 40, 255);
ImU32 highlightColor = IM_COL32(75, 75, 75, 255);
bool showGrid = true;
bool showThumbnails = true;
bool allowMultiple = false;
};
/// FileBrowser widget, supports grid & list, filtering, breadcrumbs, callbacks.
class FileBrowser
{
public:
using FileSelectedCallback = std::function<void(const std::string& path)>;
using FileSelectedCallback = std::function<void(const std::string &path)>;
FileBrowser();
~FileBrowser();
/// Draw the entire browser window
void Draw(const char* title = "File Browser");
void Draw(const char *title = "File Browser");
/// Set a filter string (wildcards, substrings, etc.)
void SetFilter(const std::string& filter);
void SetFilter(const std::string &filter);
/// Called whenever the user clicks on a file (not directory)
void SetFileSelectedCallback(FileSelectedCallback cb);
/// Access to tweak colors/sizes
FileBrowserConfig& Config() { return _cfg; }
FileBrowserConfig &Config() { return _cfg; }
private:
// helpers
void DrawToolbar();
void DrawBreadcrumbs();
void DrawGridView(const std::shared_ptr<ResourceTreeNode>& node);
void DrawListView(const std::shared_ptr<ResourceTreeNode>& node);
[[nodiscard]] bool PassesFilter(const std::string& name) const;
void DrawGridView(const std::shared_ptr<ResourceTreeNode> &node);
void DrawListView(const std::shared_ptr<ResourceTreeNode> &node);
[[nodiscard]] bool PassesFilter(const std::string &name) const;
void SetProgress(float p);
ImTextureID GetIconTexture(const ResourceTreeNode&); // stub for folder/file icons
ImTextureID GetIconTexture(const ResourceTreeNode &); // stub for folder/file icons
// state
FileBrowserConfig _cfg;
std::string _currentPath = "res://";
std::string _filter;
bool _gridMode = true;
FileSelectedCallback _onFileSelected;
float _lastProgress;
size_t _maxQueueSize;
FileBrowserConfig _cfg;
std::string _currentPath = "res://";
std::string _filter;
bool _gridMode = true;
FileSelectedCallback _onFileSelected;
float _lastProgress;
size_t _maxQueueSize;
};
} // namespace OX

View File

@@ -0,0 +1,21 @@
//
// Created by spenc on 6/2/2025.
//
#include "InspectorWindow.h"
#include "Editor.h"
#include "Core.h"
namespace OX
{
// This will be the window where you view the componenets and info on the selected Game Object
void InspectorWindow::Draw(Core &core, Editor &editor)
{
//auto scene = core.GetScene();
//std::shared_ptr<GameObject> selected = editor.GetSelected();
}
} // OX

View File

@@ -0,0 +1,26 @@
//
// Created by spenc on 6/2/2025.
//
#pragma once
#include "Core.h"
#include "systems/Scene/Scene.h"
namespace OX
{
class Editor;
class InspectorWindow
{
public:
InspectorWindow()
{
Logger::LogVerbose("Editor::InspectorWindow");
}
void Draw(Core &core, Editor &editor);
private:
};
} // OX

View File

@@ -1,17 +1,9 @@
//
// Created by spenc on 5/12/2025.
//
#include "LoggerWindow.h"
#include "Core.h"
#include <imgui.h>
#include <string>
namespace OX::LoggerWindow {
namespace OX
{
static bool s_AutoScroll = true;
static bool s_ShowOk = true;
static bool s_ShowInfo = true;
@@ -21,8 +13,12 @@ namespace OX::LoggerWindow {
static bool s_ShowVerbose = false;
static char s_FilterBuf[256] = "";
void LoggerWindow::Init()
{
Logger::LogVerbose("Editor::LoggerWindow Initialized");
}
void Draw(const char* title)
void LoggerWindow::Draw(const char *title)
{
OX_PROFILE_FUNCTION();
@@ -31,7 +27,6 @@ namespace OX::LoggerWindow {
return;
}
// Controls
if (ImGui::Button("Clear Log")) Logger::Clear();
ImGui::SameLine();
ImGui::Checkbox("Auto-scroll", &s_AutoScroll);
@@ -39,66 +34,63 @@ namespace OX::LoggerWindow {
ImGui::SetNextItemWidth(200);
ImGui::InputTextWithHint("##Filter", "Filter...", s_FilterBuf, sizeof(s_FilterBuf));
// Filter toggles
ImGui::Separator();
ImGui::Text("Levels: ");
ImGui::SameLine(); ImGui::Checkbox("Ok", &s_ShowOk);
ImGui::SameLine(); ImGui::Checkbox("Info", &s_ShowInfo);
ImGui::SameLine(); ImGui::Checkbox("Warning", &s_ShowWarning);
ImGui::SameLine(); ImGui::Checkbox("Error", &s_ShowError);
ImGui::SameLine(); ImGui::Checkbox("Debug", &s_ShowDebug);
ImGui::SameLine(); ImGui::Checkbox("Verbose", &s_ShowVerbose);
ImGui::SameLine();
ImGui::Checkbox("Ok", &s_ShowOk);
ImGui::SameLine();
ImGui::Checkbox("Info", &s_ShowInfo);
ImGui::SameLine();
ImGui::Checkbox("Warning", &s_ShowWarning);
ImGui::SameLine();
ImGui::Checkbox("Error", &s_ShowError);
ImGui::SameLine();
ImGui::Checkbox("Debug", &s_ShowDebug);
ImGui::SameLine();
ImGui::Checkbox("Verbose", &s_ShowVerbose);
ImGui::Separator();
constexpr ImGuiTableFlags flags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg |
ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter;
ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg |
ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter;
if (const float tableHeight = ImGui::GetContentRegionAvail().y - 10.0f; ImGui::BeginTable("LogTable", 3, flags, ImVec2(0, tableHeight))) {
if (const float tableHeight = ImGui::GetContentRegionAvail().y - 10.0f;
ImGui::BeginTable("LogTable", 3, flags, ImVec2(0, tableHeight))) {
ImGui::TableSetupColumn("Level", ImGuiTableColumnFlags_WidthFixed, 80.0f);
ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_WidthFixed, 80.0f);
ImGui::TableSetupColumn("Message", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableHeadersRow();
for (const auto& message : Logger::GetMessages()) {
for (const auto &message: Logger::GetMessages()) {
const MessageType level = message.type;
const std::string& text = message.text;
const std::string& timestamp = message.timestamp;
const std::string &text = message.text;
const std::string &timestamp = message.timestamp;
const bool show =
(level == MessageType::Ok && s_ShowOk) ||
(level == MessageType::Info && s_ShowInfo) ||
(level == MessageType::Warning && s_ShowWarning) ||
(level == MessageType::Error && s_ShowError) ||
(level == MessageType::Verbose && s_ShowVerbose) ||
(level == MessageType::Debug && s_ShowDebug);
(level == MessageType::Ok && s_ShowOk) ||
(level == MessageType::Info && s_ShowInfo) ||
(level == MessageType::Warning && s_ShowWarning) ||
(level == MessageType::Error && s_ShowError) ||
(level == MessageType::Debug && s_ShowDebug) ||
(level == MessageType::Verbose && s_ShowVerbose);
if (!show)
if (!show ||
(s_FilterBuf[0] != '\0' &&
text.find(s_FilterBuf) == std::string::npos))
continue;
if (s_FilterBuf[0] != '\0' &&
std::string(text).find(s_FilterBuf) == std::string::npos)
continue;
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
const Color color = Logger::GetRGBColor(level);
ImVec4 col(color.r, color.g, color.b, color.a);
const ImVec2 cursor = ImGui::GetCursorScreenPos();
constexpr float barWidth = 4.0f;
const float textHeight = ImGui::GetTextLineHeight();
ImVec2 barStart = cursor;
auto barEnd = ImVec2(cursor.x + barWidth, cursor.y + textHeight);
ImGui::GetWindowDrawList()->AddRectFilled(barStart, barEnd, ImGui::ColorConvertFloat4ToU32(col), 1.0f);
ImVec2 barEnd = ImVec2(cursor.x + barWidth, cursor.y + textHeight);
ImGui::GetWindowDrawList()->AddRectFilled(barStart, barEnd, ImGui::ColorConvertFloat4ToU32(col), 1.0f);
ImGui::Dummy(ImVec2(barWidth + 6.0f, 0));
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, col);
@@ -106,38 +98,24 @@ namespace OX::LoggerWindow {
ImGui::PopStyleColor();
ImGui::TableSetColumnIndex(1);
ImGui::Text("%s",timestamp.c_str());
ImGui::Text("%s", timestamp.c_str());
ImGui::TableSetColumnIndex(2);
ImGui::TextWrapped("%s", text.c_str());
if (s_AutoScroll) {
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 5.0f) {
ImGui::SetScrollHereY(1.0f);
}
}
if (s_AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 5.0f)
ImGui::SetScrollHereY(1.0f);
}
ImGui::EndTable();
}
ImGui::End();
}
void ClearFilters() {
void LoggerWindow::ClearFilters()
{
s_FilterBuf[0] = '\0';
s_ShowOk = s_ShowInfo = s_ShowWarning = s_ShowError = s_ShowDebug = s_ShowVerbose = true;
}
}
// CE
} // namespace OX

View File

@@ -5,13 +5,19 @@
#ifndef LOGGERWINDOW_H
#define LOGGERWINDOW_H
namespace OX {
#include "Core.h"
namespace LoggerWindow {
void Draw(const char* title = "Console");
void ClearFilters();
namespace OX
{
class LoggerWindow
{
public:
static void Init();
static void Draw(const char *title = "Console");
static void ClearFilters();
};
} // namespace OX
} // OX
#endif //LOGGERWINDOW_H
#endif // LOGGERWINDOW_H

View File

@@ -0,0 +1,93 @@
#include "SceneExplorer.h"
#include "Core.h"
#include "Editor.h"
#include "systems/Scene/Scene.h"
#include "systems/Scene/GameObject.h"
#include "systems/Scene/Components/TagComponent.h"
#include <imgui.h>
namespace OX
{
static inline const char *GetTag(GameObject *obj)
{
auto *tag = obj->GetComponent<TagComponent>();
return tag ? tag->name.c_str() : "Unnamed";
}
static void ReparentObject(GameObject *object, GameObject *newParent)
{
if (!object || !newParent || object == newParent || object->IsAncestorOf(newParent))
return;
GameObject *oldParent = object->parent();
if (oldParent)
oldParent->removeChild(object);
newParent->addExistingChild(object);
}
static void DrawGameObject(GameObject *obj, Editor &editor)
{
if (!obj) return;
ImGui::PushID(obj);
const char *tag = GetTag(obj);
bool opened = ImGui::TreeNodeEx(tag, ImGuiTreeNodeFlags_DefaultOpen);
// Selection
if (ImGui::IsItemClicked(ImGuiMouseButton_Left))
editor.SetSelected(obj);
// Context Menu
if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("Add Child")) {
auto child = std::make_unique<GameObject>("GameObject");
GameObject *ptr = child.get();
obj->addChild(std::move(child));
ptr->addComponent(std::make_unique<TagComponent>("GameObject"));
}
ImGui::EndPopup();
}
// Drag Source
if (ImGui::BeginDragDropSource()) {
ImGui::SetDragDropPayload("SCENE_OBJECT", &obj, sizeof(GameObject *));
ImGui::Text("Move: %s", tag);
ImGui::EndDragDropSource();
}
// Drop Target
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("SCENE_OBJECT")) {
auto *dropped = *(GameObject **) payload->Data;
if (dropped && dropped != obj)
ReparentObject(dropped, obj);
}
ImGui::EndDragDropTarget();
}
// Children
if (opened) {
const auto &children = obj->children();
for (const auto &child: children)
DrawGameObject(child.get(), editor);
ImGui::TreePop();
}
ImGui::PopID();
}
void SceneExplorer::Draw(Core &core, Editor &editor)
{
if (!ImGui::Begin("Scene Explorer")) {
ImGui::End();
return;
}
auto &roots = core.GetScene().roots();
for (const auto &root: roots)
DrawGameObject(root.get(), editor);
ImGui::End();
}
}

View File

@@ -0,0 +1,28 @@
//
// Created by spenc on 6/2/2025.
//
#ifndef SCENEEXPLORER_H
#define SCENEEXPLORER_H
#include "Editor.h"
#include "systems/Logger.h"
namespace OX
{
class Core;
class Editor;
class SceneExplorer
{
public:
SceneExplorer()
{
Logger::LogVerbose("Editor::SceneExplorer");
}
void Draw(Core &core, Editor &editor);
};
} // namespace OX
#endif // SCENEEXPLORER_H

View File

@@ -37,6 +37,5 @@ namespace OX
ImGui::PopStyleVar(); // Restore padding
core.GetRenderer().ResizeTarget(viewportSize.x, viewportSize.y);
}
};

View File

@@ -17,12 +17,13 @@ namespace OX
: m_name(std::move(name))
{
m_id = s_nextID++;
Logger::LogVerbose("Editor::Viewport: %s", m_name.c_str());
}
void Draw(Core& core) const;
void Draw(Core &core) const;
[[nodiscard]] int GetID() const { return m_id; }
[[nodiscard]] const std::string& GetName() const { return m_name; }
[[nodiscard]] const std::string &GetName() const { return m_name; }
private:
int m_id;
@@ -30,7 +31,6 @@ namespace OX
static int s_nextID;
};
};
#endif

1
todo.md Normal file
View File

@@ -0,0 +1 @@
# [Trello](https://trello.com/b/iuh8DC6v/onyx-engine)