FlowArithmeticCalculator 1.0.0
Flow Arithmetic Calculator
Loading...
Searching...
No Matches
GLFWQtWindowImpl.h
Go to the documentation of this file.
1#ifndef WINDOW_H
2#define WINDOW_H
3
4#include <GLFW/glfw3.h>
5
6#include <iostream>
7#include <memory>
8#include <stdexcept>
9
10#include "WindowBase.h"
11#include "logging/Loggable.h"
12#include "utils/misc.h"
15
16namespace gui::window {
24 template <typename RendererImpl, typename Canvas>
26 : protected business_logic::Loggable<GLFWQtWindowImpl<RendererImpl, Canvas>>,
27 public window::WindowBase<Canvas> {
28 public:
35 explicit GLFWQtWindowImpl(int width, int height, const char* title)
36 : business_logic::Loggable<GLFWQtWindowImpl<RendererImpl, Canvas>>(),
37 window::WindowBase<Canvas>(width, height, logger) {
38 initGLFW();
39
40 initializeGLFWWindow(width, height, title);
41
43 }
44
50 initGLFW();
51
52 // Get primary monitor
53 GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
54 if (primaryMonitor == nullptr) {
55 throw std::runtime_error("Failed to get primary monitor");
56 }
57
58 // Get video mode (which contains screen width and height)
59 const GLFWvidmode* videoMode = glfwGetVideoMode(primaryMonitor);
60 if (videoMode == nullptr) {
61 throw std::runtime_error("Failed to get primary monitor's video mode");
62 }
63
65 std::round(videoMode->width * 0.6), std::round(videoMode->height * 0.6), title);
66 };
67
68 // disable copy semantics
71
72 // disable move semantics
75
77 // initially invoke window->handleMouseMove
78 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
79 double mouseX;
80 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
81 double mouseY;
82 glfwGetCursorPos(glfwWindow, &mouseX, &mouseY);
83
84 this->handleMouseMove(mouseX, mouseY);
85 }
86
90 ~GLFWQtWindowImpl() override {
91 logger->info("GLFWQtWindowImpl {} is being destroyed",
93
94 if (glfwWindow != nullptr) {
95 glfwDestroyWindow(glfwWindow);
96 }
97 glfwTerminate();
98
99 logger->info("GLFWQtWindowImpl {} has been destroyed",
101 }
102
106 void run() override {
107 while (!shouldClose()) {
108 glfwPollEvents();
109
110 renderer->render();
111
112 // swap front and back buffers
113 glfwSwapBuffers(glfwWindow);
114 }
115
116 logger->info("shouldClose() returned true, exiting gracefully");
117 };
118
124 explicit operator GLFWwindow*() const { return glfwWindow; }
125
126 private:
127 // since Loggable is a template base class, the compiler does not see Logger::logger in the
128 // current scope; so as not to use this->logger explicitly each time, the below brings it to
129 // the current scope explicitly
131
132 GLFWwindow* glfwWindow{};
133 inline static bool initializedGLFW;
134
135 std::unique_ptr<RendererImpl> renderer;
136
140 static void initGLFW() {
141 if (!initializedGLFW) {
142 std::cout << "Initializing GLFW" << std::endl;
143
144 if (glfwInit() != 0) {
145 glfwSetErrorCallback([](int error, const char* description) {
146 std::cerr << "GLFW Error " << error << ": " << description << std::endl;
147 });
148
149 std::cout << "Initialized GLFW" << std::endl;
150
151 initializedGLFW = true;
152 } else {
153 throw std::runtime_error("Failed to initialize GLFW");
154 }
155 }
156 };
157
161 bool shouldClose() const override { return glfwWindowShouldClose(glfwWindow) != 0; }
162
169 void initializeGLFWWindow(int width, int height, const char* title) {
170 glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
171 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
172 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
173 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
174 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
175 glfwWindowHint(GLFW_STENCIL_BITS, 0);
176 glfwWindowHint(GLFW_DEPTH_BITS, 0);
177
178 glfwWindow = glfwCreateWindow(width, height, title, nullptr, nullptr);
179
180 logger->info(
181 "Creating window with title: {} and initial size: {}x{}", title, width, height);
182
183 if (glfwWindow == nullptr) {
184 glfwTerminate();
185 throw std::runtime_error("Failed to create GLFW window");
186 }
187
188 glfwMakeContextCurrent(glfwWindow);
189 glfwSwapInterval(1);
190 glfwSetWindowUserPointer(glfwWindow, this);
191 glfwSetWindowSizeCallback(glfwWindow,
192 [](GLFWwindow* glfwWindow, int winWidth, int winHeight) {
193 handleWindowResized(glfwWindow, winWidth, winHeight);
194 });
195
196 glfwSetKeyCallback(glfwWindow,
197 [](GLFWwindow* glfwWindow,
198 int key,
199 [[maybe_unused]] int scancode,
200 int action,
201 [[maybe_unused]] int mods) {
202 if (action == GLFW_PRESS) {
203 if (key >= GLFW_KEY_0 && key <= GLFW_KEY_9) {
204 auto* window = static_cast<WindowBase<Canvas>*>(
205 glfwGetWindowUserPointer(glfwWindow));
206
207 int number = key - GLFW_KEY_0;
208 window->handleNumericKeyPress(number);
209 } else if (key == GLFW_KEY_ESCAPE) {
210 auto* window = static_cast<WindowBase<Canvas>*>(
211 glfwGetWindowUserPointer(glfwWindow));
212
213 window->handleEscapeKeyPress();
214 }
215 }
216 });
217
218 glfwSetMouseButtonCallback(
220 [](GLFWwindow* glfwWindow, int button, int action, [[maybe_unused]] int mods) {
221 if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
222 auto* window =
223 static_cast<WindowBase<Canvas>*>(glfwGetWindowUserPointer(glfwWindow));
224
225 window->handleMouseDown();
226 } else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
227 auto* window =
228 static_cast<WindowBase<Canvas>*>(glfwGetWindowUserPointer(glfwWindow));
229
230 window->handleMouseUp();
231 } else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
232 auto* window =
233 static_cast<WindowBase<Canvas>*>(glfwGetWindowUserPointer(glfwWindow));
234
235 window->handleRightClick();
236 }
237 });
238 glfwSetCursorPosCallback(glfwWindow, [](GLFWwindow* glfwWindow, double x, double y) {
239 auto* window =
240 static_cast<WindowBase<Canvas>*>(glfwGetWindowUserPointer(glfwWindow));
241
242 window->handleMouseMove(x, y);
243 });
244
245 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
246 int fbWidth;
247 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
248 int fbHeight;
249 glfwGetFramebufferSize(glfwWindow, &fbWidth, &fbHeight);
250
251 renderer = std::make_unique<RendererImpl>(this, this->blocksManager);
252
253 handleWindowResized(glfwWindow, width, height);
254 }
255
262 static void handleWindowResized(GLFWwindow* window, int winWidth, int winHeight) {
263 auto* self = static_cast<GLFWQtWindowImpl*>(glfwGetWindowUserPointer(window));
264
265 if (self && self->renderer) {
266 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
267 int fbWidth;
268 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
269 int fbHeight;
270 glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
271
272 self->winSize = {.width = winWidth, .height = winHeight};
273 self->framebufferSize = {.width = fbWidth, .height = fbHeight};
274
275 float xScale = static_cast<float>(fbWidth) / static_cast<float>(winWidth);
276 float yScale = static_cast<float>(fbHeight) / static_cast<float>(winHeight);
277
278 self->logger->info(
279 "Informing renderer {} of window size change to {}x{}, framebuffer size to "
280 "{}x{} "
281 "and scales to {:.2f}x{:.2f}",
282 business_logic::stringifyAddressOf(self->renderer.get()),
283 winWidth,
284 winHeight,
285 fbWidth,
286 fbHeight,
287 xScale,
288 yScale);
289
290 self->renderer->handleWindowResized(self, xScale, yScale);
291 }
292 }
293
297 void focusWindow() override { glfwFocusWindow(glfwWindow); }
298
302 void showWarning(const std::string& title, const std::string& message) override {
304 }
305
309 std::optional<FloatingPoint> promptFloatingPointInput(
310 const std::string& title,
311 const std::string& message,
312 const std::optional<FloatingPoint>& defaultValue) override {
314 title, message, defaultValue, this);
315 }
316
320 bool promptConfirmation(const std::string& title, const std::string& message) override {
322 }
323 };
324} // namespace gui::window
325
326#endif // WINDOW_H
Class that provides a logger for the given class; automatically deduces the logger.
Definition Loggable.h:37
Class responsible for managing the GLFW main window, any Qt dialogs / modals and their lifecycle.
static GLFWQtWindowImpl< RendererImpl, Canvas > MakeFullscreen(const char *title)
Constructs a new GLFWQtWindowImpl that takes the full size of the primary monitor.
GLFWQtWindowImpl & operator=(const GLFWQtWindowImpl &)=delete
void run() override
Runs the main window loop.
GLFWQtWindowImpl & operator=(GLFWQtWindowImpl &&)=delete
void showWarning(const std::string &title, const std::string &message) override
Show a warning message.
std::optional< FloatingPoint > promptFloatingPointInput(const std::string &title, const std::string &message, const std::optional< FloatingPoint > &defaultValue) override
Prompt for a floating point input.
static void initGLFW()
Initializes GLFW.
std::unique_ptr< RendererImpl > renderer
bool shouldClose() const override
Whether the window should close.
bool promptConfirmation(const std::string &title, const std::string &message) override
Prompt for user confirmation.
GLFWQtWindowImpl(GLFWQtWindowImpl &&)=delete
void initializeGLFWWindow(int width, int height, const char *title)
Initializes the GLFW window.
GLFWQtWindowImpl(const GLFWQtWindowImpl &)=delete
void focusWindow() override
Focus the window.
static void handleWindowResized(GLFWwindow *window, int winWidth, int winHeight)
Handles the window resized event.
GLFWQtWindowImpl(int width, int height, const char *title)
Constructs a new GLFWQtWindowImpl.
~GLFWQtWindowImpl() override
Destructor that cleans up the GLFW window and terminates GLFW.
The abstract base class for implementing a window.
Definition WindowBase.h:27
void handleNumericKeyPress(int number)
Handles the numeric key press event; should be called internally by the window implementation.
Definition WindowBase.h:72
void handleRightClick()
Handles the right click event; should be called internally by the window implementation.
Definition WindowBase.h:84
std::shared_ptr< spdlog::logger > logger
Definition WindowBase.h:152
void handleMouseUp()
Handles the mouse up event; should be called internally by the window implementation.
Definition WindowBase.h:66
std::shared_ptr< business_logic::BlocksManager > blocksManager
Definition WindowBase.h:147
void handleMouseDown()
Handles the mouse down event; should be called internally by the window implementation.
Definition WindowBase.h:60
void handleMouseMove(int x, int y)
Handles the mouse move event; should be called internally by the window implementation.
Definition WindowBase.h:131
void handleEscapeKeyPress()
Handles the ESC key press event; should be called internally by the window implementation.
Definition WindowBase.h:78
static bool promptConfirmation(const std::string &title, const std::string &message, business_logic::delegate::IWindowDelegate *windowDelegate)
static void showWarning(const std::string &title, const std::string &message, business_logic::delegate::IWindowDelegate *windowDelegate)
static std::optional< FloatingPoint > promptForFloatingPointInput(const std::string &title, const std::string &prompt, const std::optional< FloatingPoint > &defaultValue, business_logic::delegate::IWindowDelegate *windowDelegate)
The business logic module.
std::string stringifyAddressOf(const T *value)
Convert a pointer to a string in format "0x..." containing its hexadecimal address.
Definition misc.h:16
The GUI window abstraction & implementation.