code integration

This commit is contained in:
Sylvain Schneider
2026-07-04 21:17:38 +02:00
parent 8329318677
commit 888765ef6b
64 changed files with 52755 additions and 1 deletions

15
quokka_gfx/quokka_gfx.h Normal file
View File

@@ -0,0 +1,15 @@
/**
* QuokkaGFX - Lightweight SDL3 wrapper - umbrella header
*
* Single header include for easy integration.
* Usage: #include "quokka_gfx/quokka_gfx.h"
*/
#pragma once
#include "quokka_gfx/Application.h"
#include "quokka_gfx/Window/Window.h"
#include "utils/Colors.h"
#include "utils/Coords.h"
#include <SDL3/SDL.h>

View File

@@ -0,0 +1,48 @@
#include "Application.h"
#include <SDL3/SDL.h>
#include <algorithm>
using namespace std;
using namespace quokka_gfx;
//--------------------------------------------------------------
/* Default constructor */
Application::Application()
: m_eventManager(*this)
{
// Initialize SDL context
m_sdlContext = SDLContext::GetContext();
}
//--------------------------------------------------------------
/* Get the WindowManager instance */
WindowManager *Application::GetWindowManager() noexcept
{
return &m_windowManager;
}
//--------------------------------------------------------------
/* Start the main event loop */
void Application::Run() const
{
// Main loop
bool running = true;
uint64_t lastTime = SDL_GetTicksNS();
while (running && m_windowManager.HasAnyWindow())
{
// Calculate delta time
const uint64_t currentTime = SDL_GetTicksNS();
float dt = static_cast<float>(currentTime - lastTime) / 1e9f; // Convert nanoseconds to seconds
lastTime = currentTime;
dt = std::max(dt, 0.00001f); // Avoid zero or negative delta time
// Poll and process events
const auto result = m_eventManager.ProcessEvents();
if (result == EventManager::PollingResult::Quit)
running = false;
// Update and Render all windows
m_windowManager.Update(dt);
m_windowManager.Render();
}
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,36 @@
#pragma once
#include "Input/EventManager.h"
#include "Input/InputManager.h"
#include "SDLContext.h"
#include "Window/WindowManager.h"
namespace quokka_gfx
{
//--------------------------------------------------------------
class Application
{
friend class EventManager;
friend class WindowManager;
friend class Window;
public:
Application(); // Default constructor
virtual ~Application() = default; // Default destructor
Application(const Application &obj) = delete; // Copy constructor
Application(Application &&obj) noexcept = delete; // Move constructor
Application &operator=(const Application &obj) = delete; // Copy assignment operator
Application &operator=(Application &&obj) noexcept = delete; // Move assignment operator
[[nodiscard]] WindowManager *GetWindowManager() noexcept; // Get the WindowManager instance
void Run() const; // Start the main event loop
protected:
SDLContext::pContext m_sdlContext = nullptr; // SDLContext instance
EventManager m_eventManager; // EventManager instance
InputManager m_inputManager; // InputManager instance
WindowManager m_windowManager; // WindowManager instance
};
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,70 @@
#include "EventManager.h"
#include "../Application.h"
#include "../Window/WindowManager.h"
#include "InputManager.h"
using namespace std;
using namespace quokka_gfx;
//--------------------------------------------------------------
/* Constructor */
EventManager::EventManager(Application &appManager)
: m_appManager(appManager)
{
// Nothing to do
}
//--------------------------------------------------------------
/* Set the global event handler function */
void EventManager::SetGlobalEventHandler(const GlobalEventHandler &globalEventHandler)
{
m_globalEventHandler = globalEventHandler;
}
//--------------------------------------------------------------
/* Poll and process SDL events */
EventManager::PollingResult EventManager::ProcessEvents() const
{
// Update the input states at the beginning of a new frame (before processing events)
m_appManager.m_inputManager.updateStates();
// Handle events
SDL_Event event;
while (SDL_PollEvent(&event))
{
// Process events
if (event.type == SDL_EVENT_QUIT)
return PollingResult::Quit;
// Call the global event handler if it is set
if (m_globalEventHandler)
{
const EventResult result = m_globalEventHandler(event);
if (result == EventResult::Veto)
continue;
}
// Update input states based on the event
m_appManager.m_inputManager.processEvent(event);
// Handle event for all windows
switch (event.type)
{
case SDL_EVENT_WINDOW_FOCUS_GAINED: // Update the focused window ID when a window gains focus
case SDL_EVENT_WINDOW_FOCUS_LOST: // Update the focused window ID when a window loses focus
case SDL_EVENT_WINDOW_MOUSE_ENTER: // Update the hovered window ID when the mouse enters a window
case SDL_EVENT_WINDOW_MOUSE_LEAVE: // Update the hovered window ID when the mouse leaves a window
case SDL_EVENT_KEY_DOWN: // Update the current key state when a key is pressed
case SDL_EVENT_KEY_UP: // Update the current key state when a key is released
case SDL_EVENT_MOUSE_MOTION: // Update the current mouse position when the mouse is moved
case SDL_EVENT_MOUSE_BUTTON_DOWN: // Update the current mouse button state when a mouse button is pressed
case SDL_EVENT_MOUSE_BUTTON_UP: // Update the current mouse button state when a mouse button is released
case SDL_EVENT_MOUSE_WHEEL: // Update the current mouse wheel state when the mouse wheel is scrolled
// Input events are already processed by InputManager, no need to handle them here
break;
default:
m_appManager.m_windowManager.OnEvent(&event);
}
}
return PollingResult::Continue;
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,46 @@
#pragma once
#include <SDL3/SDL.h>
#include <functional>
namespace quokka_gfx
{
class Application;
//--------------------------------------------------------------
class EventManager
{
public:
enum class PollingResult : uint8_t
{
Continue = 0, // Continue processing the event in the next handler
Quit, // Quit the event loop, do not process it and stop further processing
};
enum class EventResult : uint8_t
{
Skip = 0, // Skip the event, continue processing it in the next handler
Veto, // Veto the event, do not process it and stop further processing
};
using GlobalEventHandler = std::function<EventResult(SDL_Event &event)>; // Function type for global event handlers
public:
EventManager() = delete; // Default constructor
virtual ~EventManager() = default; // Default destructor
EventManager(const EventManager &obj) = delete; // Copy constructor
EventManager(EventManager &&obj) noexcept = delete; // Move constructor
EventManager &operator=(const EventManager &obj) = delete; // Copy assignment operator
EventManager &operator=(EventManager &&obj) noexcept = delete; // Move assignment operator
explicit EventManager(Application &appManager); // Constructor
void SetGlobalEventHandler(const GlobalEventHandler &globalEventHandler); // Set the global event handler function
PollingResult ProcessEvents() const; // Poll and process SDL events
protected:
Application &m_appManager; // Reference to the Application instance
GlobalEventHandler m_globalEventHandler; // Global event handler function
};
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,232 @@
#include "InputManager.h"
#include <ranges>
using namespace std;
using namespace quokka_gfx;
//--------------------------------------------------------------
/* Check if a key is being held down */
bool InputManager::isKeyDown(const SDL_Scancode code) const
{
if (!m_currentKeys.contains(code))
return false;
return m_currentKeys.at(code);
}
//--------------------------------------------------------------
/* Check if a key is pressed (transition from up to down on this frame) */
bool InputManager::isKeyPressed(const SDL_Scancode code) const
{
if (!isKeyDown(code))
return false;
if (!m_previousKeys.contains(code))
return true;
return !m_previousKeys.at(code);
}
//--------------------------------------------------------------
/* Check if a key is released (transition from down to up on this frame) */
bool InputManager::isKeyReleased(const SDL_Scancode code) const
{
if (isKeyDown(code))
return false;
if (!m_previousKeys.contains(code))
return false;
return m_previousKeys.at(code);
}
//--------------------------------------------------------------
/* Get a list of keys that are currently being held down */
void InputManager::getHoldingKeys(KeyList &keyList) const
{
keyList.clear();
// Create a view of the keys that are currently held down
auto holdingView = m_currentKeys | views::filter([](const auto &pair)
{ return pair.second; }) |
views::keys;
// Copy the keys from the view to the keyList
ranges::copy(holdingView, back_inserter(keyList));
}
//--------------------------------------------------------------
/* Get a list of keys that were pressed in the current frame */
void InputManager::getPressedKeys(KeyList &keyList) const
{
keyList.clear();
auto pressedView = m_currentKeys | views::filter([this](const auto &pair)
{
const auto& [scancode, isDown] = pair;
if (!isDown)
return false;
auto it = m_previousKeys.find(scancode);
const bool wasDown = (it != m_previousKeys.end()) ? it->second : false;
return !wasDown; }) |
views::keys;
ranges::copy(pressedView, back_inserter(keyList));
}
//--------------------------------------------------------------
/* Get a list of keys that were released in the current frame */
void InputManager::getReleasedKeys(KeyList &keyList) const
{
keyList.clear();
auto releasedView = m_previousKeys | std::views::filter([this](const auto &pair)
{
const auto& [scancode, wasDown] = pair;
if (!wasDown)
return false;
auto it = m_currentKeys.find(scancode);
const bool isDown = (it != m_currentKeys.end()) ? it->second : false;
return !isDown; }) |
std::views::keys;
std::ranges::copy(releasedView, back_inserter(keyList));
}
//--------------------------------------------------------------
/* Get the current mouse position */
FPos InputManager::getMousePosition() const
{
return m_mousePosition;
}
//--------------------------------------------------------------
/* Check if a mouse button is being held down */
bool InputManager::isMouseButtonDown(const MouseButton button) const
{
const auto it = m_currentMouseButtons.find(button);
return (it != m_currentMouseButtons.end()) ? it->second : false;
}
//--------------------------------------------------------------
/* Check if a mouse button is pressed (transition from up to down on this frame) */
bool InputManager::isMouseButtonPressed(const MouseButton button) const
{
if (!isMouseButtonDown(button))
return false;
if (!m_previousMouseButtons.contains(button))
return true;
return !m_previousMouseButtons.at(button);
}
//--------------------------------------------------------------
/* Check if a mouse button is released (transition from down to up on this frame) */
bool InputManager::isMouseButtonReleased(const MouseButton button) const
{
if (isMouseButtonDown(button))
return false;
if (!m_previousMouseButtons.contains(button))
return false;
return m_previousMouseButtons.at(button);
}
//--------------------------------------------------------------
/* Check if the mouse wheel has been scrolled in the current frame */
bool InputManager::isMouseWheeling() const
{
return (m_wheelX != 0.0f || m_wheelY != 0.0f);
}
//--------------------------------------------------------------
/* Get the mouse wheel delta for horizontal scrolling */
float InputManager::getMouseWheelX() const
{
return m_wheelX;
}
//--------------------------------------------------------------
/* Get the mouse wheel delta for vertical scrolling */
float InputManager::getMouseWheelY() const
{
return m_wheelY;
}
//--------------------------------------------------------------
/* Get the ID of the window currently focused for keyboard input */
uint32_t InputManager::getFocusedWindowID() const
{
return m_focusedWindowID;
}
//--------------------------------------------------------------
/* Get the ID of the window currently hovered by the mouse */
uint32_t InputManager::getHoveredWindowID() const
{
return m_hoveredWindowID;
}
//--------------------------------------------------------------
/* Update the input states (to be called each frame) */
void InputManager::updateStates()
{
// Clear the mouse wheel delta for the current frame
clearWheelDelta();
// Update the previous key states to the current key states for the next frame
m_previousKeys = m_currentKeys;
// Update the previous mouse button states to the current mouse button states for the next frame
m_previousMouseButtons = m_currentMouseButtons;
}
//--------------------------------------------------------------
/* Process an SDL event to update input states */
void InputManager::processEvent(const SDL_Event &event)
{
// Process events
if (event.type == SDL_EVENT_WINDOW_FOCUS_GAINED)
{
m_focusedWindowID = event.window.windowID;
}
if (event.type == SDL_EVENT_WINDOW_FOCUS_LOST)
{
if (m_focusedWindowID == event.window.windowID)
m_focusedWindowID = 0;
}
else if (event.type == SDL_EVENT_WINDOW_MOUSE_ENTER)
{
m_hoveredWindowID = event.window.windowID;
}
else if (event.type == SDL_EVENT_WINDOW_MOUSE_LEAVE)
{
m_hoveredWindowID = 0;
}
else if (event.type == SDL_EVENT_KEY_DOWN)
{
m_currentKeys[event.key.scancode] = true;
}
else if (event.type == SDL_EVENT_KEY_UP)
{
m_currentKeys[event.key.scancode] = false;
}
else if (event.type == SDL_EVENT_MOUSE_MOTION)
{
m_mousePosition = { .x = event.motion.x, .y = event.motion.y };
}
else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
{
m_currentMouseButtons[static_cast<MouseButton>(event.button.button)] = true;
}
else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
{
m_currentMouseButtons[static_cast<MouseButton>(event.button.button)] = false;
}
else if (event.type == SDL_EVENT_MOUSE_WHEEL)
{
processWheelEvent(event.wheel);
}
}
//--------------------------------------------------------------
/* Clear the mouse wheel delta */
void InputManager::clearWheelDelta()
{
m_wheelX = 0.0f;
m_wheelY = 0.0f;
}
//--------------------------------------------------------------
/* Process a mouse wheel event to update the wheel delta */
void InputManager::processWheelEvent(const SDL_MouseWheelEvent &event)
{
m_wheelX = event.x; // Positive value for scrolling right, negative for scrolling left
m_wheelY = event.y; // Positive value for scrolling up, negative for scrolling down
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,82 @@
#pragma once
#include "utils/Coords.h"
#include <SDL3/SDL.h>
#include <unordered_map>
#include <vector>
namespace quokka_gfx
{
//--------------------------------------------------------------
class InputManager
{
friend class WindowManager;
friend class Window;
friend class EventManager;
public:
enum class MouseButton : uint8_t
{
Left = SDL_BUTTON_LEFT, // Left mouse button
Middle = SDL_BUTTON_MIDDLE, // Middle mouse button
Right = SDL_BUTTON_RIGHT, // Right mouse button
X1 = SDL_BUTTON_X1, // Extra mouse button 1
X2 = SDL_BUTTON_X2 // Extra mouse button 2
};
using KeyList = std::vector<SDL_Scancode>; // List of SDL scancodes representing keys
public:
InputManager() = default; // Default constructor
virtual ~InputManager() = default; // Default destructor
InputManager(const InputManager &obj) = delete; // Copy constructor
InputManager(InputManager &&obj) noexcept = delete; // Move constructor
InputManager &operator=(const InputManager &obj) = delete; // Copy assignment operator
InputManager &operator=(InputManager &&obj) noexcept = delete; // Move assignment operator
// Key state query functions
[[nodiscard]] bool isKeyDown(SDL_Scancode code) const; // Check if a key is being held down
[[nodiscard]] bool isKeyPressed(SDL_Scancode code) const; // Check if a key is pressed (transition from up to down on this frame)
[[nodiscard]] bool isKeyReleased(SDL_Scancode code) const; // Check if a key is released (transition from down to up on this frame)
void getHoldingKeys(KeyList &keyList) const; // Get a list of keys that are currently being held down
void getPressedKeys(KeyList &keyList) const; // Get a list of keys that were pressed in the current frame
void getReleasedKeys(KeyList &keyList) const; // Get a list of keys that were released in the current frame
// Mouse state query functions
[[nodiscard]] FPos getMousePosition() const; // Get the current mouse position
[[nodiscard]] bool isMouseButtonDown(MouseButton button) const; // Check if a mouse button is being held down
[[nodiscard]] bool isMouseButtonPressed(MouseButton button) const; // Check if a mouse button is pressed (transition from up to down on this frame)
[[nodiscard]] bool isMouseButtonReleased(MouseButton button) const; // Check if a mouse button is released (transition from down to up on this frame)
[[nodiscard]] bool isMouseWheeling() const; // Check if the mouse wheel has been scrolled in the current frame
[[nodiscard]] float getMouseWheelX() const; // Get the mouse wheel delta for horizontal scrolling
[[nodiscard]] float getMouseWheelY() const; // Get the mouse wheel delta for vertical scrolling
// Window state query functions
[[nodiscard]] uint32_t getFocusedWindowID() const; // Get the ID of the window currently focused for keyboard input
[[nodiscard]] uint32_t getHoveredWindowID() const; // Get the ID of the window currently hovered by the mouse
protected:
void updateStates(); // Update the input states (to be called each frame)
void processEvent(const SDL_Event &event); // Process an SDL event to update input states
// States
std::unordered_map<SDL_Scancode, bool> m_currentKeys;
std::unordered_map<SDL_Scancode, bool> m_previousKeys;
uint32_t m_focusedWindowID = 0; // Keyboard focus window ID
std::unordered_map<MouseButton, bool> m_currentMouseButtons;
std::unordered_map<MouseButton, bool> m_previousMouseButtons;
FPos m_mousePosition;
uint32_t m_hoveredWindowID = 0; // Mouse hover window ID
float m_wheelX = 0.0f; // Mouse wheel delta for horizontal scrolling
float m_wheelY = 0.0f; // Mouse wheel delta for vertical scrolling
private:
void clearWheelDelta(); // Clear the mouse wheel delta
void processWheelEvent(const SDL_MouseWheelEvent &event); // Process a mouse wheel event to update the wheel delta
};
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,75 @@
#include "Renderer.h"
#include "../Window/Window.h"
#include <stdexcept>
using namespace std;
using namespace quokka_gfx;
//--------------------------------------------------------------
/* Constructor */
Renderer::Renderer(const Window &window)
: m_owner(window)
{
// Create the native SDL renderer associated with the window
m_nativeRenderer = SDL_CreateRenderer(m_owner.GetNativeWindow(), nullptr);
if (!m_nativeRenderer)
{
throw std::runtime_error("Failed to create SDL renderer: " + std::string(SDL_GetError()));
}
// Enable VSync for the renderer to synchronize rendering with the display's refresh rate
SDL_SetRenderVSync(m_nativeRenderer, 1);
}
//--------------------------------------------------------------
/* Default destructor */
Renderer::~Renderer()
{
if (m_nativeRenderer)
SDL_DestroyRenderer(m_nativeRenderer);
}
//--------------------------------------------------------------
/* Get the native SDL renderer */
SDL_Renderer *Renderer::getNativeRenderer() const noexcept
{
return m_nativeRenderer;
}
//--------------------------------------------------------------
/* Clear the rendering target */
void Renderer::clear() const
{
SDL_RenderClear(m_nativeRenderer);
}
//--------------------------------------------------------------
/* Present the rendered content to the window */
void Renderer::present() const
{
SDL_RenderPresent(m_nativeRenderer);
}
//--------------------------------------------------------------
void Renderer::setDrawColor(const Color &color) const
{
SDL_SetRenderDrawColor(m_nativeRenderer, color.r, color.g, color.b, color.a);
}
//--------------------------------------------------------------
/* Draw a line */
void Renderer::drawLine(const FPos &start, const FPos &end) const
{
SDL_RenderLine(m_nativeRenderer, start.x, start.y, end.x, end.y);
}
//--------------------------------------------------------------
/* Draw a rectangle outline */
void Renderer::drawRect(const FPos &start, const FSize &size) const
{
const SDL_FRect rect{ start.x, start.y, size.w, size.h };
SDL_RenderRect(m_nativeRenderer, &rect);
}
//--------------------------------------------------------------
/* Draw a filled rectangle */
void Renderer::fillRect(const FPos &start, const FSize &size) const
{
const SDL_FRect rect{ start.x, start.y, size.w, size.h };
SDL_RenderFillRect(m_nativeRenderer, &rect);
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,49 @@
#pragma once
#include "utils/Colors.h"
#include "utils/Coords.h"
#include <SDL3/SDL.h>
#include <memory>
namespace quokka_gfx
{
class Window;
//--------------------------------------------------------------
class Renderer
{
public:
using pRenderer = std::unique_ptr<Renderer>; // Unique pointer type for Renderer
public:
Renderer() = delete; // Default constructor
virtual ~Renderer(); // Default destructor
Renderer(const Renderer &obj) = delete; // Copy constructor
Renderer(Renderer &&obj) noexcept = delete; // Move constructor
Renderer &operator=(const Renderer &obj) = delete; // Copy assignment operator
Renderer &operator=(Renderer &&obj) noexcept = delete; // Move assignment operator
explicit Renderer(const Window &window); // Constructor
// Accessors
[[nodiscard]] SDL_Renderer *getNativeRenderer() const noexcept; // Get the native SDL renderer
// Rendering cycle functions
void clear() const; // Clear the rendering target
void present() const; // Present the rendered content to the window
// Setting functions
void setDrawColor(const Color &color) const; // Set the drawing color (default to white)
// Drawing functions
void drawLine(const FPos &start, const FPos &end) const; // Draw a line
void drawRect(const FPos &start, const FSize &size) const; // Draw a rectangle outline
void fillRect(const FPos &start, const FSize &size) const; // Draw a filled rectangle
protected:
const Window &m_owner; // Reference to the owner window
SDL_Renderer *m_nativeRenderer = nullptr; // Native SDL renderer pointer
};
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,52 @@
#pragma once
#include <SDL3/SDL.h>
#include <memory>
#include <stdexcept>
namespace quokka_gfx
{
//--------------------------------------------------------------
class SDLContext
{
public:
using pContext = std::shared_ptr<SDLContext>; // Shared pointer type for SDLContext
static pContext GetContext(); // Get (create if necessary) the static singleton instance of SDLContext
public:
virtual ~SDLContext(); // Default destructor
SDLContext(const SDLContext &obj) = default; // Copy constructor
SDLContext(SDLContext &&obj) noexcept = default; // Move constructor
SDLContext &operator=(const SDLContext &obj) = default; // Copy assignment operator
SDLContext &operator=(SDLContext &&obj) noexcept = default; // Move assignment operator
protected:
SDLContext(); // Default constructor
static inline pContext m_singleton = nullptr; // Shared pointer to the singleton instance of SDLContext
};
//--------------------------------------------------------------
/* Get (create if necessary) the static singleton instance of SDLContext */
inline SDLContext::pContext SDLContext::GetContext()
{
if (!m_singleton)
m_singleton = pContext(new SDLContext());
return m_singleton;
}
//--------------------------------------------------------------
/* Default constructor */
inline SDLContext::SDLContext()
{
// Initialize SDL with audio, video, and events subsystems
if (!SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS))
throw std::runtime_error(SDL_GetError());
}
//--------------------------------------------------------------
/* Default destructor */
inline SDLContext::~SDLContext()
{
// Quit SDL subsystems
SDL_Quit();
}
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,114 @@
#include "Window.h"
#include "../Application.h"
#include <stdexcept>
using namespace std;
using namespace quokka_gfx;
//--------------------------------------------------------------
/* Constructor */
Window::Window(Application &app, const std::string &title, const Size &size, const uint64_t flags)
: m_appOwner(app)
{
// Create the native SDL window with the specified title, size, and flags
m_nativeWindow = SDL_CreateWindow(
title.data(), // Window title
size.w, // Window w
size.h, // Window h
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN | flags // Window flags: High pixel density for better rendering on high-DPI displays
);
if (!m_nativeWindow)
throw runtime_error("Failed to create SDL window: " + string(SDL_GetError()));
// Create the renderer instance associated with this window
m_renderer = make_unique<Renderer>(*this);
// Register the window with the application's WindowManager
m_appOwner.GetWindowManager()->RegisterWindow(this);
}
//--------------------------------------------------------------
/* Default destructor */
Window::~Window()
{
if (m_nativeWindow)
SDL_DestroyWindow(m_nativeWindow);
}
//--------------------------------------------------------------
/* Get the native SDL window */
SDL_Window *Window::GetNativeWindow() const noexcept
{
return m_nativeWindow;
}
//--------------------------------------------------------------
/* Get the renderer instance */
const Renderer *Window::GetRenderer() const noexcept
{
return m_renderer.get();
}
//--------------------------------------------------------------
/* Get the InputManager instance */
const InputManager *Window::GetInputManager() const noexcept
{
return &m_appOwner.m_inputManager;
}
//--------------------------------------------------------------
/* Show or hide the window */
void Window::Show(const bool show) const
{
if (show)
SDL_ShowWindow(m_nativeWindow);
else
SDL_HideWindow(m_nativeWindow);
}
//--------------------------------------------------------------
/* Hide the window */
void Window::Hide() const
{
Show(false);
}
//--------------------------------------------------------------
/* Close the window */
void Window::Close()
{
m_appOwner.GetWindowManager()->CloseWindow(this);
}
//--------------------------------------------------------------
/* Set the minimum size of the window */
void Window::SetMinSize(const Size &size) const
{
SDL_SetWindowMinimumSize(m_nativeWindow, size.w, size.h);
}
//--------------------------------------------------------------
/* Handle event */
void Window::OnEvent(SDL_Event *event) {}
//--------------------------------------------------------------
/* Update the window state */
void Window::Update(const float dt)
{
// Nothing to do
}
//--------------------------------------------------------------
/* Draw the window content (returns true if the window content was drawn, false otherwise) */
bool Window::Draw()
{
return false;
}
//--------------------------------------------------------------
/* Render the window */
void Window::Render()
{
if (!m_renderer)
return;
// Set the drawing color to black and clear the rendering target
(void)m_renderer->setDrawColor(Color::Black);
(void)m_renderer->clear();
// Draw the window content
Draw();
// Present the rendered content to the window (swap the back buffer to the front)
m_renderer->present();
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,60 @@
#pragma once
#include "../Input/InputManager.h"
#include "../Renderer/Renderer.h"
#include "utils/Coords.h"
#include <SDL3/SDL.h>
#include <memory>
#include <string>
namespace quokka_gfx
{
class Application; // Forward declaration of the Application class
//--------------------------------------------------------------
class Window
{
friend class WindowManager;
public:
using pWindow = std::unique_ptr<Window>; // Unique pointer type for Window
public:
Window() = delete; // Default constructor
virtual ~Window(); // Default destructor
Window(const Window &obj) = delete; // Copy constructor
Window(Window &&obj) noexcept = default; // Move constructor
Window &operator=(const Window &obj) = delete; // Copy assignment operator
Window &operator=(Window &&obj) noexcept = delete; // Move assignment operator
explicit Window(Application &app, // Constructor
const std::string &title,
const Size &size,
uint64_t flags);
// Accessors
[[nodiscard]] SDL_Window *GetNativeWindow() const noexcept; // Get the native SDL window
[[nodiscard]] const Renderer *GetRenderer() const noexcept; // Get the renderer instance
[[nodiscard]] const InputManager *GetInputManager() const noexcept; // Get the InputManager instance
// Show or hide the window
void Show(bool show = true) const; // Show or hide the window
void Hide() const; // Hide the window
void Close(); // Close the window
void SetMinSize(const Size &size) const; // Set the minimum size of the window
// Window lifecycle functions
virtual void OnEvent(SDL_Event *event); // Handle event
virtual void Update(float dt); // Update the window state
virtual bool Draw(); // Draw the window content
void Render(); // Render the window
protected:
Application &m_appOwner; // Reference to the owning Application instance
SDL_Window *m_nativeWindow = nullptr; // Native SDL window pointer
Renderer::pRenderer m_renderer; // Renderer instance for this window
};
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,80 @@
#include "WindowManager.h"
#include "../Application.h"
using namespace std;
using namespace quokka_gfx;
//--------------------------------------------------------------
/* Register an existing window with the WindowManager */
void WindowManager::RegisterWindow(Window *window)
{
if (m_windows.empty())
window->m_appOwner.m_inputManager.m_focusedWindowID = SDL_GetWindowID(window->GetNativeWindow());
m_windows.push_back(window);
}
//--------------------------------------------------------------
/* Close a window by its SDL window ID */
void WindowManager::CloseWindow(uint32_t windowId)
{
const auto it = ranges::find_if(m_windows, [windowId](const auto &win)
{ return SDL_GetWindowID(win->GetNativeWindow()) == windowId; });
if (it != m_windows.end())
{
delete *it; // Delete the window instance
m_windows.erase(it); // Remove the window from the vector
}
}
//--------------------------------------------------------------
/* Close a window by its pointer */
void WindowManager::CloseWindow(Window *window)
{
const auto it = ranges::find_if(m_windows, [window](const auto &win)
{ return win == window; });
if (it != m_windows.end())
{
delete *it; // Delete the window instance
m_windows.erase(it); // Remove the window from the vector
}
}
//--------------------------------------------------------------
/* Get the vector of windows instances */
const WindowManager::WindowList &WindowManager::GetWindows() const noexcept
{
return m_windows;
}
//--------------------------------------------------------------
/* Check if there are any windows managed by the WindowManager */
bool WindowManager::HasAnyWindow() const noexcept
{
return !m_windows.empty();
}
//--------------------------------------------------------------
/* Handle event for all windows */
void WindowManager::OnEvent(SDL_Event *event)
{
// Handle window close events
if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED)
CloseWindow(event->window.windowID);
// Propagate the event to all registered windows
for (const auto &window : m_windows)
window->OnEvent(event);
}
//--------------------------------------------------------------
/* Update all windows */
void WindowManager::Update(const float dt) const
{
// Propagate the Update call to all registered windows
for (const auto &window : m_windows)
window->Update(dt);
}
//--------------------------------------------------------------
/* Render all windows */
void WindowManager::Render() const
{
// Propagate the Render call to all registered windows
for (const auto &window : m_windows)
window->Render();
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,41 @@
#pragma once
#include "../Window/Window.h"
#include <SDL3/SDL.h>
#include <vector>
namespace quokka_gfx
{
//--------------------------------------------------------------
class WindowManager
{
friend class Application;
friend class EventManager;
public:
using WindowList = std::vector<Window *>; // Vector to store pointers to Window instances
public:
WindowManager() = default; // Default constructor
virtual ~WindowManager() = default; // Default destructor
WindowManager(const WindowManager &obj) = delete; // Copy constructor
WindowManager(WindowManager &&obj) noexcept = delete; // Move constructor
WindowManager &operator=(const WindowManager &obj) = delete; // Copy assignment operator
WindowManager &operator=(WindowManager &&obj) noexcept = delete; // Move assignment operator
void RegisterWindow(Window *window); // Register an existing window with the WindowManager
void CloseWindow(uint32_t windowId); // Close a window by its SDL window ID
void CloseWindow(Window *window); // Close a window by its pointer
[[nodiscard]] const WindowList &GetWindows() const noexcept; // Get the vector of windows instances
[[nodiscard]] bool HasAnyWindow() const noexcept; // Check if there are any windows managed by the WindowManager
protected:
WindowList m_windows; // Vector to store unique pointers to windows
void OnEvent(SDL_Event *event); // Handle event for all windows
void Update(float dt) const; // Update all windows
void Render() const; // Render all windows
};
//--------------------------------------------------------------
} // namespace quokka_gfx

View File

@@ -0,0 +1,68 @@
#include "Colors.h"
using namespace quokka_gfx;
//--------------------------------------------------------------
const Color Color::Black{ 0, 0, 0, 255 };
const Color Color::White{ 255, 255, 255, 255 };
const Color Color::Red{ 255, 0, 0, 255 };
const Color Color::Green{ 0, 255, 0, 255 };
const Color Color::Blue{ 0, 0, 255, 255 };
const Color Color::Yellow{ 255, 255, 0, 255 };
const Color Color::Magenta{ 255, 0, 255, 255 };
const Color Color::Cyan{ 0, 255, 255, 255 };
const Color Color::Transparent{ 0, 0, 0, 0 };
//--------------------------------------------------------------
/* Constructor */
constexpr Color::Color(const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha)
: r(red)
, g(green)
, b(blue)
, a(alpha)
{
}
//--------------------------------------------------------------
/* Constructor */
constexpr Color::Color(const uint32_t hexColor)
: r((hexColor >> 16) & 0xFF)
, g((hexColor >> 8) & 0xFF)
, b(hexColor & 0xFF)
, a((hexColor >> 24) & 0xFF ? (hexColor >> 24) & 0xFF : 255)
{
}
//--------------------------------------------------------------
/* Convert Color to SDL_Color */
constexpr Color::operator SDL_Color() const
{
return toSDL_Color();
}
//--------------------------------------------------------------
/* Convert Color to SDL_FColor */
constexpr Color::operator SDL_FColor() const
{
return toSDL_FColor();
}
//--------------------------------------------------------------
/* Convert Color to hex representation */
constexpr uint32_t Color::toHex() const
{
return (static_cast<uint32_t>(a) << 24) |
(static_cast<uint32_t>(r) << 16) |
(static_cast<uint32_t>(g) << 8) |
static_cast<uint32_t>(b);
}
//--------------------------------------------------------------
/* Convert Color to SDL_Color */
constexpr SDL_Color Color::toSDL_Color() const
{
return SDL_Color{ r, g, b, a };
}
//--------------------------------------------------------------
/* Convert Color to SDL_FColor */
constexpr SDL_FColor Color::toSDL_FColor() const
{
return SDL_FColor{ static_cast<float>(r) / 255.0f,
static_cast<float>(g) / 255.0f,
static_cast<float>(b) / 255.0f,
static_cast<float>(a) / 255.0f };
}
//--------------------------------------------------------------

91
quokka_gfx/utils/Colors.h Normal file
View File

@@ -0,0 +1,91 @@
#pragma once
#include <SDL3/SDL.h>
#include <compare>
namespace quokka_gfx
{
//--------------------------------------------------------------
struct Color
{
uint8_t r = 255;
uint8_t g = 255;
uint8_t b = 255;
uint8_t a = 255;
constexpr Color() = default; // Default Constructor
constexpr Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255); // Constructor
explicit constexpr Color(uint32_t hexColor); // Constructor
[[nodiscard]] constexpr auto operator<=>(const Color &) const = default; // Three-way comparison operator
[[nodiscard]] constexpr operator SDL_Color() const; // Convert Color to SDL_Color
[[nodiscard]] constexpr operator SDL_FColor() const; // Convert Color to SDL_FColor
[[nodiscard]] constexpr uint32_t toHex() const; // Convert Color to hex representation
[[nodiscard]] constexpr SDL_Color toSDL_Color() const; // Convert Color to SDL_Color
[[nodiscard]] constexpr SDL_FColor toSDL_FColor() const; // Convert Color to SDL_FColor
// Predefined Colors
static const Color Black;
static const Color White;
static const Color Red;
static const Color Green;
static const Color Blue;
static const Color Yellow;
static const Color Magenta;
static const Color Cyan;
static const Color Transparent;
};
//--------------------------------------------------------------
///* Constructor */
// inline constexpr Color::Color(const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha)
// : r(red)
// , g(green)
// , b(blue)
// , a(alpha)
//{
// }
////--------------------------------------------------------------
///* Constructor */
// inline constexpr Color::Color(const uint32_t hexColor)
// : r((hexColor >> 16) & 0xFF)
// , g((hexColor >> 8) & 0xFF)
// , b(hexColor & 0xFF)
// , a((hexColor >> 24) & 0xFF ? (hexColor >> 24) & 0xFF : 255)
//{
// }
////--------------------------------------------------------------
///* Convert Color to SDL_Color */
// inline constexpr Color::operator SDL_Color() const
//{
// return toSDL_Color();
// }
////--------------------------------------------------------------
///* Convert Color to SDL_FColor */
// inline constexpr Color::operator SDL_FColor() const
//{
// return toSDL_FColor();
// }
////--------------------------------------------------------------
///* Convert Color to hex representation */
// inline constexpr uint32_t Color::toHex() const
//{
// return (static_cast<uint32_t>(a) << 24) |
// (static_cast<uint32_t>(r) << 16) |
// (static_cast<uint32_t>(g) << 8) |
// static_cast<uint32_t>(b);
// }
////--------------------------------------------------------------
///* Convert Color to SDL_Color */
// inline constexpr SDL_Color Color::toSDL_Color() const
//{
// return SDL_Color{ r, g, b, a };
// }
////--------------------------------------------------------------
///* Convert Color to SDL_FColor */
// inline constexpr SDL_FColor Color::toSDL_FColor() const
//{
// return SDL_FColor{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
// }
//--------------------------------------------------------------
} // namespace quokka_gfx

32
quokka_gfx/utils/Coords.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <SDL3/SDL.h>
namespace quokka_gfx
{
//--------------------------------------------------------------
struct Size
{
int w = 0;
int h = 0;
};
//--------------------------------------------------------------
struct FSize
{
float w = 0.0f;
float h = 0.0f;
};
//--------------------------------------------------------------
struct Pos
{
int x = 0;
int y = 0;
};
//--------------------------------------------------------------
struct FPos
{
float x = 0.0f;
float y = 0.0f;
};
//--------------------------------------------------------------
} // namespace quokka_gfx