libscgui/Window.cpp
2020-10-24 13:02:18 +02:00

150 lines
4.7 KiB
C++

//
// Window.cpp
// gui
//
// Created by Bob Polis at 2020-10-14
// Copyright (c) 2020 SwiftCoder. All rights reserved.
//
#include <SDL2/SDL_image.h>
#include <cmath>
#include <algorithm>
#include "Window.hpp"
using namespace sc::gui;
std::vector<Window> Window::_windows;
std::map<SDL_WindowEventID, std::vector<WindowEventHandler>> 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<SDL_WindowEventID>(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<WindowEventHandler> 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);
_w.reset(win);
_r.reset(SDL_CreateRenderer(win, -1, 0));
}
void Window::set_size(int w, int h) {
SDL_SetWindowSize(_w.get(), w, h);
}
void Window::update() const {
SDL_RenderClear(_r.get());
SDL_RenderCopy(_r.get(), _t.get(), nullptr, nullptr);
SDL_RenderPresent(_r.get());
}
void Window::load_image() {
// load image from file into surface
SDL_Surface* loaded = IMG_Load(_title.c_str());
if (!loaded) throw std::runtime_error(IMG_GetError());
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> loaded_ {loaded, SDL_FreeSurface};
// create texture from surface
_t.reset(SDL_CreateTextureFromSurface(_r.get(), loaded));
// query texture for properties, like image size
Uint32 format;
int access;
int w, h;
int result = SDL_QueryTexture(_t.get(), &format, &access, &w, &h);
if (result != 0) throw std::runtime_error(SDL_GetError());
// get screen size, to scale down if image is too big
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm);
if (dm.w < w || dm.h < h) {
double screen_ratio {static_cast<double>(dm.w) / dm.h};
double image_ratio {static_cast<double>(w) / h};
const int safety {100}; // room for window and desktop adornments
if (screen_ratio > image_ratio) { // screen relatively less high than image
h = dm.h - safety;
w = static_cast<int>(round(image_ratio * h));
} else { // screen relatively less wide than image
w = dm.w - safety;
h = static_cast<int>(round(w / image_ratio));
}
}
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<WindowEventHandler> handlers {handler};
_event_handlers.emplace(type, handlers);
}
}
bool Window::handle_event(const SDL_Event& event, bool quit) {
auto it = _event_handlers.find(static_cast<SDL_WindowEventID>(event.window.event));
if (it != _event_handlers.end()) {
for (WindowEventHandler handler : it->second) {
quit = handler(event, quit);
}
}
return quit;
}