150 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| }
 | 
