feat (wip): gui

This commit is contained in:
Djairo Hougee 2025-01-29 02:40:38 +01:00
parent aef7e34a6c
commit 3e1ee817a9
15 changed files with 589 additions and 0 deletions

0
src/core/CLI.cpp Normal file
View File

0
src/core/CLI.h Normal file
View File

25
src/core/CLIParser.cpp Normal file
View File

@ -0,0 +1,25 @@
// #include <cstddef>
// #include <getopt.h>
// int main(int argc, char* argv[]) {
// // CLI parsing code. TODO: move to core somewhere
// option longopts[] = {
// {"number", optional_argument, NULL, 'n'},
// {"show-ends", optional_argument, NULL, 'E'}, {0}};
//
// while (1) {
// const int opt = getopt_long(argc, argv, "nE::", longopts, 0);
//
// if (opt == -1) {
// break;
// }
//
// switch (opt) {
// case 'n':
// // ...
// case 'E':
// // ...
// }
// }
//
// //...
// }

0
src/core/CLIParser.h Normal file
View File

35
src/core/Colour.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "Colour.h"
#include <cmath>
#include <iostream>
#include <cstring>
using namespace core;
Colour::Colour(const float r, const float g, const float b) {
this->len = sqrt(r*r + g*g + b*b);
this->r = r;
this->g = g;
this->b = b;
}
Colour::~Colour() {
}
char* Colour::toString() {
sprintf(this->str, "%3.0f %3.0f %3.0f", round(this->r*255.0f),round(this->g*255.0f),round(this->b*255.0f));
return this->str;
}
float Colour::getLength() const {
return this->len;
}
bool Colour::operator<(const Colour& other) const {
return getLength() < other.getLength();
}

22
src/core/Colour.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <cstdint>
#include <string>
namespace core {
class Colour {
public:
Colour(const float r, const float g, const float b);
~Colour();
char* toString();
float getLength() const;
bool operator<(const Colour& other) const;
float r; // FIXME: should be private
float g;
float b;
private:
char str[128];
float len;
};
}

10
src/core/ImageMatcher.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
class ImageMatcher{
public:
private:
};

0
src/core/RGBMatcher.cpp Normal file
View File

0
src/core/RGBMatcher.h Normal file
View File

300
src/gui/Window.cpp Normal file
View File

