Adds minimal layer and event framework
Introduces a basic application layer and event framework. This commit implements a simple event system and layer management to facilitate a more modular application architecture. The example demonstrates how to create custom events and layers that can interact with the application.
This commit is contained in:
21
.vscode/c_cpp_properties.json
vendored
Normal file
21
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Win32",
|
||||||
|
"includePath": [
|
||||||
|
"${default}",
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"_DEBUG",
|
||||||
|
"UNICODE",
|
||||||
|
"_UNICODE"
|
||||||
|
],
|
||||||
|
"windowsSdkVersion": "10.0.22621.0",
|
||||||
|
"compilerPath": "C:/msys64/mingw64/bin/g++.exe",
|
||||||
|
"cStandard": "c23",
|
||||||
|
"cppStandard": "c++20"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
96
examples/layer_example.cpp
Normal file
96
examples/layer_example.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#define LAYER_IMPLEMENTATION
|
||||||
|
#include "Layer.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// ------------------------ Custom Events ------------------------
|
||||||
|
|
||||||
|
struct ShutdownEvent : public layer::Event
|
||||||
|
{
|
||||||
|
const char *GetName() const override { return "ShutdownEvent"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A simple input-like event just for demo
|
||||||
|
struct PingEvent : public layer::Event
|
||||||
|
{
|
||||||
|
explicit PingEvent(int count) : counter(count) {}
|
||||||
|
const char *GetName() const override { return "PingEvent"; }
|
||||||
|
|
||||||
|
int counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------------------------ Custom Layer -------------------------
|
||||||
|
|
||||||
|
class DemoLayer : public layer::Layer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DemoLayer(layer::Application &app)
|
||||||
|
: layer::Layer("DemoLayer"), m_App(app)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnAttach() override
|
||||||
|
{
|
||||||
|
std::cout << "[DemoLayer] Attached\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnUpdate(layer::Seconds dt) override
|
||||||
|
{
|
||||||
|
elapsed += dt;
|
||||||
|
|
||||||
|
if (elapsed > 1.0f && !sentPing)
|
||||||
|
{
|
||||||
|
sentPing = true;
|
||||||
|
PingEvent e(42);
|
||||||
|
std::cout << "[DemoLayer] Sending PingEvent\n";
|
||||||
|
m_App.OnEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsed > 2.0f && !sentShutdown)
|
||||||
|
{
|
||||||
|
sentShutdown = true;
|
||||||
|
ShutdownEvent e;
|
||||||
|
std::cout << "[DemoLayer] Sending ShutdownEvent\n";
|
||||||
|
m_App.OnEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEvent(layer::Event &e) override
|
||||||
|
{
|
||||||
|
layer::EventDispatcher dispatcher(e);
|
||||||
|
|
||||||
|
dispatcher.Dispatch<PingEvent>([this](PingEvent &pe)
|
||||||
|
{
|
||||||
|
std::cout << "[DemoLayer] Received PingEvent, counter=" << pe.counter << "\n";
|
||||||
|
return true; // mark handled
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatcher.Dispatch<ShutdownEvent>([this](ShutdownEvent &)
|
||||||
|
{
|
||||||
|
std::cout << "[DemoLayer] Received ShutdownEvent, closing app.\n";
|
||||||
|
m_App.Close();
|
||||||
|
return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
layer::Application &m_App;
|
||||||
|
float elapsed = 0.0f;
|
||||||
|
bool sentPing = false;
|
||||||
|
bool sentShutdown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------- main ------------------------------
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
layer::Application app;
|
||||||
|
|
||||||
|
DemoLayer demo(app);
|
||||||
|
app.PushLayer(&demo);
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
std::cout << "Application exited.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
309
include/Layer.h
309
include/Layer.h
@@ -0,0 +1,309 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
Layer.h - minimal application layer + event framework
|
||||||
|
-----------------------------------------------------
|
||||||
|
Usage:
|
||||||
|
// In ONE .cpp file:
|
||||||
|
#define LAYER_IMPLEMENTATION
|
||||||
|
#include "Layer.h"
|
||||||
|
|
||||||
|
// In all other .cpp files:
|
||||||
|
#include "Layer.h"
|
||||||
|
|
||||||
|
You define your own Event subclasses and use EventDispatcher
|
||||||
|
to route them in Application / Layer code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace layer
|
||||||
|
{
|
||||||
|
using Seconds = float;
|
||||||
|
|
||||||
|
// =====================================================
|
||||||
|
// Event base + dispatcher
|
||||||
|
// =====================================================
|
||||||
|
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool handled = false;
|
||||||
|
|
||||||
|
virtual ~Event() = default;
|
||||||
|
|
||||||
|
// Optional but useful for debugging
|
||||||
|
virtual const char *GetName() const = 0;
|
||||||
|
virtual std::string ToString() const { return GetName(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class EventDispatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit EventDispatcher(Event &e);
|
||||||
|
|
||||||
|
// Dispatch to a specific event type using dynamic_cast.
|
||||||
|
// T must derive from Event.
|
||||||
|
template <typename T, typename F>
|
||||||
|
bool Dispatch(F &&func)
|
||||||
|
{
|
||||||
|
T *specific = dynamic_cast<T *>(&m_Event);
|
||||||
|
if (!specific)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_Event.handled = func(*specific);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Event &m_Event;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =====================================================
|
||||||
|
// Layer base
|
||||||
|
// =====================================================
|
||||||
|
|
||||||
|
class Layer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Layer(const std::string &name = "Layer");
|
||||||
|
virtual ~Layer();
|
||||||
|
|
||||||
|
virtual void OnAttach() {}
|
||||||
|
virtual void OnDetach() {}
|
||||||
|
virtual void OnUpdate(Seconds dt) {}
|
||||||
|
virtual void OnEvent(Event &e) {}
|
||||||
|
|
||||||
|
const std::string &GetName() const { return m_DebugName; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_DebugName;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =====================================================
|
||||||
|
// LayerStack (non-owning: you manage Layer lifetime)
|
||||||
|
// =====================================================
|
||||||
|
|
||||||
|
class LayerStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LayerStack();
|
||||||
|
~LayerStack();
|
||||||
|
|
||||||
|
// NOTE: Lifetime is managed by the caller.
|
||||||
|
// Pointers must remain valid while in the stack.
|
||||||
|
void PushLayer(Layer *layer);
|
||||||
|
void PushOverlay(Layer *overlay);
|
||||||
|
void PopLayer(Layer *layer);
|
||||||
|
void PopOverlay(Layer *overlay);
|
||||||
|
|
||||||
|
using Container = std::vector<Layer *>;
|
||||||
|
|
||||||
|
Container::iterator begin() { return m_Layers.begin(); }
|
||||||
|
Container::iterator end() { return m_Layers.end(); }
|
||||||
|
Container::reverse_iterator rbegin() { return m_Layers.rbegin(); }
|
||||||
|
Container::reverse_iterator rend() { return m_Layers.rend(); }
|
||||||
|
|
||||||
|
const Container &GetLayers() const { return m_Layers; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Container m_Layers;
|
||||||
|
std::size_t m_InsertIndex; // layers [0..insert) then overlays [insert..)
|
||||||
|
};
|
||||||
|
|
||||||
|
// =====================================================
|
||||||
|
// Application
|
||||||
|
// =====================================================
|
||||||
|
|
||||||
|
class Application
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Application();
|
||||||
|
virtual ~Application();
|
||||||
|
|
||||||
|
// Simple fixed loop:
|
||||||
|
// while (IsRunning()) { update dt; call OnUpdate + layers' OnUpdate }
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
bool IsRunning() const { return m_Running; }
|
||||||
|
|
||||||
|
LayerStack &GetLayerStack() { return m_LayerStack; }
|
||||||
|
const LayerStack &GetLayerStack() const { return m_LayerStack; }
|
||||||
|
|
||||||
|
void PushLayer(Layer *layer);
|
||||||
|
void PushOverlay(Layer *overlay);
|
||||||
|
void PopLayer(Layer *layer);
|
||||||
|
void PopOverlay(Layer *overlay);
|
||||||
|
|
||||||
|
// Default behaviour: fan out to layers (topmost first).
|
||||||
|
virtual void OnEvent(Event &e);
|
||||||
|
|
||||||
|
// Optional application-wide update hook, before layer updates.
|
||||||
|
virtual void OnUpdate(Seconds dt) { (void)dt; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LayerStack m_LayerStack;
|
||||||
|
bool m_Running;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace layer
|
||||||
|
|
||||||
|
#if defined(LAYER_IMPLEMENTATION) && !defined(LAYER_IMPLEMENTATION_DONE)
|
||||||
|
#define LAYER_IMPLEMENTATION_DONE
|
||||||
|
|
||||||
|
namespace layer
|
||||||
|
{
|
||||||
|
// ----------------- EventDispatcher -------------------
|
||||||
|
|
||||||
|
inline EventDispatcher::EventDispatcher(Event &e)
|
||||||
|
: m_Event(e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- Layer ------------------------
|
||||||
|
|
||||||
|
inline Layer::Layer(const std::string &name)
|
||||||
|
: m_DebugName(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Layer::~Layer() = default;
|
||||||
|
|
||||||
|
// -------------------- LayerStack ---------------------
|
||||||
|
|
||||||
|
inline LayerStack::LayerStack()
|
||||||
|
: m_InsertIndex(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LayerStack::~LayerStack() = default;
|
||||||
|
|
||||||
|
inline void LayerStack::PushLayer(Layer *layer)
|
||||||
|
{
|
||||||
|
if (!layer)
|
||||||
|
return;
|
||||||
|
m_Layers.emplace(m_Layers.begin() + static_cast<std::ptrdiff_t>(m_InsertIndex), layer);
|
||||||
|
++m_InsertIndex;
|
||||||
|
layer->OnAttach();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LayerStack::PushOverlay(Layer *overlay)
|
||||||
|
{
|
||||||
|
if (!overlay)
|
||||||
|
return;
|
||||||
|
m_Layers.emplace_back(overlay);
|
||||||
|
overlay->OnAttach();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LayerStack::PopLayer(Layer *layer)
|
||||||
|
{
|
||||||
|
if (!layer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < m_Layers.size(); ++i)
|
||||||
|
{
|
||||||
|
if (m_Layers[i] == layer)
|
||||||
|
{
|
||||||
|
m_Layers[i]->OnDetach();
|
||||||
|
m_Layers.erase(m_Layers.begin() + static_cast<std::ptrdiff_t>(i));
|
||||||
|
if (i < m_InsertIndex && m_InsertIndex > 0)
|
||||||
|
--m_InsertIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LayerStack::PopOverlay(Layer *overlay)
|
||||||
|
{
|
||||||
|
if (!overlay)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < m_Layers.size(); ++i)
|
||||||
|
{
|
||||||
|
if (m_Layers[i] == overlay)
|
||||||
|
{
|
||||||
|
m_Layers[i]->OnDetach();
|
||||||
|
m_Layers.erase(m_Layers.begin() + static_cast<std::ptrdiff_t>(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Application --------------------
|
||||||
|
|
||||||
|
inline Application::Application()
|
||||||
|
: m_Running(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Application::~Application() = default;
|
||||||
|
|
||||||
|
inline void Application::Run()
|
||||||
|
{
|
||||||
|
using clock = std::chrono::high_resolution_clock;
|
||||||
|
auto last = clock::now();
|
||||||
|
|
||||||
|
while (m_Running)
|
||||||
|
{
|
||||||
|
auto now = clock::now();
|
||||||
|
Seconds dt = std::chrono::duration<Seconds>(now - last).count();
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
// Global app hook
|
||||||
|
OnUpdate(dt);
|
||||||
|
|
||||||
|
// Per-layer updates
|
||||||
|
for (Layer *layer : m_LayerStack.GetLayers())
|
||||||
|
{
|
||||||
|
if (layer)
|
||||||
|
layer->OnUpdate(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Application::Close()
|
||||||
|
{
|
||||||
|
m_Running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Application::PushLayer(Layer *layer)
|
||||||
|
{
|
||||||
|
m_LayerStack.PushLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Application::PushOverlay(Layer *overlay)
|
||||||
|
{
|
||||||
|
m_LayerStack.PushOverlay(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Application::PopLayer(Layer *layer)
|
||||||
|
{
|
||||||
|
m_LayerStack.PopLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Application::PopOverlay(Layer *overlay)
|
||||||
|
{
|
||||||
|
m_LayerStack.PopOverlay(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Application::OnEvent(Event &e)
|
||||||
|
{
|
||||||
|
// Fan out to layers, topmost first
|
||||||
|
auto &layers = m_LayerStack.GetLayers();
|
||||||
|
for (auto it = layers.rbegin(); it != layers.rend(); ++it)
|
||||||
|
{
|
||||||
|
Layer *layer = *it;
|
||||||
|
if (!layer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
layer->OnEvent(e);
|
||||||
|
if (e.handled)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace layer
|
||||||
|
|
||||||
|
#endif // LAYER_IMPLEMENTATION / DONE
|
||||||
|
|||||||
Reference in New Issue
Block a user