libscgui/Window.cpp

146 lines
4.4 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"
#include "Image.hpp"
using namespace sc::gui;
std::vector<Window> Window::_windows;
std::map<SDL_WindowEventID, std::vector<WindowEventHandler>> Window::_global_event_handlers;
Window& Window::new_window(const std::string& title) {
_windows.emplace_back(title);
return _windows.back();
}
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.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.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 std::string& title) : _title {title} {
SDL_Window* win = SDL_CreateWindow(title.c_str(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 450,
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::show_image(const Image& image) {
// create texture from surface
_t.reset(SDL_CreateTextureFromSurface(_r.get(), image.surface()));
// get screen size, to scale down if image is too big
int w {image.width()};
int h {image.height()};
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;
}