@ -0,0 +1,300 @@
#include "imgui.h"
#include "imgui/backends/imgui_impl_glfw.h"
#include "imgui/backends/imgui_impl_opengl3.h"
#include "layouts/SquareLayout.h"
#include <stdio.h>
#include "Window.h"
#include <GLFW/glfw3.h>
#include <iostream>
using namespace Gui;
// This function is called by glfw when the window is resized.
void framebuffer_size_callback(GLFWwindow* window, int w, int h) {
glViewport(0 , 0, w, h);
Window* newWin = reinterpret_cast<Window*>(glfwGetWindowUserPointer(window));
newWin->resize(w, h);
}
Window::Window() {Window(w, h);}
Window::Window(const int w, const int h) : w(w), h(h) {
init();
this->layoutSelected = 0;
this->layouts.push_back(new SquareLayout());
}
Window::~Window() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(this->window);
glfwTerminate();
}
void Window::init() {
glfwInit();
// requesting context version 1.0 makes glfw try to provide the latest version if possible
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
this->window = glfwCreateWindow(this->w, this->h, "C0l0ur M4tch3r", NULL, NULL);
if (this->window == NULL) {
// TODO: error handling/graceful shutdown
std::cout << "Failed to create GLFW Window\n";
glfwTerminate();
return;
}
glfwMakeContextCurrent(this->window);
glfwSetWindowUserPointer(this->window, reinterpret_cast<void*>(this));
glViewport(0, 0, this->w, this->h);
if (glfwSetFramebufferSizeCallback(this->window, framebuffer_size_callback) != 0) {
// TODO: error handling/graceful shutdown
std::cout << "Failed to set resize callback function\n";
return;
}
// ImGui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
this->io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
ImGui::StyleColorsDark();
ImGui_ImplGlfw_InitForOpenGL(this->window, true);
ImGui_ImplOpenGL3_Init();
// TODO: font support
}
int Window::run() {
while (!glfwWindowShouldClose(window)) {
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
{
ImGui_ImplGlfw_Sleep(10);
continue;
}
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
this->layouts[0]->draw(this->w, this->h);
// Rendering
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(this->window);
}
return 0;
}
void Window::resize(const int w, const int h) {
this->w = w;
this->h = h;
}
// #include "imgui.h"
// #include "imgui/backends/imgui_impl_glfw.h"
// #include "imgui/backends/imgui_impl_opengl3.h"
// #include <stdio.h>
//
// #define GL_SILENCE_DEPRECATION
//
// #if defined(IMGUI_IMPL_OPENGL_ES2)
// #include <GLES2/gl2.h>
// #endif
//
// #include <GLFW/glfw3.h> // Will drag system OpenGL headers
//
// static void glfw_error_callback(int error, const char* description)
// {
// fprintf(stderr, "GLFW Error %d: %s\n", error, description);
// }
//
// // Main code
// int main(int, char**)
// {
// glfwSetErrorCallback(glfw_error_callback);
// if (!glfwInit())
// return 1;
//
// // Decide GL+GLSL versions
// #if defined(IMGUI_IMPL_OPENGL_ES2)
// // GL ES 2.0 + GLSL 100 (WebGL 1.0)
// const char* glsl_version = "#version 100";
// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
// glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
// #elif defined(IMGUI_IMPL_OPENGL_ES3)
// // GL ES 3.0 + GLSL 300 es (WebGL 2.0)
// const char* glsl_version = "#version 300 es";
// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
// glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
// #elif defined(__APPLE__)
// // GL 3.2 + GLSL 150
// const char* glsl_version = "#version 150";
// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
// #else
// // GL 3.0 + GLSL 130
// const char* glsl_version = "#version 130";
// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
// //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
// //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
// #endif
//
// // Create window with graphics context
// GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr);
// if (window == nullptr)
// return 1;
// glfwMakeContextCurrent(window);
// glfwSwapInterval(1); // Enable vsync
//
// // Setup Dear ImGui context
// IMGUI_CHECKVERSION();
// ImGui::CreateContext();
// ImGuiIO& io = ImGui::GetIO(); (void)io;
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
//
// // Setup Dear ImGui style
// ImGui::StyleColorsDark();
//
// // Setup Platform/Renderer backends
// ImGui_ImplGlfw_InitForOpenGL(window, true);
// #ifdef __EMSCRIPTEN__
// ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
// #endif
// ImGui_ImplOpenGL3_Init(glsl_version);
//
// // Load Fonts
// // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// // - Read 'docs/FONTS.md' for more instructions and details.
// // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details.
// //io.Fonts->AddFontDefault();
// //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
// //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
// //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
// //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
// //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
// //IM_ASSERT(font != nullptr);
//
// // Our state
// bool show_demo_window = true;
// bool show_another_window = false;
// ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
//
// // Main loop
// while (!glfwWindowShouldClose(window)) {
// // Poll and handle events (inputs, window resize, etc.)
// // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// glfwPollEvents();
// if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
// {
// ImGui_ImplGlfw_Sleep(10);
// continue;
// }
//
// // Start the Dear ImGui frame
// ImGui_ImplOpenGL3_NewFrame();
// ImGui_ImplGlfw_NewFrame();
// ImGui::NewFrame();
//
// // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
// if (show_demo_window)
// ImGui::ShowDemoWindow(&show_demo_window);
//
// // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
// {
// static float f = 0.0f;
// static int counter = 0;
//
// ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
//
// ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
// ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
// ImGui::Checkbox("Another Window", &show_another_window);
//
// ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
// ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
//
// if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
// counter++;
// ImGui::SameLine();
// ImGui::Text("counter = %d", counter);
//
// ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
// ImGui::End();
// }
//
// // 3. Show another simple window.
// if (show_another_window)
// {
// ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
// ImGui::Text("Hello from another window!");
// if (ImGui::Button("Close Me"))
// show_another_window = false;
// ImGui::End();
// }
//
// // Rendering
// ImGui::Render();
// int display_w, display_h;
// glfwGetFramebufferSize(window, &display_w, &display_h);
// glViewport(0, 0, display_w, display_h);
// glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
// glClear(GL_COLOR_BUFFER_BIT);
// ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
//
// glfwSwapBuffers(window);
// }
//
// // Cleanup
// ImGui_ImplOpenGL3_Shutdown();
// ImGui_ImplGlfw_Shutdown();
// ImGui::DestroyContext();
//
// glfwDestroyWindow(window);
// glfwTerminate();
//
// return 0;
// }

28
src/gui/Window.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include "imgui.h"
#include "layouts/Layout.h"
#include <GLFW/glfw3.h>
#include <vector>
namespace Gui {
class Window {
public:
Window();
Window(const int w, const int h);
void resize(const int w, const int h);
~Window();
int run();
private:
int w;
int h;
ImGuiIO io;
GLFWwindow* window;
std::vector<Layout*> layouts;
int layoutSelected;
void init();
void cleanup();
};
}

8
src/gui/layouts/Layout.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
namespace Gui {
class Layout {
public:
virtual void draw(const int w, const int h) = 0;
};
}

View File

