diff --git a/Application.cpp b/Application.cpp new file mode 100644 index 0000000..309a946 --- /dev/null +++ b/Application.cpp @@ -0,0 +1,57 @@ +// +// Application.cpp +// libscgui +// +// Created by Bob Polis at 2020-10-23 +// Copyright (c) 2020 SwiftCoder. All rights reserved. +// + +#include "Application.hpp" +#include "Window.hpp" + +using namespace sc::gui; + +Application Application::app; + +void Application::run() { + bool quit {false}; + while (!quit) { + SDL_Event event; + if (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + quit = true; + break; + case SDL_WINDOWEVENT: + quit = Window::handle_window_event(event); + break; + default: + quit = handle_event(event); + } + } + for (const Window& window : Window::windows()) { + window.update(); + } + } +} + +void Application::add_event_handler(EventHandler handler, SDL_EventType event) { + auto it = _event_handlers.find(event); + if (it != _event_handlers.end()) { + it->second.push_back(handler); + } else { + std::vector handlers {handler}; + _event_handlers.emplace(event, handlers); + } +} + +bool Application::handle_event(const SDL_Event& event) { + bool quit {false}; + auto it = _event_handlers.find(static_cast(event.type)); + if (it != _event_handlers.end()) { + for (EventHandler handler : it->second) { + quit |= handler(event); + } + } + return quit; +} diff --git a/Application.hpp b/Application.hpp new file mode 100644 index 0000000..60ada5c --- /dev/null +++ b/Application.hpp @@ -0,0 +1,49 @@ +// +// Application.hpp +// libscgui +// +// Created by Bob Polis at 2020-10-23 +// Copyright (c) 2020 SwiftCoder. All rights reserved. +// + +#ifndef _Application_H_ +#define _Application_H_ + +#include +#include +#include + +namespace sc { + namespace gui { + using EventHandler = bool(*)(const SDL_Event&); + + class Application { + public: + static Application& instance() { return app; } + + Application() = default; + ~Application() = default; + + // prohibit copying and moving + Application(const Application&) = delete; + Application& operator=(const Application&) = delete; + Application(Application&&) = delete; + Application& operator=(Application&&) = delete; + + void run(); + void add_event_handler(EventHandler handler, SDL_EventType event); + + private: + static Application app; + + std::map> _event_handlers; + + bool handle_event(const SDL_Event& event); + }; + + // convenience function + Application& app() { return Application::instance(); } + } +} + +#endif // _Application_H_ diff --git a/Window.cpp b/Window.cpp index af6bc85..adad041 100644 --- a/Window.cpp +++ b/Window.cpp @@ -8,15 +8,78 @@ #include #include +#include #include "Window.hpp" using namespace sc::gui; -Window::Window(const char* title) : _path {title} { +std::vector Window::_windows; +std::map> Window::_global_event_handlers; + +Window* Window::from_sdl(Uint32 window_id) { + SDL_Window* w {SDL_GetWindowFromID(window_id)}; + if (w) { + auto it = std::find_if(_windows.begin(), _windows.end(), [w](Window& win) { + return win.get_window() == w; + }); + if (it != _windows.end()) { + return &*it; + } + } + return nullptr; +} + +bool Window::handle_window_event(const SDL_Event& event) { + bool quit {handle_global_event(event)}; + Window* w {from_sdl(event.window.windowID)}; + if (w) { + return w->handle_event(event, quit); + } + return quit; +} + +bool Window::handle_global_event(const SDL_Event& event) { + bool quit {false}; + auto iter = _global_event_handlers.find(static_cast(event.window.event)); + if (iter != _global_event_handlers.end()) { + for (WindowEventHandler handler : iter->second) { + quit = handler(event, quit); + } + } + switch (event.window.event) { + case SDL_WINDOWEVENT_CLOSE: { + // remove window from window list + SDL_Window* w {SDL_GetWindowFromID(event.window.windowID)}; + auto it = std::remove_if(_windows.begin(), _windows.end(), [w](Window& win) { + return win.get_window() == w; + }); + _windows.erase(it, _windows.end()); + if (_windows.size() == 0) { + quit = true; + } + break; + } + default: + break; + } + return quit; +} + +void Window::add_global_event_handler(WindowEventHandler handler, SDL_WindowEventID type) { + auto it = _global_event_handlers.find(type); + if (it != _global_event_handlers.end()) { + it->second.push_back(handler); + } else { + std::vector handlers {handler}; + _global_event_handlers.emplace(type, handlers); + } +} + +Window::Window(const char* title) : _title {title} { SDL_Window* win = SDL_CreateWindow(title, - SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - 900, 540, - SDL_WINDOW_RESIZABLE); + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 900, 540, + SDL_WINDOW_RESIZABLE); _w.reset(win); _r.reset(SDL_CreateRenderer(win, -1, 0)); } @@ -33,7 +96,7 @@ void Window::update() const { void Window::load_image() { // load image from file into surface - SDL_Surface* loaded = IMG_Load(_path.c_str()); + SDL_Surface* loaded = IMG_Load(_title.c_str()); if (!loaded) throw std::runtime_error(IMG_GetError()); std::unique_ptr loaded_ {loaded, SDL_FreeSurface}; @@ -64,3 +127,23 @@ void Window::load_image() { } set_size(w, h); } + +void Window::add_event_handler(WindowEventHandler handler, SDL_WindowEventID type) { + auto it = _event_handlers.find(type); + if (it != _event_handlers.end()) { + it->second.push_back(handler); + } else { + std::vector handlers {handler}; + _event_handlers.emplace(type, handlers); + } +} + +bool Window::handle_event(const SDL_Event& event, bool quit) { + auto it = _event_handlers.find(static_cast(event.window.event)); + if (it != _event_handlers.end()) { + for (WindowEventHandler handler : it->second) { + quit = handler(event, quit); + } + } + return quit; +} diff --git a/Window.hpp b/Window.hpp index 0388232..55c76fa 100644 --- a/Window.hpp +++ b/Window.hpp @@ -13,13 +13,26 @@ #include #include #include +#include +#include namespace sc { namespace gui { + using WindowEventHandler = bool(*)(const SDL_Event&, bool quit); + class Window { public: + static std::vector& windows() { return _windows; } + static bool handle_window_event(const SDL_Event& event); + static bool handle_global_event(const SDL_Event& event); + static void add_global_event_handler(WindowEventHandler handler, SDL_WindowEventID type); + static Window* from_sdl(Uint32 window_id); + Window(const char* title); + void add_event_handler(WindowEventHandler handler, SDL_WindowEventID type); + bool handle_event(const SDL_Event& event, bool quit); + SDL_Window* get_window() const { return _w.get(); } void set_size(int w, int h); @@ -27,10 +40,14 @@ namespace sc { void load_image(); private: - std::string _path; + static std::vector _windows; + static std::map> _global_event_handlers; + + std::string _title; std::unique_ptr _w {nullptr, SDL_DestroyWindow}; std::unique_ptr _r {nullptr, SDL_DestroyRenderer}; std::unique_ptr _t {nullptr, SDL_DestroyTexture}; + std::map> _event_handlers; }; } }