handle mouse events

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2025-06-09 00:17:54 +02:00
parent 60fccfeccc
commit 266114d046
53 changed files with 1144 additions and 1008 deletions

View File

@@ -2,11 +2,13 @@
#include <SDL3/SDL.h>
auto createWindow(const char *title, const int width, const int height) -> Window * {
auto CreateWindow(const char *title, const int width, const int height) -> Window *
{
constexpr uint32_t window_flag = SDL_WINDOW_HIDDEN;
const auto window = SDL_CreateWindow(title, width, height, window_flag);
if (window == nullptr) {
if (window == nullptr)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window", SDL_GetError(), nullptr);
return nullptr;
}
@@ -14,7 +16,8 @@ auto createWindow(const char *title, const int width, const int height) -> Windo
const SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
if (const auto renderer = SDL_CreateRendererWithProperties(props); renderer == nullptr) {
if (const auto renderer = SDL_CreateRendererWithProperties(props); renderer == nullptr)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create renderer", SDL_GetError(), nullptr);
return nullptr;
}

View File

@@ -2,4 +2,4 @@
#include "model/Window.h"
auto createWindow(const char *title, int width, int height) -> Window *;
auto CreateWindow(const char *title, int width, int height) -> Window *;

View File

@@ -1,46 +1,108 @@
#include "ResourceManager.h"
#include <memory>
#include <iostream>
#include <SDL3_image/SDL_image.h>
#include <memory>
#include <ranges>
ResourceManager& ResourceManager::getInstance() {
ResourceManager &ResourceManager::Instance()
{
static ResourceManager instance;
return instance;
}
ResourceManager::ResourceManager() {
ResourceManager::ResourceManager()
{
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "ResourceManager instance created.");
}
ResourceManager::~ResourceManager() {
for(const auto& texture : m_textures | std::views::values) {
if(texture != nullptr) {
/**
* @brief Destructor for the ResourceManager
* Cleans up all loaded textures and frees memory
*/
ResourceManager::~ResourceManager()
{
for (const auto &texture : m_textures | std::views::values)
{
if (texture != nullptr)
{
SDL_DestroyTexture(texture);
}
}
m_textures.clear();
}
SDL_Texture* ResourceManager::get_texture(SDL_Renderer* renderer, const std::string& path) {
std::string ResourceManager::GetResourcePath(const std::string &fileName)
{
const auto basePath_c = SDL_GetBasePath();
if (!basePath_c)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error retrieving base path: %s", SDL_GetError());
// Fallback to relative path as a last resort
// Warning: This only works reliably if the working directory is correct
// (e.g., when debugging from IDE)
return fileName; // Fallback (unsafe)
}
const std::string basePath(basePath_c);
std::string fullPath = basePath;
#ifdef __APPLE__
// Special handling for macOS bundle structure
// Navigate from /Contents/MacOS/ to /Contents/Resources/
size_t lastSlash = fullPath.find_last_of('/');
if (lastSlash != std::string::npos)
{
fullPath = fullPath.substr(0, lastSlash); // Remove executable name
lastSlash = fullPath.find_last_of('/');
if (lastSlash != std::string::npos)
{
fullPath = fullPath.substr(0, lastSlash + 1); // Go to Contents folder
fullPath += "Resources/";
}
else
{
// Fallback if structure is unexpected
fullPath = basePath; // Use original base path
fullPath += "Resources/"; // And try with this
}
}
else
{
// Fallback for unexpected path structure
fullPath = basePath;
fullPath += "Resources/";
}
#else
// For other platforms (Windows, Linux, etc.)
// Use standard assets folder
fullPath += "assets/";
#endif
return fullPath + fileName;
}
SDL_Texture *ResourceManager::GetTextureByName(SDL_Renderer *renderer, const std::string &path)
{
std::lock_guard lock(m_mutex);
if(const auto search = m_textures.find(path); search != m_textures.end()) {
// Check if texture is already in cache
if (const auto search = m_textures.find(GetResourcePath(path)); search != m_textures.end())
{
return search->second;
}
SDL_Texture* texture = IMG_LoadTexture(renderer, path.c_str());
// Load new texture
SDL_Texture *texture = IMG_LoadTexture(renderer, GetResourcePath(path).c_str());
if(!texture) {
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"Could not load %s -> %s",
path.c_str(),
SDL_GetError());
if (!texture)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load %s -> %s", GetResourcePath(path).c_str(),
SDL_GetError());
return nullptr;
}
// Store texture in cache
m_textures[path] = texture;
return texture;
}

View File

@@ -1,27 +1,59 @@
#pragma once
#include <unordered_map>
#include <string>
#include <mutex>
#include <string>
#include <unordered_map>
#include <SDL3/SDL.h>
class ResourceManager {
class ResourceManager
{
public:
static ResourceManager& getInstance();
/**
* @brief Returns the singleton instance of the ResourceManager
* @return Reference to the ResourceManager instance
*/
static ResourceManager &Instance();
ResourceManager(const ResourceManager&) = delete;
ResourceManager& operator=(const ResourceManager&) = delete;
ResourceManager(ResourceManager&&) = delete;
ResourceManager& operator=(ResourceManager&&) = delete;
ResourceManager(const ResourceManager &) = delete;
ResourceManager &operator=(const ResourceManager &) = delete;
ResourceManager(ResourceManager &&) = delete;
ResourceManager &operator=(ResourceManager &&) = delete;
~ResourceManager();
SDL_Texture* get_texture(SDL_Renderer* renderer, const std::string& path);
/**
* @brief Loads a texture or returns an already loaded texture
* @param renderer The SDL_Renderer used to load the texture
* @param path Path to the texture file (relative to resource directory)
* @return Pointer to the loaded SDL_Texture or nullptr on error
*
* This function implements a caching system for textures. If a texture
* has already been loaded, it returns it from the cache. Otherwise,
* it loads the texture and stores it in the cache. Access is thread-safe
* through mutex protection.
*/
SDL_Texture *GetTextureByName(SDL_Renderer *renderer, const std::string &path);
private:
/**
* @brief Constructor for the ResourceManager
* Initializes a new instance and logs its creation
*/
ResourceManager();
std::unordered_map<std::string, SDL_Texture*> m_textures;
/**
* @brief Determines the full resource path for a file
* @param fileName Name of the resource file
* @return Complete path to the resource
*
* This function handles platform-specific paths (especially macOS) and
* constructs the correct path to resources. For macOS, it considers the
* bundle structure (.app/Contents/Resources/).
*/
static std::string GetResourcePath(const std::string &fileName);
std::unordered_map<std::string, SDL_Texture *> m_textures;
mutable std::mutex m_mutex;
};
};

View File

@@ -1,61 +1,73 @@
#include "debug/DebugOverlay.h"
#include <imgui_impl_sdlrenderer3.h>
#include "Common.h"
#include "Version.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "ui/Matrix.h"
#include <imgui_impl_sdlrenderer3.h>
namespace DebugOverlay {
void init(const AppContext* context) {
namespace DebugOverlay
{
void Init(const AppContext *context)
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io{ImGui::GetIO()};
ImGuiIO &io{ImGui::GetIO()};
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark();
ImGui_ImplSDL3_InitForSDLRenderer(context->window(), context->renderer());
ImGui_ImplSDLRenderer3_Init(context->renderer());
ImGui_ImplSDL3_InitForSDLRenderer(context->MainWindow(), context->MainRenderer());
ImGui_ImplSDLRenderer3_Init(context->MainRenderer());
}
void update(AppContext* context, const SDL_Event* event) {
void Update(AppContext *context, const SDL_Event *event)
{
ImGui_ImplSDL3_ProcessEvent(event);
if(show_led_matrix) {
if(context->matrix_window() == nullptr) {
const auto win = createWindow("LED Matrix", 32 * 50, 8 * 50);
if (show_led_matrix)
{
if (context->LedMatrixWindow() == nullptr)
{
const auto win = CreateWindow("LED Matrix", 32 * 50, 8 * 50);
SDL_SetWindowFocusable(win->window(), false);
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
SDL_SetWindowPosition(win->window(), 0, 0);
SDL_ShowWindow(win->window());
context->setMatrix(new Matrix(win));
context->SetMatrix(new Matrix(win));
}
} else {
if(context->matrix_window() != nullptr) {
SDL_DestroyWindow(context->matrix_window());
}
else
{
if (context->LedMatrixWindow() != nullptr)
{
SDL_DestroyWindow(context->LedMatrixWindow());
context->setMatrix(nullptr);
context->SetMatrix(nullptr);
}
}
}
void render(const AppContext* context) {
void Render(const AppContext *context)
{
ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
if(show_debug_window && ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("Config")) {
if (show_debug_window && ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("Config"))
{
ImGui::Checkbox("Show LED Matrix", &show_led_matrix);
ImGui::Checkbox("Show Unhandled Events", &show_unhandled_events);
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Help")) {
if (ImGui::BeginMenu("Help"))
{
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::SeparatorText("App Info");
ImGui::Text("Project: %s", MyProject.c_str());
@@ -70,12 +82,13 @@ void render(const AppContext* context) {
// Rendering
ImGui::Render();
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), context->renderer());
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), context->MainRenderer());
}
void cleanup() {
void Cleanup()
{
ImGui_ImplSDLRenderer3_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
}
} // namespace DebugOverlay
} // namespace DebugOverlay

View File

@@ -4,16 +4,17 @@
#include "model/AppContext.h"
namespace DebugOverlay {
namespace DebugOverlay
{
inline bool show_debug_window = false;
inline bool show_unhandled_events = false;
inline bool show_led_matrix = true;
inline bool show_led_matrix = false;
void init(const AppContext* context);
void Init(const AppContext *context);
void update(AppContext* context, const SDL_Event* event);
void Update(AppContext *context, const SDL_Event *event);
void render(const AppContext* context);
void Render(const AppContext *context);
void cleanup();
}
void Cleanup();
}

View File

@@ -2,10 +2,10 @@
#include "u8g2.h"
#define U8G2_SCREEN_WIDTH (128)
#define U8G2_SCREEN_HEIGHT (64)
#define U8G2_SCREEN_FACTOR (3)
#define U8G2_SCREEN_WIDTH (128)
#define U8G2_SCREEN_HEIGHT (64)
#define U8G2_SCREEN_FACTOR (3)
#define U8G2_SCREEN_PADDING (25)
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

View File

@@ -3,8 +3,10 @@
#include <SDL3/SDL.h>
#include <cstdint>
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_BYTE_SEND:
case U8X8_MSG_BYTE_INIT:
case U8X8_MSG_BYTE_SET_DC:
@@ -18,8 +20,10 @@ uint8_t u8x8_byte_sdl_hw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* a
return 1;
}
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
switch(msg) {
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_DELAY_MILLI:
SDL_Delay(arg_int);
break;

View File

@@ -1,160 +0,0 @@
#include <imgui_impl_sdlrenderer3.h>
#include <vector>
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include "Common.h"
#include "debug/DebugOverlay.h"
#include "hal/u8g2_hal_sdl.h"
#include "model/AppContext.h"
#include "ui/Device.h"
#include "ui/UIWidget.h"
#include "u8g2.h"
#include "PushButton.h"
#include "ui/widgets/Button.h"
#include "ui/widgets/D_Pad.h"
constexpr unsigned int WINDOW_WIDTH =
(U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + 2 * BUTTON_WIDTH +
DPAD_WIDTH + 2 * U8G2_SCREEN_PADDING);
constexpr unsigned int WINDOW_HEIGHT = (U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + 50);
std::shared_ptr<Device> device;
std::vector<std::shared_ptr<UIWidget>> widgets;
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
if(SDL_Init(SDL_INIT_VIDEO) == false) {
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL! -> %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if(TTF_Init() == false) {
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize TTF! -> %s", SDL_GetError());
return SDL_APP_FAILURE;
}
const auto win = createWindow("System Control (Simulator)", WINDOW_WIDTH, WINDOW_HEIGHT);
if(!win) {
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window! -> %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
SDL_SetWindowPosition(win->window(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_ShowWindow(win->window());
const auto context = new AppContext(win);
*appstate = context;
device = std::make_shared<Device>(context);
widgets.push_back(device);
DebugOverlay::init(context);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
const auto context = static_cast<AppContext*>(appstate);
DebugOverlay::update(context, event);
switch(event->type) {
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
/// multi window
if(SDL_GetWindowID(context->window()) == event->window.windowID) {
return SDL_APP_SUCCESS;
}
break;
case SDL_EVENT_QUIT:
/// single window application
return SDL_APP_SUCCESS;
case SDL_EVENT_KEY_DOWN:
switch(event->key.key) {
case SDLK_ESCAPE:
return SDL_APP_SUCCESS;
case SDLK_UP:
device->onButtonClicked(BUTTON_UP);
break;
case SDLK_DOWN:
device->onButtonClicked(BUTTON_DOWN);
break;
case SDLK_LEFT:
device->onButtonClicked(BUTTON_LEFT);
break;
case SDLK_RIGHT:
device->onButtonClicked(BUTTON_RIGHT);
break;
case SDLK_RETURN:
device->onButtonClicked(BUTTON_SELECT);
break;
case SDLK_BACKSPACE:
device->onButtonClicked(BUTTON_BACK);
break;
default:
break;
}
break;
case SDL_EVENT_KEY_UP:
if(event->key.key == SDLK_LSHIFT) {
DebugOverlay::show_debug_window = !DebugOverlay::show_debug_window;
}
break;
case SDL_EVENT_MOUSE_MOTION:
//device->hitTest(&event->motion);
break;
default: {
if(DebugOverlay::show_unhandled_events) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Unused event: %d", event->type);
}
} break;
}
// return continue to continue
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void* appstate) {
const auto context = static_cast<AppContext*>(appstate);
/// render main window
SDL_SetRenderDrawColor(context->renderer(), 0, 0, 0, 255);
SDL_RenderClear(context->renderer());
for(const auto& widget : widgets) {
widget->render();
}
DebugOverlay::render(context);
SDL_RenderPresent(context->renderer());
/// render led matrix
context->matrix_render();
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
DebugOverlay::cleanup();
free(appstate);
// SDL will clean up the window/renderer for us.
TTF_Quit();
}

180
src/main.cpp Normal file
View File

@@ -0,0 +1,180 @@
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <imgui_impl_sdlrenderer3.h>
#include <vector>
#include <u8g2.h>
#include "Common.h"
#include "debug/DebugOverlay.h"
#include "hal/u8g2_hal_sdl.h"
#include "model/AppContext.h"
#include "PushButton.h"
#include "ui/Device.h"
#include "ui/UIWidget.h"
#include "ui/widgets/Button.h"
#include "ui/widgets/D_Pad.h"
constexpr unsigned int WINDOW_WIDTH = (U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING +
2 * BUTTON_WIDTH + DPAD_WIDTH + 2 * U8G2_SCREEN_PADDING);
constexpr unsigned int WINDOW_HEIGHT = (U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + 50);
std::shared_ptr<Device> device;
std::vector<std::shared_ptr<UIWidget>> widgets;
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
if (SDL_Init(SDL_INIT_VIDEO) == false)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL! -> %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (TTF_Init() == false)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize TTF! -> %s", SDL_GetError());
return SDL_APP_FAILURE;
}
const auto win = CreateWindow("System Control (Simulator)", WINDOW_WIDTH, WINDOW_HEIGHT);
if (!win)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window! -> %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
SDL_SetWindowPosition(win->window(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_ShowWindow(win->window());
const auto context = new AppContext(win);
*appstate = context;
device = std::make_shared<Device>(context);
widgets.push_back(device);
DebugOverlay::Init(context);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
const auto context = static_cast<AppContext *>(appstate);
DebugOverlay::Update(context, event);
switch (event->type)
{
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
/// multi window
if (SDL_GetWindowID(context->MainWindow()) == event->window.windowID)
{
return SDL_APP_SUCCESS;
}
break;
case SDL_EVENT_QUIT:
/// single window application
return SDL_APP_SUCCESS;
case SDL_EVENT_KEY_DOWN:
switch (event->key.key)
{
case SDLK_ESCAPE:
return SDL_APP_SUCCESS;
case SDLK_UP:
device->OnButtonClicked(BUTTON_UP);
break;
case SDLK_DOWN:
device->OnButtonClicked(BUTTON_DOWN);
break;
case SDLK_LEFT:
device->OnButtonClicked(BUTTON_LEFT);
break;
case SDLK_RIGHT:
device->OnButtonClicked(BUTTON_RIGHT);
break;
case SDLK_RETURN:
device->OnButtonClicked(BUTTON_SELECT);
break;
case SDLK_BACKSPACE:
device->OnButtonClicked(BUTTON_BACK);
break;
default:
break;
}
break;
case SDL_EVENT_KEY_UP:
if (event->key.key == SDLK_LSHIFT)
{
DebugOverlay::show_debug_window = !DebugOverlay::show_debug_window;
}
break;
case SDL_EVENT_MOUSE_MOTION:
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (event->button.button == SDL_BUTTON_LEFT)
{
device->HandleTap(&event->button);
}
break;
case SDL_EVENT_MOUSE_BUTTON_UP:
if (event->button.button == SDL_BUTTON_LEFT)
{
device->ReleaseTap(&event->button);
}
default: {
if (DebugOverlay::show_unhandled_events)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Unused event: %d", event->type);
}
}
break;
}
// return continue to continue
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
const auto context = static_cast<AppContext *>(appstate);
/// render main window
SDL_SetRenderDrawColor(context->MainRenderer(), 0, 0, 0, 255);
SDL_RenderClear(context->MainRenderer());
for (const auto &widget : widgets)
{
widget->Render();
}
DebugOverlay::Render(context);
SDL_RenderPresent(context->MainRenderer());
/// render led matrix
context->Render();
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
DebugOverlay::Cleanup();
free(appstate);
// SDL will clean up the window/renderer for us.
TTF_Quit();
}

View File

@@ -2,42 +2,53 @@
#include "ui/Matrix.h"
auto AppContext::window() const -> SDL_Window* {
auto AppContext::MainWindow() const -> SDL_Window *
{
return m_window->window();
}
auto AppContext::renderer() const -> SDL_Renderer* {
auto AppContext::MainRenderer() const -> SDL_Renderer *
{
return m_window->renderer();
}
auto AppContext::surface() const -> SDL_Surface* {
auto AppContext::MainSurface() const -> SDL_Surface *
{
return SDL_GetWindowSurface(m_window->window());
}
void AppContext::setMatrix(Matrix* matrix) {
void AppContext::SetMatrix(Matrix *matrix)
{
m_matrix = matrix;
}
auto AppContext::matrix() const -> Matrix* {
auto AppContext::LedMatrix() const -> Matrix *
{
return m_matrix;
}
auto AppContext::matrix_window() const -> SDL_Window* {
if(m_matrix && m_matrix->window()) {
auto AppContext::LedMatrixWindow() const -> SDL_Window *
{
if (m_matrix && m_matrix->window())
{
return m_matrix->window()->window();
}
return nullptr;
}
auto AppContext::matrix_renderer() const -> SDL_Renderer* {
if(m_matrix && m_matrix->window()) {
auto AppContext::LedMatrixRenderer() const -> SDL_Renderer *
{
if (m_matrix && m_matrix->window())
{
return m_matrix->window()->renderer();
}
return nullptr;
}
void AppContext::matrix_render() const {
if(m_matrix && m_matrix->window()) {
m_matrix->render();
void AppContext::Render() const
{
if (m_matrix && m_matrix->window())
{
m_matrix->Render();
}
}

View File

@@ -5,39 +5,41 @@
class Matrix;
class AppContext {
public:
explicit AppContext(const Window* window)
: m_window(window) {
m_font_default = TTF_OpenFont("assets/haxrcorp-4089.otf", 21);
m_font_text = TTF_OpenFont("assets/Helvetica-Bold.otf", 21);
class AppContext
{
public:
explicit AppContext(const Window *window) : m_window(window)
{
m_font_default = TTF_OpenFont("haxrcorp-4089.otf", 21);
m_font_text = TTF_OpenFont("Helvetica-Bold.otf", 21);
}
~AppContext() {
~AppContext()
{
TTF_CloseFont(m_font_default);
TTF_CloseFont(m_font_text);
}
[[nodiscard]] auto window() const -> SDL_Window*;
[[nodiscard]] auto MainWindow() const -> SDL_Window *;
[[nodiscard]] auto renderer() const -> SDL_Renderer*;
[[nodiscard]] auto MainRenderer() const -> SDL_Renderer *;
[[nodiscard]] auto surface() const -> SDL_Surface*;
[[nodiscard]] auto MainSurface() const -> SDL_Surface *;
void setMatrix(Matrix* matrix);
void SetMatrix(Matrix *matrix);
[[nodiscard]] auto matrix() const -> Matrix*;
[[nodiscard]] auto LedMatrix() const -> Matrix *;
[[nodiscard]] auto matrix_window() const -> SDL_Window*;
[[nodiscard]] auto LedMatrixWindow() const -> SDL_Window *;
[[nodiscard]] auto matrix_renderer() const -> SDL_Renderer*;
[[nodiscard]] auto LedMatrixRenderer() const -> SDL_Renderer *;
void matrix_render() const;
void Render() const;
TTF_Font* m_font_default = nullptr;
TTF_Font *m_font_default = nullptr;
private:
const Window* m_window;
Matrix* m_matrix = nullptr;
TTF_Font* m_font_text = nullptr;
private:
const Window *m_window;
Matrix *m_matrix = nullptr;
TTF_Font *m_font_text = nullptr;
};

View File

@@ -1,9 +1,11 @@
#include "model/Window.h"
auto Window::window() const -> SDL_Window* {
auto Window::window() const -> SDL_Window *
{
return m_window;
}
auto Window::renderer() const -> SDL_Renderer* {
auto Window::renderer() const -> SDL_Renderer *
{
return SDL_GetRenderer(m_window);
}

View File

@@ -2,16 +2,17 @@
#include "SDL3/SDL.h"
class Window {
public:
explicit Window(SDL_Window* window)
: m_window(window) {
class Window
{
public:
explicit Window(SDL_Window *window) : m_window(window)
{
}
[[nodiscard]] auto window() const -> SDL_Window*;
[[nodiscard]] auto window() const -> SDL_Window *;
[[nodiscard]] auto renderer() const -> SDL_Renderer*;
[[nodiscard]] auto renderer() const -> SDL_Renderer *;
private:
SDL_Window* m_window = nullptr;
private:
SDL_Window *m_window = nullptr;
};

View File

@@ -1,200 +1,264 @@
#include "ui/Device.h"
#include "SDL3_ttf/SDL_ttf.h"
#include <hal/u8g2_hal_sdl.h>
#include <u8g2.h>
#include "MenuOptions.h"
#include "ui/SplashScreen.h"
#include "ui/widgets/Button.h"
#include "ui/widgets/D_Pad.h"
#include <u8g2.h>
#include <hal/u8g2_hal_sdl.h>
#include "ui/SplashScreen.h"
#include "MenuOptions.h"
u8g2_t u8g2;
menu_options_t options;
static void set_pixel_rgba(
const SDL_Surface* surface,
const int x,
const int y,
const uint32_t pixel_color) {
if(!surface || x < 0 || x >= surface->w || y < 0 || y >= surface->h) {
static void set_pixel_rgba(const SDL_Surface *surface, const int x, const int y, const uint32_t pixel_color)
{
if (!surface || x < 0 || x >= surface->w || y < 0 || y >= surface->h)
{
return;
}
const auto p =
static_cast<uint8_t*>(surface->pixels) + y * surface->pitch + x * sizeof(uint32_t);
*reinterpret_cast<uint32_t*>(p) = pixel_color;
const auto p = static_cast<uint8_t *>(surface->pixels) + y * surface->pitch + x * sizeof(uint32_t);
*reinterpret_cast<uint32_t *>(p) = pixel_color;
}
Device::Device(void* appstate)
: UIWidget(appstate) {
m_children.push_back(
std::make_shared<Button>(
get_context(),
U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + DPAD_WIDTH,
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - BUTTON_WIDTH,
BUTTON_WIDTH,
[]() { pushKey(SDLK_RETURN); }));
m_children.push_back(
std::make_shared<Button>(
get_context(),
U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 4 * U8G2_SCREEN_PADDING + DPAD_WIDTH +
BUTTON_WIDTH,
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - 2 * BUTTON_WIDTH,
BUTTON_WIDTH,
[]() { pushKey(SDLK_BACKSPACE); }));
m_children.push_back(
std::make_shared<D_Pad>(
get_context(),
U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 2 * U8G2_SCREEN_PADDING,
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - DPAD_WIDTH,
DPAD_WIDTH,
[](int d) {}));
Device::Device(void *appstate) : UIWidget(appstate)
{
auto dpad_callback = [](const D_Pad::Direction direction) {
SDL_Keycode key = 0;
switch (direction)
{
case D_Pad::Direction::UP:
key = SDLK_UP;
break;
case D_Pad::Direction::DOWN:
key = SDLK_DOWN;
break;
case D_Pad::Direction::LEFT:
key = SDLK_LEFT;
break;
case D_Pad::Direction::RIGHT:
key = SDLK_RIGHT;
break;
case D_Pad::Direction::NONE: // Fallthrough oder keine Aktion
default:
break;
}
if (key != 0)
{
PushKey(key);
}
};
u8g2_Setup_sh1106_128x64_noname_f(
&u8g2, U8G2_R0, u8x8_byte_sdl_hw_spi, u8x8_gpio_and_delay_sdl);
m_children.push_back(std::make_shared<Button>(
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + DPAD_WIDTH,
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - BUTTON_WIDTH, BUTTON_WIDTH,
[]() {
PushKey(SDLK_RETURN);
}));
m_children.push_back(std::make_shared<Button>(
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 4 * U8G2_SCREEN_PADDING + DPAD_WIDTH + BUTTON_WIDTH,
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - 2 * BUTTON_WIDTH, BUTTON_WIDTH,
[]() {
PushKey(SDLK_BACKSPACE);
}));
m_children.push_back(std::make_shared<D_Pad>(
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 2 * U8G2_SCREEN_PADDING,
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - DPAD_WIDTH, DPAD_WIDTH, dpad_callback));
u8g2_Setup_sh1106_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sdl_hw_spi, u8x8_gpio_and_delay_sdl);
u8x8_InitDisplay(u8g2_GetU8x8(&u8g2));
options = {
.u8g2 = &u8g2,
.setScreen = [this](const std::shared_ptr<Widget>& screen) { this->setScreen(screen); },
.pushScreen = [this](const std::shared_ptr<Widget>& screen) { this->pushScreen(screen); },
.popScreen = [this]() { this->popScreen(); },
.setScreen = [this](const std::shared_ptr<Widget> &screen) {
this->SetScreen(screen);
},
.pushScreen = [this](const std::shared_ptr<Widget> &screen) {
this->PushScreen(screen);
},
.popScreen = [this]() {
this->PopScreen();
},
};
widget = std::make_shared<SplashScreen>(&options);
}
void Device::setScreen(const std::shared_ptr<Widget>& screen) {
if(screen != nullptr) {
void Device::SetScreen(const std::shared_ptr<Widget> &screen)
{
if (screen != nullptr)
{
widget = screen;
history.clear();
history.emplace_back(widget);
}
}
void Device::pushScreen(const std::shared_ptr<Widget>& screen) {
if(screen != nullptr) {
void Device::PushScreen(const std::shared_ptr<Widget> &screen)
{
if (screen != nullptr)
{
widget = screen;
history.emplace_back(widget);
}
}
void Device::popScreen() {
if(history.size() >= 2) {
void Device::PopScreen()
{
if (history.size() >= 2)
{
history.pop_back();
widget = history.back();
}
}
void Device::pushKey(const SDL_Keycode key) {
void Device::PushKey(const SDL_Keycode key)
{
SDL_Event ev;
ev.type = SDL_EVENT_KEY_DOWN;
ev.key.key = key;
SDL_PushEvent(&ev);
}
void Device::render_u8g2() const {
SDL_Surface* u8g2_surface = nullptr;
SDL_Texture* u8g2_texture = nullptr;
void Device::RenderU8G2() const
{
SDL_Surface *u8g2_surface = nullptr;
SDL_Texture *u8g2_texture = nullptr;
u8g2_surface =
SDL_CreateSurface(U8G2_SCREEN_WIDTH, U8G2_SCREEN_HEIGHT, SDL_PIXELFORMAT_RGBA8888);
u8g2_surface = SDL_CreateSurface(U8G2_SCREEN_WIDTH, U8G2_SCREEN_HEIGHT, SDL_PIXELFORMAT_RGBA8888);
if(!u8g2_surface) {
if (!u8g2_surface)
{
SDL_Log("SDL_CreateSurfaceFrom Error: %s\n", SDL_GetError());
} else {
}
else
{
const auto color_black = SDL_MapSurfaceRGBA(u8g2_surface, 0, 0, 0, 255);
const auto color_white = SDL_MapSurfaceRGBA(u8g2_surface, 255, 255, 255, 255);
if(!SDL_LockSurface(u8g2_surface)) {
SDL_Log("SDL_LockSurface Error: %s\n", SDL_GetError());
} else {
const auto u8g2_buf = u8g2_GetBufferPtr(&u8g2);
for(auto y = 0; y < U8G2_SCREEN_HEIGHT; ++y) {
for(auto x = 0; x < U8G2_SCREEN_WIDTH; ++x) {
const auto page = y / 8;
const auto bit_index = y % 8;
const auto byte_ptr = u8g2_buf + page * U8G2_SCREEN_WIDTH + x;
const auto pixel_is_set = (*byte_ptr >> bit_index) & 0x01;
set_pixel_rgba(u8g2_surface, x, y, pixel_is_set ? color_white : color_black);
}
const auto u8g2_buf = u8g2_GetBufferPtr(&u8g2);
for (auto y = 0; y < U8G2_SCREEN_HEIGHT; ++y)
{
for (auto x = 0; x < U8G2_SCREEN_WIDTH; ++x)
{
const auto page = y / 8;
const auto bit_index = y % 8;
const auto byte_ptr = u8g2_buf + page * U8G2_SCREEN_WIDTH + x;
const auto pixel_is_set = (*byte_ptr >> bit_index) & 0x01;
set_pixel_rgba(u8g2_surface, x, y, pixel_is_set ? color_white : color_black);
}
SDL_UnlockSurface(u8g2_surface);
}
u8g2_texture = SDL_CreateTextureFromSurface(get_context()->renderer(), u8g2_surface);
if(!u8g2_texture) {
u8g2_texture = SDL_CreateTextureFromSurface(GetContext()->MainRenderer(), u8g2_surface);
if (!u8g2_texture)
{
SDL_Log("SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
}
if(!SDL_SetTextureScaleMode(u8g2_texture, SDL_SCALEMODE_NEAREST)) {
if (!SDL_SetTextureScaleMode(u8g2_texture, SDL_SCALEMODE_NEAREST))
{
SDL_Log("SDL_SetTextureScaleMode Error: %s\n", SDL_GetError());
}
SDL_DestroySurface(u8g2_surface);
}
if(u8g2_texture) {
if (u8g2_texture)
{
SDL_FRect destRect;
destRect.x = U8G2_SCREEN_PADDING;
destRect.y = U8G2_SCREEN_PADDING;
destRect.w = U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR;
destRect.h = U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR;
SDL_RenderTexture(get_context()->renderer(), u8g2_texture, nullptr, &destRect);
SDL_RenderTexture(GetContext()->MainRenderer(), u8g2_texture, nullptr, &destRect);
SDL_DestroyTexture(u8g2_texture);
}
}
void Device::draw_background() const {
void Device::DrawBackground() const
{
int windowWidth = 0;
int windowHeight = 0;
if(!SDL_GetWindowSize(get_context()->window(), &windowWidth, &windowHeight)) {
if (!SDL_GetWindowSize(GetContext()->MainWindow(), &windowWidth, &windowHeight))
{
SDL_Log("SDL_GetWindowSize Error: %s\n", SDL_GetError());
}
const auto rect =
SDL_FRect{0.0f, 0.0f, static_cast<float>(windowWidth), static_cast<float>(windowHeight)};
SDL_SetRenderDrawColor(get_context()->renderer(), 193, 46, 31, 255);
SDL_RenderFillRect(get_context()->renderer(), &rect);
const auto rect = SDL_FRect{0.0f, 0.0f, static_cast<float>(windowWidth), static_cast<float>(windowHeight)};
SDL_SetRenderDrawColor(GetContext()->MainRenderer(), 193, 46, 31, 255);
SDL_RenderFillRect(GetContext()->MainRenderer(), &rect);
}
void Device::draw_screen() const {
void Device::DrawScreen() const
{
u8g2_ClearBuffer(&u8g2);
if(widget != nullptr) {
if (widget != nullptr)
{
widget->update(SDL_GetTicks());
widget->render();
}
render_u8g2();
RenderU8G2();
}
void Device::draw_text() const {
constexpr auto color = SDL_Color({0, 255, 200});
const auto surface =
TTF_RenderText_Blended(get_context()->m_font_default, "HelloWorld SDL3 TTF", 0, color);
const auto texture = SDL_CreateTextureFromSurface(get_context()->renderer(), surface);
SDL_DestroySurface(surface);
const SDL_FRect dstRect{
500, 100, static_cast<float>(texture->w), static_cast<float>(texture->h)};
SDL_RenderTexture(get_context()->renderer(), texture, nullptr, &dstRect);
SDL_DestroyTexture(texture); // ? Is this safe to do here ?
}
void Device::Render() const
{
DrawBackground();
DrawScreen();
void Device::render() const {
draw_background();
draw_screen();
// draw_text();
for(const auto& child : m_children) {
child->render();
for (const auto &child : m_children)
{
child->Render();
}
}
void Device::hit_test(SDL_MouseMotionEvent* event) const {
SDL_Log("x: %f", event->x);
void Device::HandleTap(const SDL_MouseButtonEvent *event) const
{
// SDL_Log("HandleTap: x=%f, y=%f, button=%d", event->x, event->y, event->button);
for (const auto &child : m_children)
{
if (child->IsHit(static_cast<int>(event->x), static_cast<int>(event->y)))
{
child->OnTap(static_cast<int>(event->x), static_cast<int>(event->y));
break;
}
}
}
void Device::onButtonClicked(const uint8_t button) const {
if(widget != nullptr) {
void Device::ReleaseTap(const SDL_MouseButtonEvent *event) const
{
// SDL_Log("ReleaseTap: x=%f, y=%f, button=%d", event->x, event->y, event->button);
for (const auto &child : m_children)
{
if (child->IsHit(static_cast<int>(event->x), static_cast<int>(event->y)))
{
child->ReleaseTap(static_cast<int>(event->x), static_cast<int>(event->y));
break;
}
}
}
void Device::OnButtonClicked(const uint8_t button) const
{
if (widget != nullptr)
{
widget->onButtonClicked(button);
}
}
bool Device::IsHit(int mouse_x, int mouse_y) const
{
return false;
}
void Device::OnTap(int mouse_x, int mouse_y)
{
////
}
void Device::ReleaseTap(int mouse_x, int mouse_y)
{
///
}

View File

@@ -5,38 +5,44 @@
#include <vector>
#include "UIWidget.h"
#include "model/AppContext.h"
#include "common/Widget.h"
#include "model/AppContext.h"
class Device final : public UIWidget {
public:
explicit Device(void* appstate);
class Device final : public UIWidget
{
public:
explicit Device(void *appstate);
void render() const override;
void Render() const override;
void hit_test(SDL_MouseMotionEvent* event) const;
void HandleTap(const SDL_MouseButtonEvent *event) const;
void onButtonClicked(uint8_t button) const;
void ReleaseTap(const SDL_MouseButtonEvent *event) const;
private:
void draw_background() const;
void OnButtonClicked(uint8_t button) const;
void draw_screen() const;
[[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override;
void draw_text() const;
void OnTap(int mouse_x, int mouse_y) override;
void render_u8g2() const;
void ReleaseTap(int mouse_x, int mouse_y) override;
void setScreen(const std::shared_ptr<Widget>& screen);
private:
void DrawBackground() const;
void pushScreen(const std::shared_ptr<Widget>& screen);
void DrawScreen() const;
void popScreen();
void RenderU8G2() const;
static void pushKey(SDL_Keycode key);
void SetScreen(const std::shared_ptr<Widget> &screen);
void PushScreen(const std::shared_ptr<Widget> &screen);
void PopScreen();
static void PushKey(SDL_Keycode key);
std::vector<std::shared_ptr<UIWidget>> m_children{};
std::shared_ptr<Widget> widget;
std::vector<std::shared_ptr<Widget>> history;
};

View File

@@ -1,22 +1,30 @@
#include "ui/Matrix.h"
Matrix::Matrix(Window *window): m_window(window) {
Matrix::Matrix(Window *window) : m_window(window)
{
}
Window *Matrix::window() const {
Window *Matrix::window() const
{
return m_window;
}
void Matrix::draw_colored_grid(const int rows, const int cols, const float cellSize, const float spacing) const {
void Matrix::DrawColoredGrid(const int rows, const int cols, const float cellSize, const float spacing) const
{
int i = 0;
for (int w = 0; w < cols; w++) {
for (int w = 0; w < cols; w++)
{
const auto phase = w % (2 * rows);
for (int h_raw = 0; h_raw < rows; h_raw++) {
for (int h_raw = 0; h_raw < rows; h_raw++)
{
int h;
if (phase < rows) {
if (phase < rows)
{
h = h_raw;
} else {
}
else
{
h = rows - 1 - h_raw;
}
@@ -37,11 +45,12 @@ void Matrix::draw_colored_grid(const int rows, const int cols, const float cellS
}
}
void Matrix::render() const {
void Matrix::Render() const
{
SDL_SetRenderDrawColor(m_window->renderer(), 0, 0, 0, 255);
SDL_RenderClear(m_window->renderer());
draw_colored_grid(8, 32, 50.0f, 1.0f);
DrawColoredGrid(8, 32, 50.0f, 1.0f);
SDL_RenderPresent(m_window->renderer());
}

View File

@@ -2,16 +2,17 @@
#include "model/Window.h"
class Matrix {
public:
explicit Matrix(Window* window);
class Matrix
{
public:
explicit Matrix(Window *window);
[[nodiscard]] Window* window() const;
[[nodiscard]] Window *window() const;
void render() const;
void Render() const;
private:
void draw_colored_grid(int rows, int cols, float cellSize, float spacing) const;
private:
void DrawColoredGrid(int rows, int cols, float cellSize, float spacing) const;
Window* m_window;
Window *m_window;
};

View File

@@ -1,10 +1,12 @@
#include "ui/UIWidget.h"
UIWidget::UIWidget(void *appstate): m_context(static_cast<AppContext *>(appstate)) {
UIWidget::UIWidget(void *appstate) : m_context(static_cast<AppContext *>(appstate))
{
}
UIWidget::~UIWidget() = default;
auto UIWidget::get_context() const -> AppContext* {
auto UIWidget::GetContext() const -> AppContext *
{
return m_context;
}

View File

@@ -2,15 +2,22 @@
#include "model/AppContext.h"
class UIWidget {
class UIWidget
{
public:
explicit UIWidget(void *appstate);
virtual ~UIWidget();
virtual void render() const = 0;
virtual void Render() const = 0;
[[nodiscard]] AppContext *get_context() const;
[[nodiscard]] virtual bool IsHit(int mouse_x, int mouse_y) const = 0;
virtual void OnTap(int mouse_x, int mouse_y) = 0;
virtual void ReleaseTap(int mouse_x, int mouse_y) = 0;
[[nodiscard]] AppContext *GetContext() const;
private:
AppContext *m_context{};

View File

@@ -1,34 +1,49 @@
#include "ui/widgets/Button.h"
#include "ResourceManager.h"
#include <functional>
#include <utility>
#include <SDL3_image/SDL_image.h>
#include "ResourceManager.h"
auto pressed = false;
Button::Button(
void* appState,
const float x,
const float y,
const float width,
std::function<void()> callback)
: UIWidget(appState)
, m_x(x)
, m_y(y)
, m_width(width)
, m_callback(std::move(callback)) {
Button::Button(void *appState, const float x, const float y, const float width, std::function<void()> callback)
: UIWidget(appState), m_x(x), m_y(y), m_width(width), m_callback(std::move(callback))
{
}
void Button::render() const {
const auto button = ResourceManager::getInstance().get_texture(
get_context()->renderer(), "assets/button_normal.png");
const auto overlay = ResourceManager::getInstance().get_texture(
get_context()->renderer(), "assets/button_pressed_overlay.png");
void Button::Render() const
{
const auto button =
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "button_normal.png");
const auto overlay =
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "button_pressed_overlay.png");
const auto dst = SDL_FRect(m_x, m_y, m_width, m_width);
SDL_RenderTexture(get_context()->renderer(), button, nullptr, &dst);
if(pressed) {
SDL_RenderTexture(get_context()->renderer(), overlay, nullptr, &dst);
SDL_RenderTexture(GetContext()->MainRenderer(), button, nullptr, &dst);
if (m_isPressed)
{
SDL_RenderTexture(GetContext()->MainRenderer(), overlay, nullptr, &dst);
}
}
bool Button::IsHit(const int mouse_x, const int mouse_y) const
{
const auto fx = static_cast<float>(mouse_x);
const auto fy = static_cast<float>(mouse_y);
return (fx >= m_x && fx <= (m_x + m_width) &&
fy >= m_y && fy <= (m_y + m_width)) || m_isPressed;
}
void Button::OnTap(int mouse_x, int mouse_y)
{
if (m_callback)
{
m_isPressed = true;
m_callback();
}
}
void Button::ReleaseTap(int mouse_x, int mouse_y)
{
m_isPressed = false;
}

View File

@@ -6,15 +6,23 @@
#define BUTTON_WIDTH (35)
class Button final : public UIWidget {
public:
Button(void* appState, float x, float y, float width, std::function<void()> callback);
class Button final : public UIWidget
{
public:
Button(void *appState, float x, float y, float width, std::function<void()> callback);
void render() const override;
void Render() const override;
[[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override;
void OnTap(int mouse_x, int mouse_y) override;
void ReleaseTap(int mouse_x, int mouse_y) override;
private:
float m_x;
float m_y;
float m_width;
std::function<void()> m_callback;
bool m_isPressed = false;
};

View File

@@ -1,24 +1,82 @@
#include "D_Pad.h"
#include "ui/widgets/D_Pad.h"
#include "ResourceManager.h"
D_Pad::D_Pad(
void* appState,
const float x,
const float y,
const float width,
std::function<void(int)> callback)
: UIWidget(appState)
, m_x(x)
, m_y(y)
, m_width(width)
, m_callback(std::move(callback)) {
D_Pad::D_Pad(void *appState, const float x, const float y, const float width, std::function<void(Direction)> callback)
: UIWidget(appState), m_x(x), m_y(y), m_width(width), m_callback(std::move(callback))
{
}
void D_Pad::render() const {
const auto dPad = ResourceManager::getInstance().get_texture(
get_context()->renderer(), "assets/d-pad_normal.png");
void D_Pad::Render() const
{
const auto dPad =
ResourceManager::Instance().GetTextureByName(GetContext()->MainRenderer(), "d-pad_normal.png");
const auto dst = SDL_FRect(m_x, m_y, m_width, m_width);
SDL_RenderTexture(get_context()->renderer(), dPad, nullptr, &dst);
SDL_RenderTexture(GetContext()->MainRenderer(), dPad, nullptr, &dst);
}
bool D_Pad::IsHit(const int mouse_x, const int mouse_y) const
{
const auto fx = static_cast<float>(mouse_x);
const auto fy = static_cast<float>(mouse_y);
return (fx >= m_x && fx <= (m_x + m_width) &&
fy >= m_y && fy <= (m_y + m_width));
}
D_Pad::Direction D_Pad::GetDirectionFromTap(const float local_x, const float local_y) const
{
const float segment = m_width / 3.0f;
int col = -1;
if (local_x < segment)
col = 0;
else if (local_x < 2 * segment)
col = 1;
else if (local_x <= m_width)
col = 2;
else
return Direction::NONE;
int row = -1;
if (local_y < segment)
row = 0;
else if (local_y < 2 * segment)
row = 1;
else if (local_y <= m_width)
row = 2;
else
return Direction::NONE;
if (col == 1 && row == 0)
return Direction::UP;
if (col == 1 && row == 2)
return Direction::DOWN;
if (col == 0 && row == 1)
return Direction::LEFT;
if (col == 2 && row == 1)
return Direction::RIGHT;
return Direction::NONE;
}
void D_Pad::OnTap(const int mouse_x, const int mouse_y)
{
if (m_callback)
{
const auto local_x = static_cast<float>(mouse_x) - m_x;
const auto local_y = static_cast<float>(mouse_y) - m_y;
if (local_x >= 0 && local_x <= m_width && local_y >= 0 && local_y <= m_width)
{
const auto dir = GetDirectionFromTap(local_x, local_y);
m_callback(dir);
}
}
}
void D_Pad::ReleaseTap(const int mouse_x, const int mouse_y)
{
///
}

View File

@@ -5,15 +5,25 @@
#define DPAD_WIDTH (105)
class D_Pad final : public UIWidget {
public:
D_Pad(void* appState, float x, float y, float width, std::function<void(int)> callback);
void render() const override;
class D_Pad final : public UIWidget
{
public:
enum class Direction { NONE, UP, DOWN, LEFT, RIGHT };
D_Pad(void *appState, float x, float y, float width, std::function<void(Direction)> callback);
void Render() const override;
[[nodiscard]] bool IsHit(int mouse_x, int mouse_y) const override;
void OnTap(int mouse_x, int mouse_y) override;
void ReleaseTap(int mouse_x, int mouse_y) override;
private:
float m_x;
float m_y;
float m_width;
std::function<void(int)> m_callback;
float m_x, m_y, m_width;
std::function<void(Direction)> m_callback;
[[nodiscard]] Direction GetDirectionFromTap(float local_x, float local_y) const;
};