@ -0,0 +1,127 @@
#include "SquareLayout.h"
#include "imgui.h"
#include "imgui/backends/imgui_impl_glfw.h"
#include "imgui/backends/imgui_impl_opengl3.h"
#include <iostream>
using namespace Gui;
auto flags = ImGuiWindowFlags_NoTitleBar |ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse;
SquareLayout::SquareLayout() : run(false), refImg("") {
this->selectedColour = new float[3];
this->selectedColour[0] = 0.0f;
this->selectedColour[1] = 1.0f;
this->selectedColour[2] = 2.0f;
// this->selectedColour = (float *)malloc(3*sizeof(float));
}
SquareLayout::~SquareLayout() {
// free(this->selectedColour);
}
bool SquareLayout::shouldRun() {
return this->run;
}
void SquareLayout::draw(const int w, const int h) {
drawSettings(w, h);
drawColours(w, h);
}
void SquareLayout::drawSettings(const int w, const int h) {
ImGui::SetNextWindowPos(ImVec2(0,0));
ImGui::SetNextWindowSize(ImVec2(w/2,h/2));
ImGui::Begin("SettingsPanel", 0, flags);
ImGui::Text("Select reference image");
// TODO: file picker
float wi = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.60f;
ImGui::SetNextItemWidth(wi);
ImGui::ColorPicker3("##MyColor##1", this->selectedColour, ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_DisplayHex);
if (ImGui::Button("Add Colour")) {
auto c3 = this->selectedColour;
this->colours.insert(core::Colour(c3[0], c3[1], c3[2]));
}
ImGui::End();
}
int makeComboBox(std::set<core::Colour> &cols, const char* title, const int wi) {
ImGui::SetNextItemWidth(wi);
static int selected = 0;
int n=0;
std::vector<core::Colour> toErase;
if (ImGui::BeginListBox(title, ImVec2(wi, ImGui::GetContentRegionAvail().y))) {
for (auto it = cols.begin(); it != cols.end(); ++it) {
core::Colour c = *it;
const bool is_selected = (selected == n);
if (ImGui::Selectable(c.toString(), is_selected))
selected = n;
ImGui::SameLine();
ImGui::PushID(n);
if (ImGui::Button("D"))
toErase.push_back(c);
ImGui::PopID();
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected)
ImGui::SetItemDefaultFocus();
n++;
}
for (core::Colour c: toErase) {
cols.erase(c);
}
ImGui::EndListBox();
}
return selected;
}
void SquareLayout::drawColours(const int w, const int h) {
ImGui::SetNextWindowPos(ImVec2(0,h/2));
ImGui::SetNextWindowSize(ImVec2(w/2,h/2));
ImGui::Begin("ColourViewer", 0, flags);
int wi = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.80f;
int selected = makeComboBox(this->colours, "##box1", wi);
wi = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.195f;
ImGui::SameLine();
if (this->colours.size()) {
auto c = *next(this->colours.begin(), selected);
ImGui::ColorButton("##3c", ImVec4(c.r, c.g, c.b, 0.0f), ImGuiColorEditFlags_NoBorder, ImVec2(wi, ImGui::GetContentRegionAvail().y));
} else {
ImGui::ColorButton("##3c", ImVec4(0,0,0,0), ImGuiColorEditFlags_NoBorder, ImVec2(wi, ImGui::GetContentRegionAvail().y));
}
ImGui::End();
ImGui::SetNextWindowPos(ImVec2(w/2,h/2));
ImGui::SetNextWindowSize(ImVec2(w/2,h/2));
ImGui::Begin("ColourViewer##2", 0, flags);
wi = ((ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.20f);
if (this->colours.size()) {
auto c = *next(this->colours.begin(), selected);
ImGui::ColorButton("##3d", ImVec4(c.r, c.g, c.b, 0.0f), ImGuiColorEditFlags_NoBorder, ImVec2(wi, ImGui::GetContentRegionAvail().y));
} else {
ImGui::ColorButton("##3d", ImVec4(0,0,0,0), ImGuiColorEditFlags_NoBorder, ImVec2(wi, ImGui::GetContentRegionAvail().y));
}
ImGui::SameLine();
// TODO: change this->colours to the new (calculated) colours
int newselected = makeComboBox(this->colours, "##box2", ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y);
ImGui::End();
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "../../core/Colour.h"
#include "Layout.h"
#include <set>
#include <string>
#include <vector>
namespace Gui {
class SquareLayout : public Layout {
public:
SquareLayout();
~SquareLayout();
bool shouldRun();
void draw(const int w, const int h);
private:
std::set<core::Colour> colours;
std::string refImg;
float* selectedColour;
bool run;
void drawSettings(const int w, const int h);
void drawColours(const int w, const int h);
};
}

9
src/main.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "gui/Window.h"
int main(int argc, char* argv[]) {
Gui::Window window(800, 600);
int out = window.run();
return out;
}