diff --git a/client/include/ui.h b/client/include/ui.h index 9a41a08..74f8ca9 100644 --- a/client/include/ui.h +++ b/client/include/ui.h @@ -145,15 +145,19 @@ typedef struct GPUContainerStruct { typedef struct UIContextStruct UIContext; -typedef bool (*ui_key_callback)(UIContext* ui, RenderContext* gpu, int key, int action, int mods); -typedef void (*ui_button_callback)(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods); -typedef void (*ui_scroll_callback)(UIContext* ui, RenderContext* gpu, double x, double y); -typedef void (*ui_cursor_callback)(UIContext* ui, RenderContext* gpu, float x, float y); -typedef void (*ui_deselect_callback)(UIContext* ui, RenderContext* gpu); +typedef void (*ui_text_callback)(void* data, UIContext* ui, RenderContext* gpu, unsigned int codepoint); +typedef bool (*ui_key_callback)(void* data, UIContext* ui, RenderContext* gpu, int key, int action, int mods); +typedef void (*ui_button_callback)(void* data, UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods); +typedef void (*ui_scroll_callback)(void* data, UIContext* ui, RenderContext* gpu, double x, double y); +typedef void (*ui_cursor_callback)(void* data, UIContext* ui, RenderContext* gpu, float x, float y); +typedef void (*ui_deselect_callback)(void* data, UIContext* ui, RenderContext* gpu); typedef struct UICallbacksStruct { - uint32_t layer; - uint32_t element; + uint32_t layer; + uint32_t element; + void* data; + + ui_text_callback text; ui_key_callback key; ui_button_callback button; ui_scroll_callback scroll; diff --git a/client/src/main.c b/client/src/main.c index 2545838..cdbaf4b 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -92,16 +92,26 @@ uint32_t add_hex_region(ClientContext* context) { } #define COLOR_PICK_CONTAINER_ID 0x03 +typedef struct ColorUIDataStruct { + vec3 current; + vec3 saved[12]; + char string[8]; + int string_len; +} ColorUIData; + +float floatmod(float a, float b) { + return a - b*floor(a/b); +} void hsv_to_rgb(vec3 hsv, vec3 rgb) { float C = hsv[1] * hsv[2]; float H = hsv[0]*6; - float X = C * (1 - fabs((H - 2 * floor( H / 2 )) - 1)); + float X = C * (1 - fabs(floatmod(H, 2) - 1)); vec4 temp = {0, 0, 0, 0}; if(0 <= H && H <= 1) { temp[0] = C; temp[1] = X; - } else if(1 <= H && H <= 2) { + } else if(1 <= H && H <= 2) { temp[0] = X; temp[1] = C; } else if(2 <= H && H <= 3) { @@ -124,24 +134,27 @@ void hsv_to_rgb(vec3 hsv, vec3 rgb) { rgb[2] = temp[2] + m; } -void rgb_string_set(UIContext* ui, RenderContext* gpu, vec3 hsv) { +void rgb_string_set(UIContext* ui, RenderContext* gpu, ColorUIData* data, vec3 hsv) { vec3 rgb; hsv_to_rgb(hsv, rgb); - char temp[10]; - snprintf(temp, 10, "#%02x%02x%02x", + snprintf(data->string, 8, "#%02X%02X%02X", (uint)(rgb[0]*255), (uint)(rgb[1]*255), (uint)(rgb[2]*255)); - update_ui_string(temp, COLOR_PICK_CONTAINER_ID, 0, 0, ui, gpu); + data->string_len = 6; + update_ui_string(data->string, COLOR_PICK_CONTAINER_ID, 0, 0, ui, gpu); } -void sv_square_pick(UIContext* ui, RenderContext* gpu, float s, float v) { +void sv_square_pick(UIContext* ui, RenderContext* gpu, ColorUIData* data, float s, float v) { if(s < 0) s = 0; if(s > 1) s = 1; if(v < 0) v = 0; if(v > 1) v = 1; + data->current[1] = s; + data->current[2] = 1-v; + Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui); Layer* layer = &container->layers[0]; GPUDrawable* select_outline = &layer->drawables_buffer[3]; @@ -186,47 +199,52 @@ void sv_square_pick(UIContext* ui, RenderContext* gpu, float s, float v) { 4*sizeof(vec4), gpu); - rgb_string_set(ui, gpu, select->color[0]); + rgb_string_set(ui, gpu, data, select->color[0]); } -void sv_square_button_callback(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) { +void sv_square_button_callback(void* data, UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) { (void)mods; (void)x; if(action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) { set_active_element(COLOR_PICK_CONTAINER_ID, 0, 1, ui); - sv_square_pick(ui, gpu, x, y); + sv_square_pick(ui, gpu, data, x, y); } else if(action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) { clear_active_element(ui, gpu); } } -void sv_square_cursor_callback(UIContext* ui, RenderContext* gpu, float x, float y) { +void sv_square_cursor_callback(void* data, UIContext* ui, RenderContext* gpu, float x, float y) { if(ui->active_element == 1 && ui->active_layer == 0 && ui->active_container == COLOR_PICK_CONTAINER_ID) { - sv_square_pick(ui, gpu, x, y); + sv_square_pick(ui, gpu, data, x, y); } } -void hue_bar_set(UIContext* ui, RenderContext* gpu, float y) { +void hue_bar_set(UIContext* ui, RenderContext* gpu, ColorUIData* data, float y) { if(y < 0) y = 0; if(y > 1) y = 1; + data->current[0] = y; + Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui); Layer* layer = &container->layers[0]; GPUDrawable* sv_square = &layer->drawables_buffer[1]; - GPUDrawable* select = &layer->drawables_buffer[4]; + GPUDrawable* sv_select = &layer->drawables_buffer[4]; + GPUDrawable* hue_select = &layer->drawables_buffer[6]; sv_square->color[0][0] = y; sv_square->color[1][0] = y; sv_square->color[2][0] = y; sv_square->color[3][0] = y; - select->color[0][0] = y; - select->color[1][0] = y; - select->color[2][0] = y; - select->color[3][0] = y; + sv_select->color[0][0] = y; + sv_select->color[1][0] = y; + sv_select->color[2][0] = y; + sv_select->color[3][0] = y; + + hue_select->pos[1] = 2 + y*130; add_transfers( &sv_square->color[0], @@ -236,39 +254,46 @@ void hue_bar_set(UIContext* ui, RenderContext* gpu, float y) { gpu); add_transfers( - &select->color[0], + &sv_select->color[0], layer->drawables, 4*sizeof(GPUDrawable) + offsetof(GPUDrawable, color), 4*sizeof(vec4), gpu); - rgb_string_set(ui, gpu, select->color[0]); + add_transfers( + &hue_select->pos[1], + layer->drawables, + 6*sizeof(GPUDrawable) + offsetof(GPUDrawable, pos) + sizeof(float), + 1*sizeof(float), + gpu); + + rgb_string_set(ui, gpu, data, sv_select->color[0]); } -void hue_bar_scroll_callback(UIContext* ui, RenderContext* gpu, double x, double y) { +void hue_bar_scroll_callback(void* data, UIContext* ui, RenderContext* gpu, double x, double y) { (void)x; Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui); - hue_bar_set(ui, gpu, y*0.01 + container->layers[0].drawables_buffer[1].color[0][0]); + hue_bar_set(ui, gpu, data, y*0.01 + container->layers[0].drawables_buffer[1].color[0][0]); } -void hue_bar_cursor_callback(UIContext* ui, RenderContext* gpu, float x, float y) { +void hue_bar_cursor_callback(void*data, UIContext* ui, RenderContext* gpu, float x, float y) { (void)x; if(ui->active_element == 2 && ui->active_layer == 0 && ui->active_container == COLOR_PICK_CONTAINER_ID) { - hue_bar_set(ui, gpu, y); + hue_bar_set(ui, gpu, data, y); } } -void hue_bar_button_callback(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) { +void hue_bar_button_callback(void* data, UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) { (void)mods; (void)x; if(action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) { set_active_element(COLOR_PICK_CONTAINER_ID, 0, 2, ui); - hue_bar_set(ui, gpu, y); + hue_bar_set(ui, gpu, data, y); } else if(action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) { clear_active_element(ui, gpu); } @@ -296,18 +321,107 @@ void hex_string_set_color(UIContext* ui, RenderContext* gpu, float color) { gpu); } -bool hex_string_key_callback(UIContext* ui, RenderContext* gpu, int key, int action, int mods) { +void hex_string_text_callback(void* ptr, UIContext* ui, RenderContext* gpu, unsigned int codepoint) { + ColorUIData* data = ptr; + + if(codepoint >= 'a' && codepoint <= 'f') { + codepoint += 'A' - 'a'; + } else if(!((codepoint >= 'A' && codepoint <= 'F') || (codepoint >= '0' && codepoint <= '9'))) { + return; + } + + if(data->string_len < 6) { + data->string_len += 1; + data->string[data->string_len] = codepoint; + update_ui_string(data->string, COLOR_PICK_CONTAINER_ID, 0, 0, ui, gpu); + } +} + +void rgb_to_hsv(vec3 rgb, vec3 hsv) { + hsv[2] = max(rgb[0], max(rgb[1], rgb[2])); + float M = min(rgb[0], min(rgb[1], rgb[2])); + float C = hsv[2] - M; + hsv[1] = C / hsv[2]; + float X; + + if(hsv[2] == rgb[0] && M == rgb[2]) { + X = rgb[1] - M; + hsv[0] = (X/C + 0)/6; + // H = [0, 1] + } else if(hsv[2] == rgb[1] && M == rgb[2]) { + X = 1 - rgb[0] - M; + hsv[0] = (X/C + 1)/6; + // H = [1, 2] + } else if(hsv[2] == rgb[1] && M == rgb[0]) { + X = rgb[2] - M; + hsv[0] = (X/C + 2)/6; + // H = [2, 3] + } else if(hsv[2] == rgb[2] && M == rgb[0]) { + X = 1 - rgb[1] - M; + hsv[0] = (X/C + 3)/6; + // H = [3, 4] + } else if(hsv[2] == rgb[2] && M == rgb[1]) { + X = rgb[0] - M; + hsv[0] = (X/C + 4)/6; + // H = [4, 5] + } else if(hsv[2] == rgb[0] && M == rgb[1]) { + X = 1 - rgb[2] - M; + hsv[0] = (X/C + 5)/6; + // H = [5, 6] + } +} + +bool hex_string_key_callback(void* ptr, UIContext* ui, RenderContext* gpu, int key, int action, int mods) { (void)mods; + ColorUIData* data = ptr; + char tmp[3]; + vec3 rgb; - if(action == GLFW_PRESS && key == GLFW_KEY_ESCAPE) { - clear_active_element(ui, gpu); - return true; + if(action == GLFW_PRESS) { + switch(key) { + case GLFW_KEY_ESCAPE: + rgb_string_set(ui, gpu, data, data->current); + clear_active_element(ui, gpu); + break; + case GLFW_KEY_ENTER: + // TODO: validate hex string and reset or set hsv + tmp[0] = data->string[1]; + tmp[1] = data->string[2]; + tmp[2] = 0; + rgb[0] = strtol(tmp, NULL, 16)/255.0; + + tmp[0] = data->string[3]; + tmp[1] = data->string[4]; + tmp[2] = 0; + rgb[1] = strtol(tmp, NULL, 16)/255.0; + + tmp[0] = data->string[5]; + tmp[1] = data->string[6]; + tmp[2] = 0; + rgb[2] = strtol(tmp, NULL, 16)/255.0; + + rgb_to_hsv(rgb, data->current); + + hue_bar_set(ui, gpu, data, data->current[0]); + sv_square_pick(ui, gpu, data, data->current[1], 1-data->current[2]); + + clear_active_element(ui, gpu); + break; + case GLFW_KEY_BACKSPACE: + if(data->string_len > 0) { + data->string[data->string_len] = '\0'; + data->string_len -= 1; + update_ui_string(data->string, COLOR_PICK_CONTAINER_ID, 0, 0, ui, gpu); + } + break; + } } - return false; + return true; } -void hex_string_button_callback(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) { +void hex_string_button_callback(void* data, UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) { + (void)data; (void)mods; (void)x; (void)y; @@ -318,7 +432,7 @@ void hex_string_button_callback(UIContext* ui, RenderContext* gpu, float x, floa } } -void hex_string_deselect_callback(UIContext* ui, RenderContext* gpu) { +void hex_string_deselect_callback(void* data, UIContext* ui, RenderContext* gpu) { hex_string_set_color(ui, gpu, 0); } @@ -329,7 +443,7 @@ VkResult color_ui(ClientContext* context) { .color = {1, 1, 1, 1}, .size = 16, .offset = 0, - .length = 9, + .length = 7, .font = 0, }, }; @@ -355,13 +469,13 @@ VkResult color_ui(ClientContext* context) { .events = UI_EVENT_BUTTON | UI_EVENT_SCROLL, }, { - .pos = {9, 9}, + .pos = {130-4, 130-4}, .size = {7, 7}, .color = {{0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}}, }, { .type = DRAWABLE_TYPE_RECT_HSV, - .pos = {10, 10}, + .pos = {130-3, 130-3}, .size = {5, 5}, .color = {{1, 0, 0, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}}, }, @@ -370,6 +484,11 @@ VkResult color_ui(ClientContext* context) { .size = {95, 15}, .events = UI_EVENT_BUTTON | UI_EVENT_CURSOR, }, + { + .pos = {134, 2}, + .size = {10, 1}, + .color = {{0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}}, + }, { .pos = {146, 2}, .size = {20, 20}, @@ -445,16 +564,32 @@ VkResult color_ui(ClientContext* context) { .num_drawables = sizeof(drawables)/sizeof(GPUDrawable), }; + ColorUIData* data = malloc(sizeof(ColorUIData)); + data->string_len = 6; + data->string[0] = '#'; + data->string[1] = '0'; + data->string[2] = '0'; + data->string[3] = '0'; + data->string[4] = '0'; + data->string[5] = '0'; + data->string[6] = '0'; + data->string[7] = '\0'; + data->current[0] = 0; + data->current[1] = 0; + data->current[2] = 0; + UICallbacks callbacks[] = { { .layer = 0, .element = 1, + .data = data, .button = sv_square_button_callback, .cursor = sv_square_cursor_callback, }, { .layer = 0, .element = 2, + .data = data, .button = hue_bar_button_callback, .cursor = hue_bar_cursor_callback, .scroll = hue_bar_scroll_callback, @@ -462,8 +597,10 @@ VkResult color_ui(ClientContext* context) { { .layer = 0, .element = 5, + .data = data, .button = hex_string_button_callback, .key = hex_string_key_callback, + .text = hex_string_text_callback, .deselect = hex_string_deselect_callback, }, }; @@ -931,13 +1068,30 @@ VkResult main_thread(ClientContext* context) { return 0; } +void text_callback(GLFWwindow* window, unsigned int codepoint) { + ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); + if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->text != NULL) { + context->ui->active_callbacks->text( + context->ui->active_callbacks->data, + context->ui, + context->render, + codepoint); + } +} + void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { (void)scancode; (void)mods; ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->key != NULL) { - if(context->ui->active_callbacks->key(context->ui, context->render, key, action, mods)) { + if(context->ui->active_callbacks->key( + context->ui->active_callbacks->data, + context->ui, + context->render, + key, + action, + mods)) { return; } } @@ -1202,8 +1356,9 @@ void button_callback(GLFWwindow* window, int button, int action, int mods) { (cursor[1] - element_pos[1])/element_size[1], }; - if(point[0] <= 1 && point[0] >= 0 && point[1] <= 1 && point[1] >= 0) { + if((point[0] <= 1 && point[0] >= 0 && point[1] <= 1 && point[1] >= 0) || action == GLFW_RELEASE) { context->ui->active_callbacks->button( + context->ui->active_callbacks->data, context->ui, context->render, point[0], @@ -1222,7 +1377,15 @@ void button_callback(GLFWwindow* window, int button, int action, int mods) { for(uint32_t c = 0; c < container_ptr->callback_count; c++) { if(container_ptr->callbacks[c].element == element) { if(container_ptr->callbacks[c].button != NULL) { - container_ptr->callbacks[c].button(context->ui, context->render, position[0], position[1], button, action, mods); + container_ptr->callbacks[c].button( + container_ptr->callbacks[c].data, + context->ui, + context->render, + position[0], + position[1], + button, + action, + mods); } break; } @@ -1251,7 +1414,12 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->scroll != NULL) { - context->ui->active_callbacks->scroll(context->ui, context->render, xoffset, yoffset); + context->ui->active_callbacks->scroll( + context->ui->active_callbacks->data, + context->ui, + context->render, + xoffset, + yoffset); } else if(ui_intersect( context->ui->cursor, context->render, @@ -1265,7 +1433,12 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { for(uint32_t c = 0; c < container_ptr->callback_count; c++) { if(container_ptr->callbacks[c].element == element) { if(container_ptr->callbacks[c].scroll != NULL) { - container_ptr->callbacks[c].scroll(context->ui, context->render, xoffset, yoffset); + container_ptr->callbacks[c].scroll( + container_ptr->callbacks[c].data, + context->ui, + context->render, + xoffset, + yoffset); } break; } @@ -1306,6 +1479,7 @@ void cursor_callback(GLFWwindow* window, double xpos, double ypos) { drawable_ptr->size[1], }; context->ui->active_callbacks->cursor( + context->ui->active_callbacks->data, context->ui, context->render, (context->ui->cursor[0] - element_pos[0])/element_size[0], @@ -1315,7 +1489,12 @@ void cursor_callback(GLFWwindow* window, double xpos, double ypos) { for(uint32_t c = 0; c < container_ptr->callback_count; c++) { if(container_ptr->callbacks[c].element == element) { if(container_ptr->callbacks[c].cursor != NULL) { - container_ptr->callbacks[c].cursor(context->ui, context->render, position[0], position[1]); + container_ptr->callbacks[c].cursor( + container_ptr->callbacks[c].data, + context->ui, + context->render, + position[0], + position[1]); } break; } @@ -1361,6 +1540,7 @@ int main() { glfwSetMouseButtonCallback(context.window, button_callback); glfwSetScrollCallback(context.window, scroll_callback); glfwSetCursorPosCallback(context.window, cursor_callback); + glfwSetCharCallback(context.window, text_callback); int error; VkResult result; diff --git a/client/src/ui.c b/client/src/ui.c index 1edab5b..a034434 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -1268,7 +1268,7 @@ bool ui_intersect( void clear_active_element(UIContext* ui, RenderContext* gpu) { if(ui->active_callbacks != NULL && ui->active_callbacks->deselect != NULL) { - ui->active_callbacks->deselect(ui, gpu); + ui->active_callbacks->deselect(ui->active_callbacks->data, ui, gpu); } ui->active_callbacks = NULL; ui->active_element = 0;