I'm writing a very simple engine which I hope to use to create a game at some point and felt that I needed buttons to allow the user to select options in a menu, so I wrote up a rudimentary button class. Think of this as similar to Windows forms if you like.
Whilst what I've produced works and I can detect the button presses I feel like this is not the simplest way to do it (although note that I would like to do it myself as a learning exercise rather than using an existing library to produce the buttons). One of my biggest problems is that the buttons are done using GLFW
rather than GL
and so have a top left anchor and working based on window width and height in pixels rather than a center anchor ranging from -1 to 1, needless to say this makes rendering buttons a chore. Any advice on how to improve that alongside general scrutiny would be much obliged.
display.h
#ifndef DISPLAY_H
#define DISPLAY_H
// GL Includes
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// Other Includes
#include <iostream>
#include "button.h"
#include <vector>
class Display {
public:
Display(GLuint width, GLuint height, const char* title, GLFWkeyfun keycallback, GLFWmousebuttonfun mousecallback);
~Display();
bool IsClosed();
void Update(bool draw, bool pollevents);
void Clear(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
void AddButton(Button b);
void CheckButtons(double x, double y);
private:
GLFWwindow* window;
std::vector<Button> buttons;
bool isClosed;
};
#endif
display.cpp
#include "display.h"
Display::Display(GLuint width, GLuint height, const char* title, GLFWkeyfun keycallback, GLFWmousebuttonfun mousecallback) {
// Initialize GLFW and setup window config
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
// Create a GLFWwindow
window = glfwCreateWindow(width, height, title, nullptr, nullptr);
if (!window) {
std::cout << "Could not initialize window.";
std::cin.get();
exit(1);
}
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, keycallback);
glfwSetMouseButtonCallback(window, mousecallback);
// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
glewExperimental = GL_TRUE;
// Initialize GLEW
glewInit();
// Set Viewport to fill window
glViewport(0, 0, width, height);
isClosed = false;
}
Display::~Display() {
glfwTerminate();
}
bool Display::IsClosed() {
return isClosed;
}
void Display::Update(bool draw, bool pollevents) {
// Update Display
if (draw) {
glfwSwapBuffers(window);
}
// Check for key press etc.
if (pollevents) {
glfwPollEvents();
}
// Exit
if (glfwWindowShouldClose(window)) {
isClosed = true;
}
}
void Display::Clear(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT);
}
void Display::AddButton(Button b) {
buttons.push_back(b);
}
void Display::CheckButtons(double x, double y) {
for (int i = 0; i < buttons.size(); i++) {
buttons[i].CheckClick(x, y);
}
}
button.h
#ifndef BUTTON_H
#define BUTTON_H
// GL Includes
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
class Button {
public:
Button(double top, double left, double width, double height, void(*f)(void));
~Button();
void CheckClick(double x, double y);
private:
double w;
double h;
double t;
double l;
double b;
void* onClick;
};
#endif
button.cpp
#include "button.h"
Button::Button(double top, double left, double width, double height, void(*f)(void)) {
t = top;
l = left;
w = width;
h = height;
onClick = f;
}
Button::~Button() {
}
void Button::CheckClick(double x, double y) {
if (x >= l && x <= l + w && y >= t && y <= t + h) {
((void(*)())onClick)();
}
}
main.cpp
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// Other includes
#include "shader.h"
#include "display.h"
#include "mesh.h"
#include "button.h"
#include <iostream>
// Consts
const GLuint WIDTH = 300, HEIGHT = 300;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, int button, int action, int modifier);
void testbutton1();
void testbutton2();
void testbutton3();
Display display(WIDTH, HEIGHT, "OpenGL", key_callback, mouse_callback);
// The MAIN function, from here we start the application and run the game loop
int main()
{
Button button1(0, 125, 50, 50, testbutton1);
Button button2(250, 0, 50, 50, testbutton2);
Button button3(250, 250, 50, 50, testbutton3);
display.AddButton(button1);
display.AddButton(button2);
display.AddButton(button3);
// Game loop
while (!display.IsClosed())
{
display.Clear(0.2f, 0.3f, 0.3f, 1.0f);
//drawing code omitted
display.Update(true, true);
}
return 0;
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
// Is called whenever a mouse button is pressed/released via GLFW
void mouse_callback(GLFWwindow* window, int button, int action, int modifier)
{
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_1) {
display.CheckButtons(xpos, ypos);
}
}
void testbutton1() {
//omitted
}
void testbutton2() {
//omitted
}
void testbutton3() {
//omitted
}