diff --git a/gui.c b/gui.c index 9877641..4a1ed2d 100644 --- a/gui.c +++ b/gui.c @@ -128,7 +128,7 @@ utf_encode(gui_long u, gui_char *c, gui_size clen) return len; } -struct gui_color +static struct gui_color gui_make_color(gui_byte r, gui_byte g, gui_byte b, gui_byte a) { struct gui_color col; @@ -137,7 +137,7 @@ gui_make_color(gui_byte r, gui_byte g, gui_byte b, gui_byte a) return col; } -struct gui_vec2 +static struct gui_vec2 gui_make_vec2(gui_float x, gui_float y) { struct gui_vec2 vec; @@ -145,41 +145,49 @@ gui_make_vec2(gui_float x, gui_float y) return vec; } -void -gui_default_config(struct gui_config *config) +static void +gui_triangle_from_direction(struct gui_vec2 *result, gui_float x, gui_float y, + gui_float w, gui_float h, gui_float pad_x, gui_float pad_y, + enum gui_direction direction) { - if (!config) return; - config->global_alpha = 1.0f; - config->scrollbar_width = 16; - config->scroll_factor = 2; - config->panel_padding = gui_make_vec2(15.0f, 10.0f); - config->panel_min_size = gui_make_vec2(32.0f, 32.0f); - config->item_spacing = gui_make_vec2(8.0f, 8.0f); - config->item_padding = gui_make_vec2(4.0f, 4.0f); - config->colors[GUI_COLOR_TEXT] = gui_make_color(255, 255, 255, 255); - config->colors[GUI_COLOR_PANEL] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_BORDER] = gui_make_color(0, 0, 0, 255); - config->colors[GUI_COLOR_TITLEBAR] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_BUTTON] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_BUTTON_HOVER] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_BUTTON_BORDER] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_TOGGLE] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_TOGGLE_ACTIVE] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_SCROLL] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_SCROLL_CURSOR] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_SLIDER] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_SLIDER_CURSOR] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_PROGRESS] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_PROGRESS_CURSOR] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_INPUT] = gui_make_color(45, 45, 45, 45); - config->colors[GUI_COLOR_INPUT_BORDER] = gui_make_color(100, 100, 100, 100); - config->colors[GUI_COLOR_HISTO] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_HISTO_BARS] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_HISTO_NEGATIVE] = gui_make_color(255, 255, 255, 255); - config->colors[GUI_COLOR_HISTO_HIGHLIGHT] = gui_make_color(255, 0, 0, 255); - config->colors[GUI_COLOR_PLOT] = gui_make_color(100, 100, 100, 255); - config->colors[GUI_COLOR_PLOT_LINES] = gui_make_color(45, 45, 45, 255); - config->colors[GUI_COLOR_PLOT_HIGHLIGHT] = gui_make_color(255, 0, 0, 255); + gui_float w_half, h_half; + w = MAX(4 * pad_x, w); + h = MAX(4 * pad_y, h); + w = w - 2 * pad_x; + h = h - 2 * pad_y; + w_half = w / 2.0f; + h_half = h / 2.0f; + x = x + pad_x; + y = y + pad_y; + if (direction == GUI_UP) { + result[0].x = x + w_half; + result[0].y = y; + result[1].x = x; + result[1].y = y + h; + result[2].x = x + w; + result[2].y = y + h; + } else if (direction == GUI_RIGHT) { + result[0].x = x; + result[0].y = y; + result[1].x = x; + result[1].y = y + h; + result[2].x = x + w; + result[2].y = y + h_half; + } else if (direction == GUI_DOWN) { + result[0].x = x; + result[0].y = y; + result[1].x = x + w_half; + result[1].y = y + h; + result[2].x = x + w; + result[2].y = y; + } else { + result[0].x = x; + result[0].y = y + h_half; + result[1].x = x + w; + result[1].y = y + h; + result[2].x = x + w; + result[2].y = y; + } } void @@ -261,6 +269,32 @@ gui_font_text_width(const struct gui_font *font, const gui_char *t, gui_size l) return text_width; } +static gui_size +gui_font_chars_in_space(const struct gui_font *font, const gui_char *text, gui_size len, + gui_float space) +{ + gui_size chars = 0; + gui_long unicode; + gui_size text_width = 0; + const struct gui_font_glyph *glyph; + gui_size text_len = 0; + gui_size glyph_len; + + if (!text || !len) return 0; + glyph_len = utf_decode(text, &unicode, len); + while (text_len <= len && glyph_len) { + if (unicode == UTF_INVALID) return 0; + glyph = (unicode < font->glyph_count) ? &font->glyphes[unicode] : font->fallback; + glyph = (glyph->code == 0) ? font->fallback : glyph; + text_width += (gui_size)(glyph->xadvance * font->scale); + if (text_width >= space) break; + glyph_len = utf_decode(text + text_len, &unicode, len - text_len); + text_len += glyph_len; + chars += glyph_len; + } + return chars; +} + void gui_begin(struct gui_draw_buffer *buffer, void *memory, gui_size size) { @@ -297,7 +331,7 @@ gui_end(struct gui_draw_buffer *buffer) } static gui_int -gui_push(struct gui_draw_buffer *buffer, gui_size count, +gui_push_command(struct gui_draw_buffer *buffer, gui_size count, const struct gui_rect *rect, gui_texture tex) { gui_size i; @@ -327,7 +361,7 @@ gui_push(struct gui_draw_buffer *buffer, gui_size count, } static void -gui_vertex(struct gui_draw_buffer *buffer, gui_float x, gui_float y, +gui_push_vertex(struct gui_draw_buffer *buffer, gui_float x, gui_float y, struct gui_color col, gui_float u, gui_float v) { struct gui_vertex *vertex; @@ -362,46 +396,46 @@ gui_vertex_line(struct gui_draw_buffer* buffer, gui_float x0, gui_float y0, vec2_load(hp0, +hn.y, -hn.x); vec2_load(hp1, -hn.y, +hn.x); - gui_vertex(buffer, a.x + hp0.x, a.y + hp0.y, col, 0.0f, 0.0f); - gui_vertex(buffer, b.x + hp0.x, b.y + hp0.y, col, 0.0f, 0.0f); - gui_vertex(buffer, a.x + hp1.x, a.y + hp1.y, col, 0.0f, 0.0f); - gui_vertex(buffer, b.x + hp0.x, b.y + hp0.y, col, 0.0f, 0.0f); - gui_vertex(buffer, b.x + hp1.x, b.y + hp1.y, col, 0.0f, 0.0f); - gui_vertex(buffer, a.x + hp1.x, a.y + hp1.y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, a.x + hp0.x, a.y + hp0.y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, b.x + hp0.x, b.y + hp0.y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, a.x + hp1.x, a.y + hp1.y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, b.x + hp0.x, b.y + hp0.y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, b.x + hp1.x, b.y + hp1.y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, a.x + hp1.x, a.y + hp1.y, col, 0.0f, 0.0f); } static void -gui_line(struct gui_draw_buffer *buffer, gui_float x0, gui_float y0, +gui_draw_line(struct gui_draw_buffer *buffer, gui_float x0, gui_float y0, gui_float x1, gui_float y1, struct gui_color col) { struct gui_draw_command *cmd; if (!buffer) return; if (col.a == 0) return; - if (!gui_push(buffer, 6, &null_rect, null_tex)) return; + if (!gui_push_command(buffer, 6, &null_rect, null_tex)) return; gui_vertex_line(buffer, x0, y0, x1, y1, col); } static void -gui_trianglef(struct gui_draw_buffer *buffer, gui_float x0, gui_float y0, +gui_draw_trianglef(struct gui_draw_buffer *buffer, gui_float x0, gui_float y0, gui_float x1, gui_float y1, gui_float x2, gui_float y2, struct gui_color c) { struct gui_draw_command *cmd; if (!buffer) return; if (c.a == 0) return; - if (!gui_push(buffer, 3, &null_rect, null_tex)) return; - gui_vertex(buffer, x0, y0, c, 0.0f, 0.0f); - gui_vertex(buffer, x1, y1, c, 0.0f, 0.0f); - gui_vertex(buffer, x2, y2, c, 0.0f, 0.0f); + if (!gui_push_command(buffer, 3, &null_rect, null_tex)) return; + gui_push_vertex(buffer, x0, y0, c, 0.0f, 0.0f); + gui_push_vertex(buffer, x1, y1, c, 0.0f, 0.0f); + gui_push_vertex(buffer, x2, y2, c, 0.0f, 0.0f); } static void -gui_rect(struct gui_draw_buffer *buffer, gui_float x, gui_float y, +gui_draw_rect(struct gui_draw_buffer *buffer, gui_float x, gui_float y, gui_float w, gui_float h, struct gui_color col) { struct gui_draw_command *cmd; if (!buffer) return; if (col.a == 0) return; - if (!gui_push(buffer, 6 * 4, &null_rect, null_tex)) return; + if (!gui_push_command(buffer, 6 * 4, &null_rect, null_tex)) return; gui_vertex_line(buffer, x, y, x + w, y, col); gui_vertex_line(buffer, x + w, y, x + w, y + h, col); gui_vertex_line(buffer, x + w, y + h, x, y + h, col); @@ -409,28 +443,28 @@ gui_rect(struct gui_draw_buffer *buffer, gui_float x, gui_float y, } static void -gui_rectf(struct gui_draw_buffer *buffer, gui_float x, gui_float y, +gui_draw_rectf(struct gui_draw_buffer *buffer, gui_float x, gui_float y, gui_float w, gui_float h, struct gui_color col) { struct gui_draw_command *cmd; if (!buffer) return; if (col.a == 0) return; - if (!gui_push(buffer, 6, &null_rect, null_tex)) return; + if (!gui_push_command(buffer, 6, &null_rect, null_tex)) + return; - gui_vertex(buffer, x, y, col, 0.0f, 0.0f); - gui_vertex(buffer, x + w, y, col, 0.0f, 0.0f); - gui_vertex(buffer, x + w, y + h, col, 0.0f, 0.0f); - gui_vertex(buffer, x, y, col, 0.0f, 0.0f); - gui_vertex(buffer, x + w, y + h, col, 0.0f, 0.0f); - gui_vertex(buffer, x, y + h, col, 0.0f, 0.0f); + gui_push_vertex(buffer, x, y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, x + w, y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, x + w, y + h, col, 0.0f, 0.0f); + gui_push_vertex(buffer, x, y, col, 0.0f, 0.0f); + gui_push_vertex(buffer, x + w, y + h, col, 0.0f, 0.0f); + gui_push_vertex(buffer, x, y + h, col, 0.0f, 0.0f); } static void -gui_string(struct gui_draw_buffer *buffer, const struct gui_font *font, gui_float x, +gui_draw_string(struct gui_draw_buffer *buffer, const struct gui_font *font, gui_float x, gui_float y, gui_float w, gui_float h, struct gui_color col, const gui_char *t, gui_size len) { - struct gui_draw_command *cmd; struct gui_rect clip; gui_size text_len; gui_long unicode; @@ -439,7 +473,8 @@ gui_string(struct gui_draw_buffer *buffer, const struct gui_font *font, gui_floa if (!buffer || !t || !font || !len) return; clip.x = x; clip.y = y; clip.w = w; clip.h = h; - if (!gui_push(buffer, 6 * len, &clip, font->texture)) return; + if (!gui_push_command(buffer, 6 * len, &clip, font->texture)) + return; text_len = utf_decode(t, &unicode, len); while (text_len <= len) { @@ -456,206 +491,321 @@ gui_string(struct gui_draw_buffer *buffer, const struct gui_font *font, gui_floa x2 = (gui_float)(x1 + (gui_float)g->width * font->scale); char_width = g->xadvance * font->scale; - gui_vertex(buffer, x1, y1, col, g->uv[0].u, g->uv[0].v); - gui_vertex(buffer, x2, y1, col, g->uv[1].u, g->uv[0].v); - gui_vertex(buffer, x2, y2, col, g->uv[1].u, g->uv[1].v); - gui_vertex(buffer, x1, y1, col, g->uv[0].u, g->uv[0].v); - gui_vertex(buffer, x2, y2, col, g->uv[1].u, g->uv[1].v); - gui_vertex(buffer, x1, y2, col, g->uv[0].u, g->uv[1].v); + gui_push_vertex(buffer, x1, y1, col, g->uv[0].u, g->uv[0].v); + gui_push_vertex(buffer, x2, y1, col, g->uv[1].u, g->uv[0].v); + gui_push_vertex(buffer, x2, y2, col, g->uv[1].u, g->uv[1].v); + gui_push_vertex(buffer, x1, y1, col, g->uv[0].u, g->uv[0].v); + gui_push_vertex(buffer, x2, y2, col, g->uv[1].u, g->uv[1].v); + gui_push_vertex(buffer, x1, y2, col, g->uv[0].u, g->uv[1].v); text_len += utf_decode(t + text_len, &unicode, len - text_len); x += char_width; } } -gui_int -gui_button(struct gui_draw_buffer *buffer, const struct gui_button *button, - const struct gui_font *font, const struct gui_input *in) +static void +gui_draw_image(struct gui_draw_buffer *buffer, gui_float x, gui_float y, + gui_float w, gui_float h, gui_texture texture, struct gui_texCoord from, + struct gui_texCoord to) { - gui_size text_width; - gui_float label_x, label_y; - gui_float label_w, label_h; - gui_int ret = gui_false; - gui_float mouse_x, mouse_y; - gui_float clicked_x, clicked_y; - struct gui_color fc, bg, hc; - gui_float x, y, w, h; - const gui_char *t; + const struct gui_color col = {0,0,0,0}; + struct gui_rect clip; + clip.x = x; clip.y = y; + clip.w = w; clip.h = h; + if (!gui_push_command(buffer, 6, &clip, texture)) + return; + gui_push_vertex(buffer, x, y, col, from.u, from.v); + gui_push_vertex(buffer, x + w, y, col, to.u, from.v); + gui_push_vertex(buffer, x + w, y + h, col, to.u, to.v); + gui_push_vertex(buffer, x, y, col, from.u, to.v); + gui_push_vertex(buffer, x + w, y + h, col, to.u, to.v); + gui_push_vertex(buffer, x, y + h, col, from.u, to.v); +} + +void +gui_text(struct gui_draw_buffer *buffer, const struct gui_text *text, + const struct gui_font *font) +{ + gui_float label_x; + gui_float label_y; + gui_float label_w; + gui_float label_h; + if (!buffer || !text || !font) + return; + + label_x = text->x + text->pad_x; + label_y = text->y + text->pad_y; + label_w = MAX(0, text->w - 2 * text->pad_x); + label_h = MAX(0, text->h - 2 * text->pad_y); + gui_draw_rectf(buffer, text->x, text->y, text->w, text->h, text->background); + gui_draw_string(buffer, font, label_x, label_y, label_w, label_h, + text->font, (const gui_char*)text->text, text->length); +} + +gui_size +gui_text_wrap(struct gui_draw_buffer *buffer, const struct gui_text *text, + const struct gui_font *font) +{ + gui_float label_x; + gui_float label_y; + gui_float label_w; + gui_float label_h; + gui_float space; + gui_size lines = 0; + gui_size len = 0; + gui_size chars = 0; + if (!buffer || !text || !font) + return 0; + + label_x = text->x + text->pad_x; + label_y = text->y + text->pad_y; + label_w = MAX(0, text->w - 2 * text->pad_x); + label_h = MAX(0, text->h - 2 * text->pad_y); + space = font->height + 2 * text->pad_y; + chars = gui_font_chars_in_space(font, (const gui_char*)text->text, text->length, label_w); + while (chars && label_h >= space) { + lines++; + gui_draw_string(buffer, font, label_x, label_y, label_w, space, + text->font, (const gui_char*)text->text + len, chars); + + len += chars; + label_h -= space; + label_y += space; + chars = gui_font_chars_in_space(font, (const gui_char*)text->text + chars, + text->length - chars, label_w); + } + return lines; +} + +void +gui_image(struct gui_draw_buffer *buffer, const struct gui_image *image) +{ + gui_float image_x; + gui_float image_y; + gui_float image_w; + gui_float image_h; + if (!buffer || !image) + return; + + image_x = image->x + image->pad_x; + image_y = image->y + image->pad_y; + image_w = MAX(0, image->w - 2 * image->pad_x); + image_h = MAX(0, image->h - 2 * image->pad_y); + gui_draw_rectf(buffer, image->x, image->y, image->w, image->h, image->background); + gui_draw_image(buffer, image_x, image_y, image_w, image_h, + image->texture, image->uv[0], image->uv[1]); +} + +gui_int +gui_button_text(struct gui_draw_buffer *buffer, const struct gui_button *button, + const char *text, gui_size length, const struct gui_font *font, + const struct gui_input *in) +{ + gui_int ret = gui_false; + struct gui_color font_color, background, highlight; + gui_float button_w, button_h; if (!buffer || !in || !button) return gui_false; - x = button->x, y = button->y; - w = button->w, h = button->h; - t = (const gui_char*)button->text; - mouse_x = in->mouse_pos.x; - mouse_y = in->mouse_pos.y; - clicked_x = in->mouse_clicked_pos.x; - clicked_y = in->mouse_clicked_pos.y; - fc = button->font; - bg = button->background; - hc = button->highlight; + font_color = button->font; + background = button->background; + highlight = button->highlight; + button_w = MAX(button->w, 2 * button->pad_x); + button_h = MAX(button->h, 2 * button->pad_x); - if (INBOX(mouse_x, mouse_y, x, y, w, h)) { - if (INBOX(clicked_x, clicked_y, x, y, w, h)) + if (INBOX(in->mouse_pos.x, in->mouse_pos.y, button->x, button->y, button_w, button_h)) { + font_color = button->background; + background = button->highlight; + if (INBOX(in->mouse_clicked_pos.x, in->mouse_clicked_pos.y, + button->x, button->y, button_w, button_h)) ret = (in->mouse_down && in->mouse_clicked); - fc = bg; bg = hc; } + gui_draw_rectf(buffer, button->x, button->y, button_w, button_h, background); + gui_draw_rect(buffer, button->x+1, button->y+1, button_w-1, button_h-1, button->foreground); - gui_rectf(buffer, x, y, w, h, bg); - gui_rect(buffer, x+1, y+1, w-1, h-1, button->foreground); - if (!font || !button->text || !button->length) return ret; - - text_width = gui_font_text_width(font, t, button->length); - if (text_width > (w - 2.0f * button->pad_x)) { - label_x = x + button->pad_x; - label_y = y + button->pad_y; - label_w = w - 2 * button->pad_x; - label_h = h - 2 * button->pad_y; - } else { - label_w = w - 2 * button->pad_x; - label_h = h - 2 * button->pad_y; - label_x = x + (label_w - (gui_float)text_width) / 2; - label_y = y + (label_h - font->height) / 2; + if (font && text && length) { + gui_float label_x, label_y, label_w, label_h; + gui_size text_width = gui_font_text_width(font, (const gui_char*)text, length); + if (text_width > (button_w - 2.0f * button->pad_x)) { + label_x = button->x + button->pad_x; + label_y = button->y + button->pad_y; + label_w = button_w - 2 * button->pad_x; + label_h = button_h - 2 * button->pad_y; + } else { + label_w = button_w - 2 * button->pad_x; + label_h = button_h - 2 * button->pad_y; + label_x = button->x + (label_w - (gui_float)text_width) / 2; + label_y = button->y + (label_h - font->height) / 2; + } + gui_draw_string(buffer, font, label_x, label_y, label_w, label_h, font_color, + (const gui_char*)text, length); } - gui_string(buffer, font, label_x, label_y, label_w, label_h, fc, t, button->length); return ret; } +gui_int +gui_button_triangle(struct gui_draw_buffer *buffer, struct gui_button* button, + enum gui_direction direction, const struct gui_input *in) +{ + gui_bool pressed; + struct gui_vec2 points[3]; + pressed = gui_button_text(buffer, button, NULL, 0, NULL, in); + gui_triangle_from_direction(points, button->x, button->y, button->w, button->h, + button->pad_x, button->pad_y, direction); + gui_draw_trianglef(buffer, points[0].x, points[0].y, points[1].x, points[1].y, + points[2].x, points[2].y, button->foreground); + return pressed; +} + +gui_int +gui_button_image(struct gui_draw_buffer *buffer, struct gui_button* button, + gui_texture tex, struct gui_texCoord from, struct gui_texCoord to, + const struct gui_input *in) +{ + gui_bool pressed; + struct gui_image image; + pressed = gui_button_text(buffer, button, NULL, 0, NULL, in); + image.x = button->x; + image.y = button->y; + image.w = button->y; + image.h = button->y; + image.pad_x = button->pad_x; + image.pad_y = button->pad_y; + image.texture = tex; + image.uv[0] = from; + image.uv[1] = to; + image.background = button->background; + gui_image(buffer, &image); + return pressed; +} + gui_int gui_toggle(struct gui_draw_buffer *buffer, const struct gui_toggle *toggle, const struct gui_font *font, const struct gui_input *in) { + gui_int toggle_active; gui_float select_size; - gui_float x, y, w, h; - gui_int active; + gui_float toggle_w, toggle_h; gui_float select_x, select_y; gui_float cursor_x, cursor_y; gui_float cursor_pad, cursor_size; - gui_float label_x, label_w; - struct gui_color fg, bg; - const gui_char *t; + if (!buffer || !toggle || !in) + return 0; - if (!buffer || !toggle || !font || !in) return 0; - active = toggle->active; - t = (const gui_char*)toggle->text; - x = toggle->x, y = toggle->y; - w = toggle->w, h = toggle->h; - w = MAX(w, font->height + 2 * toggle->pad_x); - h = MAX(h, font->height + 2 * toggle->pad_y); - fg = toggle->foreground; - bg = toggle->background; + toggle_w = MAX(toggle->w, font->height + 2 * toggle->pad_x); + toggle_h = MAX(toggle->h, font->height + 2 * toggle->pad_y); + toggle_active = toggle->active; - select_x = x + toggle->pad_x; - select_y = y + toggle->pad_y; + select_x = toggle->x + toggle->pad_x; + select_y = toggle->y + toggle->pad_y; select_size = font->height; cursor_pad = select_size / 8; cursor_x = select_x + cursor_pad; cursor_y = select_y + cursor_pad; cursor_size = select_size - 2 * cursor_pad; - - label_x = x + select_size + toggle->pad_x * 2; - label_w = w - select_size + 3 * toggle->pad_x; - if (!in->mouse_down && in->mouse_clicked) { - const gui_float clicked_x = in->mouse_clicked_pos.x; - const gui_float clicked_y = in->mouse_clicked_pos.y; - if (INBOX(clicked_x, clicked_y, cursor_x, cursor_y, cursor_size, cursor_size)) - active = !active; + if (INBOX(in->mouse_clicked_pos.x, in->mouse_clicked_pos.y, + cursor_x, cursor_y, cursor_size, cursor_size)) + toggle_active = !toggle_active; } - gui_rectf(buffer, select_x, select_y, select_size, select_size, bg); - if (active) gui_rectf(buffer, cursor_x, cursor_y, cursor_size, cursor_size, fg); - gui_string(buffer, font, label_x, y + toggle->pad_y, label_w, - h - 2 * toggle->pad_y, toggle->font, t, toggle->length); - return active; + gui_draw_rectf(buffer, select_x, select_y, select_size, select_size, toggle->background); + if (toggle_active) + gui_draw_rectf(buffer, cursor_x, cursor_y, cursor_size, cursor_size, toggle->foreground); + + if (font && toggle->text && toggle->length) { + gui_float label_x, label_w; + label_x = toggle->x + select_size + toggle->pad_x * 2; + label_w = toggle_w - select_size + 3 * toggle->pad_x; + gui_draw_string(buffer, font, label_x, toggle->y + toggle->pad_y, label_w, + toggle_h - 2 * toggle->pad_y, toggle->font, toggle->text, toggle->length); + } + return toggle_active; } gui_float gui_slider(struct gui_draw_buffer *buffer, const struct gui_slider *slider, const struct gui_input *in) { - gui_float min, max, value, step; - gui_float x, y, w, h; + gui_float slider_min, slider_max; + gui_float slider_value, slider_steps; + gui_float slider_w, slider_h; gui_float mouse_x, mouse_y; gui_float clicked_x, clicked_y; - gui_float cursor_x, cursor_y, cursor_h; - gui_float cursor_w; - gui_float steps; + gui_float cursor_x, cursor_y; + gui_float cursor_w, cursor_h; + if (!buffer || !slider || !in) + return 0; - if (!buffer || !slider || !in) return 0; - x = slider->x; y = slider->y; - w = MAX(slider->w, 2 * slider->pad_x); - h = MAX(slider->h, 2 * slider->pad_y); - max = MAX(slider->min, slider->max); - min = MIN(slider->min, slider->max); - value = CLAMP(min, slider->value, max); - step = slider->step; - steps = (max - min) / step; + slider_w = MAX(slider->w, 2 * slider->pad_x); + slider_h = MAX(slider->h, 2 * slider->pad_y); + slider_max = MAX(slider->min, slider->max); + slider_min = MIN(slider->min, slider->max); + slider_value = CLAMP(slider_min, slider->value, slider_max); + slider_steps = (slider_max - slider_min) / slider->step; - cursor_w = (w - 2 * slider->pad_x) / (((max - min) + step) / step); - cursor_h = h - 2 * slider->pad_y; - cursor_x = x + slider->pad_x + (cursor_w * (value - min)); - cursor_y = y + slider->pad_y; + cursor_w = (slider_w - 2 * slider->pad_x); + cursor_w = cursor_w / (((slider_max - slider_min) + slider->step) / slider->step); + cursor_h = slider_h - 2 * slider->pad_y; + cursor_x = slider->x + slider->pad_x + (cursor_w * (slider_value - slider_min)); + cursor_y = slider->y + slider->pad_y; - if (!buffer || !in) return 0; mouse_x = in->mouse_pos.x; mouse_y = in->mouse_pos.y; clicked_x = in->mouse_clicked_pos.x; clicked_y = in->mouse_clicked_pos.y; if (in->mouse_down && - INBOX(clicked_x, clicked_y, x, y, w, h) && - INBOX(mouse_x, mouse_y, x, y, w, h)) + INBOX(clicked_x, clicked_y, slider->x, slider->y, slider_w, slider_h) && + INBOX(mouse_x, mouse_y, slider->x, slider->y, slider_w, slider_h)) { const float d = mouse_x - (cursor_x + cursor_w / 2.0f); - const float pxstep = (w - 2 * slider->pad_x) / steps; + const float pxstep = (slider_w - 2 * slider->pad_x) / slider_steps; if (ABS(d) >= pxstep) { - value += (d < 0) ? -step : step; - value = CLAMP(min, value, max); - cursor_x = x + slider->pad_x + (cursor_w * (value - min)); + slider_value += (d < 0) ? -slider->step : slider->step; + slider_value = CLAMP(slider_min, slider_value, slider_max); + cursor_x = slider->x + slider->pad_x + (cursor_w * (slider_value - slider_min)); } } - gui_rectf(buffer, x, y, w, h, slider->background); - gui_rectf(buffer, cursor_x, cursor_y, cursor_w, cursor_h, slider->foreground); - return value; + gui_draw_rectf(buffer, slider->x, slider->y, slider_w, slider_h, slider->background); + gui_draw_rectf(buffer, cursor_x, cursor_y, cursor_w, cursor_h, slider->foreground); + return slider_value; } gui_size gui_progress(struct gui_draw_buffer *buffer, const struct gui_progress *prog, const struct gui_input *in) { - gui_float scale; - gui_float mouse_x, mouse_y; + gui_float prog_scale; gui_float cursor_x, cursor_y; gui_float cursor_w, cursor_h; - gui_float x, y, w, h; - gui_size value; + gui_float prog_w, prog_h; + gui_size prog_value; if (!buffer || !in || !prog) return 0; - mouse_x = in->mouse_pos.x; - mouse_y = in->mouse_pos.y; + prog_w = MAX(prog->w, 2 * prog->pad_x + 1); + prog_h = MAX(prog->h, 2 * prog->pad_y + 1); + prog_value = MIN(prog->current, prog->max); - x = prog->x; y = prog->y; - w = MAX(prog->w, 2 * prog->pad_x + 1); - h = MAX(prog->h, 2 * prog->pad_y + 1); - value = MIN(prog->current, prog->max); - - if (prog->modifyable && in->mouse_down && INBOX(mouse_x, mouse_y, x, y, w, h)) { - gui_float ratio = (gui_float)(mouse_x - x) / (gui_float)w; - value = (gui_size)((gui_float)prog->max * ratio); + if (prog->modifyable && in->mouse_down && + INBOX(in->mouse_pos.x, in->mouse_pos.y, prog->x, prog->y, prog_w, prog_h)){ + gui_float ratio = (gui_float)(in->mouse_pos.x - prog->x) / (gui_float)prog_w; + prog_value = (gui_size)((gui_float)prog->max * ratio); } - if (!prog->max) return value; - value = MIN(value, prog->max); - scale = (gui_float)value / (gui_float)prog->max; - cursor_h = h - 2 * prog->pad_y; - cursor_w = (w - 2 * prog->pad_x) * scale; - cursor_x = x + prog->pad_x; - cursor_y = y + prog->pad_y; - gui_rectf(buffer, x, y, w, h, prog->background); - gui_rectf(buffer, cursor_x, cursor_y, cursor_w, cursor_h, prog->foreground); - return value; + if (!prog->max) return prog_value; + prog_value = MIN(prog_value, prog->max); + prog_scale = (gui_float)prog_value / (gui_float)prog->max; + + cursor_h = prog_h - 2 * prog->pad_y; + cursor_w = (prog_w - 2 * prog->pad_x) * prog_scale; + cursor_x = prog->x + prog->pad_x; + cursor_y = prog->y + prog->pad_y; + + gui_draw_rectf(buffer, prog->x, prog->y, prog_w, prog_h, prog->background); + gui_draw_rectf(buffer, cursor_x, cursor_y, cursor_w, cursor_h, prog->foreground); + return prog_value; } gui_int @@ -664,65 +814,63 @@ gui_input(struct gui_draw_buffer *buf, gui_char *buffer, gui_size *length, const struct gui_font *font, const struct gui_input *in) { gui_size offset = 0; - gui_float label_x, label_y; - gui_float label_w, label_h; - gui_float x, y, w, h; - gui_int active; - gui_float mouse_x, mouse_y; - gui_size text_width; - + gui_float input_w, input_h; + gui_int input_active; if (!buffer || !in || !font || !input) return 0; - mouse_x = in->mouse_clicked_pos.x; - mouse_y = in->mouse_clicked_pos.y; - x = input->x, y = input->y; - w = MAX(input->w, 2 * input->pad_x); - h = MAX(input->h, font->height); - active = input->active; + input_w = MAX(input->w, 2 * input->pad_x); + input_h = MAX(input->h, font->height); + input_active = input->active; + if (in->mouse_clicked && in->mouse_down) { + input_active = INBOX(in->mouse_pos.x, in->mouse_pos.y, + input->x, input->y, input_w, input_h); + } + gui_draw_rectf(buf, input->x, input->y, input_w, input_h, input->background); + gui_draw_rect(buf, input->x + 1, input->y, input_w - 1, input_h, input->foreground); - if (in->mouse_clicked && in->mouse_down) - active = INBOX(mouse_x, mouse_y, x, y, w, h); - if (active) { + if (input_active) { const struct gui_key *del = &in->keys[GUI_KEY_DEL]; const struct gui_key *bs = &in->keys[GUI_KEY_BACKSPACE]; const struct gui_key *enter = &in->keys[GUI_KEY_ENTER]; const struct gui_key *esc = &in->keys[GUI_KEY_ESCAPE]; const struct gui_key *space = &in->keys[GUI_KEY_SPACE]; - if (in->text_len && *length < input->max) { - gui_long unicode; - gui_size i = 0, l = 0; - gui_size ulen = utf_decode(in->text, &unicode, in->text_len); - while (ulen && (l + ulen) <= in->text_len && *length < input->max) { - for (i = 0; i < ulen; i++) - buffer[(*length)++] = in->text[l + i]; - l = l + ulen; - ulen = utf_decode(in->text + l, &unicode, in->text_len - l); - } - } + if ((del->down && del->clicked) || (bs->down && bs->clicked)) if (*length > 0) *length = *length - 1; if ((enter->down && enter->clicked) || (esc->down && esc->clicked)) - active = gui_false; + input_active = gui_false; if ((space->down && space->clicked) && (*length < input->max)) buffer[(*length)++] = ' '; + + if (in->text_len && *length < input->max) { + gui_size text_len = 0, glyph_len = 0; + gui_long unicode; + gui_size i = 0; + glyph_len = utf_decode(in->text, &unicode, in->text_len); + while (glyph_len && (text_len + glyph_len) <= in->text_len && *length < input->max) { + for (i = 0; i < glyph_len; i++) + buffer[(*length)++] = in->text[text_len + i]; + text_len = text_len + glyph_len; + glyph_len = utf_decode(in->text + text_len, &unicode, in->text_len - text_len); + } + } } - label_x = x + input->pad_x; - label_y = y + input->pad_y; - label_w = w - 2 * input->pad_x; - label_h = h - 2 * input->pad_y; - text_width = gui_font_text_width(font, buffer, *length); - while (text_width > label_w) { - offset += 1; - text_width = gui_font_text_width(font, &buffer[offset], *length - offset); + if (font && buffer && length && *length) { + gui_float label_x = input->x + input->pad_x; + gui_float label_y = input->y + input->pad_y; + gui_float label_w = input_w - 2 * input->pad_x; + gui_float label_h = input_h - 2 * input->pad_y; + gui_size text_width = gui_font_text_width(font, buffer, *length); + while (text_width > label_w) { + offset += 1; + text_width = gui_font_text_width(font, &buffer[offset], *length - offset); + } + gui_draw_string(buf, font, label_x, label_y, label_w, label_h, input->font, + &buffer[offset], *length); } - - gui_rectf(buf, x, y, w, h, input->background); - gui_rect(buf, x + 1, y, w - 1, h, input->foreground); - gui_string(buf, font, label_x, label_y, label_w, label_h, input->font, - &buffer[offset], *length); - return active; + return input_active; } gui_int @@ -730,69 +878,66 @@ gui_plot(struct gui_draw_buffer *buffer, const struct gui_plot *plot, const struct gui_input *in) { gui_size i; - gui_float last_x; - gui_float last_y; - gui_float max, min; - gui_float range, ratio; - gui_size step; + gui_int plot_selected = -1; + gui_float plot_max_value, plot_min_value; + gui_float plot_value_range, plot_value_ratio; + gui_size plot_step; + gui_float plot_w, plot_h; gui_float canvas_x, canvas_y; gui_float canvas_w, canvas_h; - gui_float x, y, w, h; - gui_int selected = -1; - gui_float mouse_x; - gui_float mouse_y; + gui_float last_x; + gui_float last_y; struct gui_color col; - if (!buffer || !plot || !in) - return selected; + return plot_selected; - x = plot->x, y = plot->y; - w = plot->w, h = plot->h; - mouse_x = in->mouse_pos.x; - mouse_y = in->mouse_pos.y; col = plot->foreground; - gui_rectf(buffer, x, y, w, h, plot->background); + plot_w = MAX(plot->w, 2 * plot->pad_x); + plot_h = MAX(plot->h, 2 * plot->pad_y); + gui_draw_rectf(buffer, plot->x, plot->y, plot_w, plot_h, plot->background); if (!plot->value_count) - return selected; + return plot_selected; - max = plot->values[0], min = plot->values[0]; + plot_max_value = plot->values[0]; + plot_min_value = plot->values[0]; for (i = 0; i < plot->value_count; ++i) { - if (plot->values[i] > max) - max = plot->values[i]; - if (plot->values[i] < min) - min = plot->values[i]; + if (plot->values[i] > plot_max_value) + plot_max_value = plot->values[i]; + if (plot->values[i] < plot_min_value) + plot_min_value = plot->values[i]; } - range = max - min; - canvas_x = x + plot->pad_x; - canvas_y = y + plot->pad_y; - canvas_w = MAX(1 + 2 * plot->pad_x, w - 2 * plot->pad_x); - canvas_h = MAX(1 + 2 * plot->pad_y, h - 2 * plot->pad_y); - step = (gui_size)canvas_w / plot->value_count; - ratio = (plot->values[0] - min) / range; + canvas_x = plot->x + plot->pad_x; + canvas_y = plot->y + plot->pad_y; + canvas_w = MAX(1 + 2 * plot->pad_x, plot_w - 2 * plot->pad_x); + canvas_h = MAX(1 + 2 * plot->pad_y, plot_h - 2 * plot->pad_y); + + plot_step = (gui_size)canvas_w / plot->value_count; + plot_value_range = plot_max_value - plot_min_value; + plot_value_ratio = (plot->values[0] - plot_min_value) / plot_value_range; + last_x = canvas_x; - last_y = (canvas_y + canvas_h) - ratio * (gui_float)canvas_h; - if (INBOX(mouse_x, mouse_y, last_x-3, last_y-3, 6, 6)) { - selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)i : -1; + last_y = (canvas_y + canvas_h) - plot_value_ratio * (gui_float)canvas_h; + if (INBOX(in->mouse_pos.x, in->mouse_pos.y, last_x-3, last_y-3, 6, 6)) { + plot_selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)i : -1; col = plot->highlight; } + gui_draw_rectf(buffer, last_x - 3, last_y - 3, 6, 6, col); - gui_rectf(buffer, last_x - 3, last_y - 3, 6, 6, col); for (i = 1; i < plot->value_count; i++) { gui_float cur_x, cur_y; - ratio = (plot->values[i] - min) / range; - cur_x = canvas_x + (gui_float)(step * i); - cur_y = (canvas_y + canvas_h) - (ratio * (gui_float)canvas_h); - gui_line(buffer, last_x, last_y, cur_x, cur_y, plot->foreground); - if (INBOX(mouse_x, mouse_y, cur_x-3, cur_y-3, 6, 6)) { - selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)i : -1; + plot_value_ratio = (plot->values[i] - plot_min_value) / plot_value_range; + cur_x = canvas_x + (gui_float)(plot_step * i); + cur_y = (canvas_y + canvas_h) - (plot_value_ratio * (gui_float)canvas_h); + gui_draw_line(buffer, last_x, last_y, cur_x, cur_y, plot->foreground); + if (INBOX(in->mouse_pos.x, in->mouse_pos.y, cur_x-3, cur_y-3, 6, 6)) { + plot_selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)i : plot_selected; col = plot->highlight; } else col = plot->foreground; - gui_rectf(buffer, cur_x - 3, cur_y - 3, 6, 6, col); - last_x = cur_x; - last_y = cur_y; + gui_draw_rectf(buffer, cur_x - 3, cur_y - 3, 6, 6, col); + last_x = cur_x, last_y = cur_y; } - return selected; + return plot_selected; } gui_int @@ -800,51 +945,46 @@ gui_histo(struct gui_draw_buffer *buffer, const struct gui_histo *histo, const struct gui_input *in) { gui_size i; - gui_float max; + gui_int selected = -1; gui_float canvas_x, canvas_y; gui_float canvas_w, canvas_h; + gui_float histo_max_value; + gui_float histo_w, histo_h; gui_float item_w = 0.0f; - gui_int selected = -1; - gui_float x, y, w, h; - struct gui_color fg, nc; - if (!buffer || !histo || !in) return selected; - x = histo->x, y = histo->y; - w = histo->w, h = histo->h; - fg = histo->foreground; - nc = histo->negative; - max = histo->values[0]; + histo_w = MAX(histo->w, 2 * histo->pad_x); + histo_h = MAX(histo->h, 2 * histo->pad_y); + gui_draw_rectf(buffer, histo->x, histo->y, histo_w, histo_h, histo->background); + + histo_max_value = histo->values[0]; for (i = 0; i < histo->value_count; ++i) { - if (ABS(histo->values[i]) > max) - max = histo->values[i]; + if (ABS(histo->values[i]) > histo_max_value) + histo_max_value = histo->values[i]; } - gui_rectf(buffer, x, y, w, h, histo->background); - canvas_x = x + histo->pad_x; - canvas_y = y + histo->pad_y; - canvas_w = MAX(0, w - 2 * histo->pad_x); - canvas_h = MAX(0, h - 2 * histo->pad_y); + canvas_x = histo->x + histo->pad_x; + canvas_y = histo->y + histo->pad_y; + canvas_w = histo_w - 2 * histo->pad_x; + canvas_h = histo_h - 2 * histo->pad_y; if (histo->value_count) { gui_float padding = (gui_float)(histo->value_count-1) * histo->pad_x; item_w = (canvas_w - padding) / (gui_float)(histo->value_count); } for (i = 0; i < histo->value_count; i++) { - const gui_float j = (gui_float)i; - const gui_float mouse_x = in->mouse_pos.x; - const gui_float mouse_y = in->mouse_pos.y; - const gui_float ratio = ABS(histo->values[i]) / max; - const gui_float item_h = canvas_h * ratio; - const gui_float item_x = canvas_x + (j * item_w) + (j * histo->pad_y); + const gui_float histo_ratio = ABS(histo->values[i]) / histo_max_value; + struct gui_color item_color = (histo->values[i] < 0) ? histo->negative: histo->foreground; + const gui_float item_h = canvas_h * histo_ratio; const gui_float item_y = (canvas_y + canvas_h) - item_h; - struct gui_color col = (histo->values[i] < 0) ? nc: fg; - if (INBOX(mouse_x, mouse_y, item_x, item_y, item_w, item_h)) { - selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)i : -1; - col = histo->highlight; + gui_float item_x = canvas_x + ((gui_float)i * item_w); + item_x = item_x + ((gui_float)i * histo->pad_y); + if (INBOX(in->mouse_pos.x, in->mouse_pos.y, item_x, item_y, item_w, item_h)) { + selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)i: selected; + item_color = histo->highlight; } - gui_rectf(buffer, item_x, item_y, item_w, item_h, col); + gui_draw_rectf(buffer, item_x, item_y, item_w, item_h, item_color); } return selected; } @@ -853,103 +993,107 @@ gui_size gui_scroll(struct gui_draw_buffer *buffer, const struct gui_scroll *scroll, const struct gui_input *in) { - gui_float mouse_x, mouse_y; - gui_float prev_x, prev_y; - gui_float x, y, w, h; - gui_size step, offset; + gui_bool button_up_pressed; + gui_bool button_down_pressed; + struct gui_button button; - gui_bool u, d; - gui_float button_size, button_half, button_y; - gui_float bar_h, bar_y; - struct gui_button up, down; - struct gui_color fg, bg; - gui_size target; + gui_float scroll_y, scroll_w, scroll_h; + gui_size scroll_offset; + gui_size scroll_step; + gui_float scroll_off, scroll_ratio; - gui_float ratio; - gui_float off; gui_float cursor_x, cursor_y; gui_float cursor_w, cursor_h; gui_float cursor_px, cursor_py; gui_bool inscroll, incursor; - gui_float pad; - gui_float xoff, yoff, boff; - gui_float xpad, ypad, xmid; + if (!buffer || !in || !scroll) return 0; + scroll_y = scroll->y; + scroll_w = MAX(scroll->w, 0); + scroll_h = MAX(scroll->h, 2 * scroll_w); + gui_draw_rectf(buffer, scroll->x, scroll->y, scroll_w, scroll_h, scroll->background); + if (scroll->target <= scroll_h) return 0; - if (!buffer || !in) return 0; - x = scroll->x, y = scroll->y; - w = scroll->w, h = scroll->h; - target = scroll->target; - fg = scroll->foreground; - bg = scroll->background; - gui_rectf(buffer, x, y, w, h, scroll->background); - if (scroll->target <= h) return 0; + button.x = scroll->x; + button.y = scroll->y; + button.w = scroll_w; + button.h = scroll_w; + button.pad_x = scroll_w / 4; + button.pad_y = scroll_w / 4; + button.background = scroll->foreground; + button.foreground = scroll->background; + button.highlight = scroll->foreground; + button_up_pressed = gui_button_triangle(buffer, &button, GUI_UP, in); + button.y = scroll->y + scroll_h - button.h; + button_down_pressed = gui_button_triangle(buffer, &button, GUI_DOWN, in); - mouse_x = in->mouse_pos.x; - mouse_y = in->mouse_pos.y; - prev_x = in->mouse_prev.x; - prev_y = in->mouse_prev.y; + scroll_h = scroll_h - 2 * button.h; + scroll_y = scroll->y + button.h; + scroll_step = MIN(scroll->step, (gui_size)scroll_h); + scroll_offset = MIN(scroll->offset, (gui_size)(scroll->target - (gui_size)scroll_h)); + scroll_ratio = (gui_float)scroll_h/(gui_float)scroll->target; + scroll_off = (gui_float)scroll_offset/(gui_float)scroll->target; - w = MAX(w, 0); - h = MAX(h, 2 * w); - step = MIN(scroll->step, (gui_size)h); + cursor_h = scroll_ratio * scroll_h; + cursor_y = scroll_y + (scroll_off * scroll_h); + cursor_w = scroll_w; + cursor_x = scroll->x; - pad = w / 4; - button_size = w; - button_half = button_size / 2; - button_y = y + h - button_size; - bar_h = h - 2 * button_size; - bar_y = y + button_size; - offset = MIN(scroll->offset, (gui_size)(scroll->target - (gui_size)bar_h)); - - ratio = (gui_float)bar_h/(gui_float)scroll->target; - off = (gui_float)offset/(gui_float)scroll->target; - cursor_h = ratio * bar_h; - cursor_y = bar_y + (off * bar_h); - cursor_w = w; - cursor_x = x; - - up.x = x, up.y = y; - up.w = button_size, up.h = button_size; - up.pad_y = 0, up.pad_x = 0; - up.text = NULL, up.length = 0; - up.font = fg, up.background = fg; - up.foreground = bg, up.highlight = fg; - - down.x = x, down.y = y + h - button_size; - down.w = button_size, down.h = button_size; - down.pad_y = 0, down.pad_x = 0; - down.text = NULL, down.length = 0; - down.font = fg, down.background = fg; - down.foreground = bg, down.highlight = fg; - u = gui_button(buffer, &up, NULL, in); - d = gui_button(buffer, &down, NULL, in); - - xpad = x + pad; - ypad = button_y + pad; - xmid = x + button_half; - xoff = x + (button_size - pad); - yoff = y + (button_size - pad); - boff = button_y + (button_size - pad); - gui_trianglef(buffer, xmid, y + pad, xoff, yoff, xpad, yoff, scroll->background); - gui_trianglef(buffer, xpad, ypad, xoff, ypad, xmid, boff, scroll->background); - - inscroll = INBOX(mouse_x, mouse_y, x, y, w, h); - incursor = INBOX(prev_x, prev_y, cursor_x, cursor_y, cursor_w, cursor_h); + inscroll = INBOX(in->mouse_pos.x, in->mouse_pos.y, scroll->x, scroll->y, scroll_w, scroll_h); + incursor = INBOX(in->mouse_prev.x, in->mouse_prev.y, cursor_x, cursor_y, cursor_w, cursor_h); if (in->mouse_down && inscroll && incursor) { const gui_float pixel = in->mouse_delta.y; - const gui_float delta = (pixel / (gui_float)bar_h) * (gui_float)target; - offset = MIN(offset + (gui_size)delta, target - (gui_size)bar_h); + const gui_float delta = (pixel / (gui_float)scroll_h) * (gui_float)scroll->target; + scroll_offset = MIN(scroll_offset + (gui_size)delta, scroll->target - (gui_size)scroll_h); cursor_y += pixel; - } else if (u || d) { - offset = (d) ? MIN(offset + step, target - (gui_size)bar_h): MAX(0, offset - step); - off = (gui_float)offset / (gui_float)scroll->target; - cursor_y = bar_y + (off * bar_h); + } else if (button_up_pressed || button_down_pressed) { + scroll_offset = (button_down_pressed) ? + MIN(scroll_offset + scroll_step, scroll->target - (gui_size)scroll_h): + MAX(0, scroll_offset - scroll_step); + scroll_off = (gui_float)scroll_offset / (gui_float)scroll->target; + cursor_y = scroll_y + (scroll_off * scroll_h); } - gui_rectf(buffer, cursor_x, cursor_y, cursor_w, cursor_h, fg); - gui_rect(buffer, cursor_x+1, cursor_y, cursor_w-1, cursor_h, bg); - return offset; + gui_draw_rectf(buffer, cursor_x, cursor_y, cursor_w, cursor_h, scroll->foreground); + gui_draw_rect(buffer, cursor_x+1, cursor_y, cursor_w-1, cursor_h, scroll->background); + return scroll_offset; +} + +void +gui_default_config(struct gui_config *config) +{ + if (!config) return; + config->global_alpha = 1.0f; + config->scrollbar_width = 16; + config->scroll_factor = 2; + config->panel_padding = gui_make_vec2(15.0f, 10.0f); + config->panel_min_size = gui_make_vec2(32.0f, 32.0f); + config->item_spacing = gui_make_vec2(8.0f, 8.0f); + config->item_padding = gui_make_vec2(4.0f, 4.0f); + config->colors[GUI_COLOR_TEXT] = gui_make_color(255, 255, 255, 255); + config->colors[GUI_COLOR_PANEL] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_BORDER] = gui_make_color(0, 0, 0, 255); + config->colors[GUI_COLOR_TITLEBAR] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_BUTTON] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_BUTTON_HOVER] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_BUTTON_BORDER] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_TOGGLE] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_TOGGLE_ACTIVE] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_SCROLL] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_SCROLL_CURSOR] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_SLIDER] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_SLIDER_CURSOR] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_PROGRESS] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_PROGRESS_CURSOR] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_INPUT] = gui_make_color(45, 45, 45, 45); + config->colors[GUI_COLOR_INPUT_BORDER] = gui_make_color(100, 100, 100, 100); + config->colors[GUI_COLOR_HISTO] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_HISTO_BARS] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_HISTO_NEGATIVE] = gui_make_color(255, 255, 255, 255); + config->colors[GUI_COLOR_HISTO_HIGHLIGHT] = gui_make_color(255, 0, 0, 255); + config->colors[GUI_COLOR_PLOT] = gui_make_color(100, 100, 100, 255); + config->colors[GUI_COLOR_PLOT_LINES] = gui_make_color(45, 45, 45, 255); + config->colors[GUI_COLOR_PLOT_HIGHLIGHT] = gui_make_color(255, 0, 0, 255); } void @@ -990,7 +1134,7 @@ gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *out, header_height = panel->font->height + 3 * config->item_padding.y; header_height += config->panel_padding.y; - gui_rectf(out, x, y, w, header_height, *header); + gui_draw_rectf(out, x, y, w, header_height, *header); panel->out = out; panel->x = x; @@ -1004,7 +1148,7 @@ gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *out, if (panel->flags & GUI_PANEL_SCROLLBAR) panel->height = h; - if (panel->flags & GUI_PANEL_CLOSEABLE) { + if (panel->flags & (GUI_PANEL_CLOSEABLE|GUI_PANEL_HEADER)) { gui_size text_width; gui_float close_x, close_y, close_w, close_h; const gui_char *X = (const gui_char*)"x"; @@ -1016,7 +1160,7 @@ gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *out, close_h = panel->font->height + 2 * config->item_padding.y; w -= ((gui_float)text_width + config->panel_padding.x); - gui_string(panel->out, panel->font, close_x, close_y, close_w, close_h, + gui_draw_string(panel->out, panel->font, close_x, close_y, close_w, close_h, config->colors[GUI_COLOR_TEXT], X, 1); if (INBOX(mouse_x, mouse_y, close_x, close_y, close_w, close_h)) { if (INBOX(clicked_x, clicked_y, close_x, close_y, close_w, close_h)) @@ -1024,7 +1168,7 @@ gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *out, } } - if (panel->flags & GUI_PANEL_MINIMIZABLE) { + if (panel->flags & (GUI_PANEL_MINIMIZABLE|GUI_PANEL_HEADER)) { gui_size text_width; gui_float min_x, min_y, min_w, min_h; const gui_char *score = (panel->minimized) ? @@ -1038,7 +1182,7 @@ gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *out, min_h = panel->font->height + 2 * config->item_padding.y; w -= (gui_float)text_width; - gui_string(panel->out, panel->font, min_x, min_y, min_w, min_h, + gui_draw_string(panel->out, panel->font, min_x, min_y, min_w, min_h, config->colors[GUI_COLOR_TEXT], score, 1); if (INBOX(mouse_x, mouse_y, min_x, min_y, min_w, min_h)) { if (INBOX(clicked_x, clicked_y, min_x, min_y, min_w, min_h)) @@ -1053,7 +1197,7 @@ gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *out, label_w = w - 2 * config->panel_padding.x - 2 * config->item_padding.x; label_h = panel->font->height + 2 * config->item_padding.y; while (*i++ != '\0') text_len++; - gui_string(panel->out, panel->font, label_x, label_y, label_w, label_h, + gui_draw_string(panel->out, panel->font, label_x, label_y, label_w, label_h, config->colors[GUI_COLOR_TEXT], (const gui_char*)text, text_len); return ret; } @@ -1068,8 +1212,8 @@ gui_panel_row(struct gui_panel *panel, gui_float height, gui_size cols) panel->index = 0; panel->row_columns = cols; panel->row_height = height + config->item_spacing.y; - gui_rectf(panel->out, panel->x, panel->at_y, panel->width, - height + config->panel_padding.y, *color); + gui_draw_rectf(panel->out, panel->x, panel->at_y, panel->width, + height + config->panel_padding.y, *color); } void @@ -1080,8 +1224,10 @@ gui_panel_seperator(struct gui_panel *panel, gui_size cols) config = panel->config; cols = MIN(cols, panel->row_columns - panel->index); panel->index += cols; - if (panel->index >= panel->row_columns) - gui_panel_row(panel, panel->row_height - config->item_spacing.y, panel->row_columns); + if (panel->index >= panel->row_columns) { + const gui_float row_height = panel->row_height - config->item_spacing.y; + gui_panel_row(panel, row_height, panel->row_columns); + } } static void @@ -1090,8 +1236,10 @@ gui_panel_alloc_space(struct gui_rect *bounds, struct gui_panel *panel) const struct gui_config *config = panel->config; gui_float padding, spacing, space; gui_float item_offset, item_width, item_spacing; - if (panel->index >= panel->row_columns) - gui_panel_row(panel, panel->row_height - config->item_spacing.y, panel->row_columns); + if (panel->index >= panel->row_columns) { + const gui_float row_height = panel->row_height - config->item_spacing.y; + gui_panel_row(panel, row_height, panel->row_columns); + } padding = 2.0f * config->panel_padding.x; spacing = (gui_float)(panel->row_columns - 1) * config->item_spacing.x; @@ -1108,8 +1256,58 @@ gui_panel_alloc_space(struct gui_rect *bounds, struct gui_panel *panel) panel->index++; } +void +gui_panel_text(struct gui_panel *panel, const char *str, gui_size len) +{ + struct gui_rect bounds; + struct gui_text text; + const struct gui_config *config; + + if (!panel || !panel->config || !panel->in || !panel->out) return; + if (!panel->font || panel->minimized) return; + gui_panel_alloc_space(&bounds, panel); + config = panel->config; + + text.x = bounds.x; + text.y = bounds.y; + text.w = bounds.w; + text.h = bounds.h; + text.pad_x = config->item_padding.x; + text.pad_y = config->item_padding.y; + text.text = str; + text.length = len; + text.font = config->colors[GUI_COLOR_TEXT]; + text.background = config->colors[GUI_COLOR_PANEL]; + return gui_text(panel->out, &text, panel->font); +} + +gui_size +gui_panel_text_wrap(struct gui_panel *panel, const char *str, gui_size len) +{ + struct gui_rect bounds; + struct gui_text text; + const struct gui_config *config; + + if (!panel || !panel->config || !panel->in || !panel->out) return 0; + if (!panel->font || panel->minimized) return 0; + gui_panel_alloc_space(&bounds, panel); + config = panel->config; + + text.x = bounds.x; + text.y = bounds.y; + text.w = bounds.w; + text.h = bounds.h; + text.pad_x = config->item_padding.x; + text.pad_y = config->item_padding.y; + text.text = str; + text.length = len; + text.font = config->colors[GUI_COLOR_TEXT]; + text.background = config->colors[GUI_COLOR_PANEL]; + return gui_text_wrap(panel->out, &text, panel->font); +} + gui_int -gui_panel_button(struct gui_panel *panel, const char *str, gui_size len) +gui_panel_button_text(struct gui_panel *panel, const char *str, gui_size len) { struct gui_rect bounds; struct gui_button button; @@ -1120,8 +1318,6 @@ gui_panel_button(struct gui_panel *panel, const char *str, gui_size len) gui_panel_alloc_space(&bounds, panel); config = panel->config; - button.text = str; - button.length = len; button.x = bounds.x; button.y = bounds.y; button.w = bounds.w; @@ -1132,7 +1328,57 @@ gui_panel_button(struct gui_panel *panel, const char *str, gui_size len) button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER]; button.font = config->colors[GUI_COLOR_TEXT]; button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER]; - return gui_button(panel->out, &button, panel->font, panel->in); + return gui_button_text(panel->out, &button, str, len, panel->font, panel->in); +} + +gui_int +gui_panel_button_triangle(struct gui_panel *panel, enum gui_direction d) +{ + struct gui_rect bounds; + struct gui_button button; + const struct gui_config *config; + + if (!panel || !panel->config || !panel->in || !panel->out) return 0; + if (!panel->font || panel->minimized) return 0; + gui_panel_alloc_space(&bounds, panel); + config = panel->config; + + button.x = bounds.x; + button.y = bounds.y; + button.w = bounds.w; + button.h = bounds.h; + button.pad_x = config->item_padding.x; + button.pad_y = config->item_padding.y; + button.background = config->colors[GUI_COLOR_BUTTON]; + button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER]; + button.font = config->colors[GUI_COLOR_TEXT]; + button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER]; + return gui_button_triangle(panel->out, &button, d, panel->in); +} + +gui_int +gui_panel_button_image(struct gui_panel *panel, gui_texture tex, + struct gui_texCoord from, struct gui_texCoord to) +{ + struct gui_rect bounds; + struct gui_button button; + const struct gui_config *config; + + if (!panel || !panel->config || !panel->in || !panel->out) return 0; + if (!panel->font || panel->minimized) return 0; + gui_panel_alloc_space(&bounds, panel); + config = panel->config; + button.x = bounds.x; + button.y = bounds.y; + button.w = bounds.w; + button.h = bounds.h; + button.pad_x = config->item_padding.x; + button.pad_y = config->item_padding.y; + button.background = config->colors[GUI_COLOR_BUTTON]; + button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER]; + button.font = config->colors[GUI_COLOR_TEXT]; + button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER]; + return gui_button_image(panel->out, &button, tex, from, to, panel->in); } gui_int @@ -1155,7 +1401,7 @@ gui_panel_toggle(struct gui_panel *panel, const char *text, gui_size length, toggle.pad_x = config->item_padding.x; toggle.pad_y = config->item_padding.y; toggle.active = is_active; - toggle.text = text; + toggle.text = (const gui_char*)text; toggle.length = length; toggle.font = config->colors[GUI_COLOR_TEXT]; toggle.background = config->colors[GUI_COLOR_TOGGLE]; diff --git a/gui.h b/gui.h index 03fb1de..4684b11 100644 --- a/gui.h +++ b/gui.h @@ -8,21 +8,19 @@ /* * ------------- TODO-List ------------ - * - cleanup * - widgets - * o Text - * o Text Wrapped - * o Image + * o Input (1) * o Counter - * o Image Button * - panel + * o Group (4) + * o Border (3) * o Moveable * o Scaleable - * o Scrollbar + * o Scrollbar (2) * o Tabs * o Icon * o combobox - * o listView + * o listView (5) * o treeView * o textBox * --------------------------------------- @@ -44,6 +42,7 @@ typedef unsigned long gui_size; typedef gui_char gui_glyph[GUI_UTF_SIZE]; enum {gui_false, gui_true}; +enum gui_direction {GUI_UP, GUI_RIGHT, GUI_DOWN, GUI_LEFT}; struct gui_color {gui_byte r,g,b,a;}; struct gui_colorf {gui_float r,g,b,a;}; struct gui_texCoord {gui_float u,v;}; @@ -123,15 +122,21 @@ struct gui_text { gui_size length; struct gui_color font; struct gui_color background; - struct gui_color foreground; +}; + +struct gui_image { + gui_float x, y; + gui_float w, h; + gui_float pad_x, pad_y; + struct gui_texCoord uv[2]; + gui_texture texture; + struct gui_color background; }; struct gui_button { gui_float x, y; gui_float w, h; gui_float pad_x, pad_y; - const char *text; - gui_size length; struct gui_color font; struct gui_color background; struct gui_color foreground; @@ -144,7 +149,7 @@ struct gui_toggle { gui_float pad_x, pad_y; gui_int active; gui_size length; - const char *text; + const gui_char *text; struct gui_color font; struct gui_color background; struct gui_color foreground; @@ -182,12 +187,33 @@ struct gui_scroll { struct gui_color foreground; }; +enum gui_input_flags { + GUI_INPUT_ALL = -1, + GUI_INPUT_INTEGER = 0x01, + GUI_INPUT_FLOAT = 0x02, + GUI_INPUT_LITERALS = 0x04, +}; + struct gui_input_field { gui_float x, y; gui_float w, h; gui_float pad_x, pad_y; gui_size max; gui_bool active; + gui_flags flags; + gui_bool password; + struct gui_color background; + struct gui_color foreground; + struct gui_color font; +}; + +struct gui_spinner { + gui_float x, y; + gui_float w, h; + gui_float pad_x, pad_y; + gui_bool active; + gui_float max; + gui_float min; struct gui_color background; struct gui_color foreground; struct gui_color font; @@ -257,9 +283,12 @@ struct gui_config { }; enum gui_panel_flags { - GUI_PANEL_MINIMIZABLE = 0x01, - GUI_PANEL_CLOSEABLE = 0x02, - GUI_PANEL_SCROLLBAR = 0x04 + GUI_PANEL_GROUP = 0x01, + GUI_PANEL_HEADER = 0x02, + GUI_PANEL_BORDER = 0x04, + GUI_PANEL_MINIMIZABLE = 0x08, + GUI_PANEL_CLOSEABLE = 0x10, + GUI_PANEL_SCROLLBAR = 0x20 }; struct gui_panel { @@ -277,11 +306,6 @@ struct gui_panel { const struct gui_config *config; }; -/* Utility */ -struct gui_color gui_make_color(gui_byte r, gui_byte g, gui_byte b, gui_byte a); -struct gui_vec2 gui_make_vec2(gui_float x, gui_float y); -void gui_default_config(struct gui_config *config); - /* Input */ void gui_input_begin(struct gui_input *in); void gui_input_motion(struct gui_input *in, gui_int x, gui_int y); @@ -297,36 +321,51 @@ gui_size gui_end(struct gui_draw_buffer *buf); /* Widgets */ void gui_text(struct gui_draw_buffer *buf, const struct gui_text *text, const struct gui_font *font); -void gui_text_wrap(struct gui_draw_buffer *buf, const struct gui_text *text, +gui_size gui_text_wrap(struct gui_draw_buffer *buf, const struct gui_text *text, const struct gui_font *font); -gui_int gui_button(struct gui_draw_buffer *buf, const struct gui_button *button, - const struct gui_font *font, const struct gui_input *in); +void gui_image(struct gui_draw_buffer *buf, const struct gui_image *image); +gui_int gui_button_text(struct gui_draw_buffer *buf, const struct gui_button *button, + const char *text, gui_size len, const struct gui_font *font, + const struct gui_input *in); +gui_int gui_button_triangle(struct gui_draw_buffer *buffer, struct gui_button* button, + enum gui_direction direction, const struct gui_input *in); +gui_int gui_button_image(struct gui_draw_buffer *buffer, struct gui_button* button, + gui_texture tex, struct gui_texCoord from, struct gui_texCoord to, + const struct gui_input *in); gui_int gui_toggle(struct gui_draw_buffer *buf, const struct gui_toggle *toggle, const struct gui_font *font, const struct gui_input *in); gui_float gui_slider(struct gui_draw_buffer *buf, const struct gui_slider *slider, const struct gui_input *in); gui_size gui_progress(struct gui_draw_buffer *buf, const struct gui_progress *prog, const struct gui_input *in); -gui_size gui_scroll(struct gui_draw_buffer *buf, const struct gui_scroll *scroll, - const struct gui_input *in); gui_int gui_input(struct gui_draw_buffer *buf, gui_char *buffer, gui_size *length, const struct gui_input_field *input, const struct gui_font *font, const struct gui_input *in); +gui_int gui_spinner(struct gui_draw_buffer *buf, const struct gui_spinner *spinner, + gui_float *value, gui_float *step, + const struct gui_font *font, const struct gui_input *in); +gui_size gui_scroll(struct gui_draw_buffer *buf, const struct gui_scroll *scroll, + const struct gui_input *in); gui_int gui_histo(struct gui_draw_buffer *buf, const struct gui_histo *histo, const struct gui_input *in); gui_int gui_plot(struct gui_draw_buffer *buf, const struct gui_plot *plot, const struct gui_input *in); /* Panel */ +void gui_default_config(struct gui_config *config); void gui_panel_init(struct gui_panel *panel, const struct gui_config *config, - const struct gui_font *font, const struct gui_input *input); + const struct gui_font *font, const struct gui_input *input); gui_int gui_panel_begin(struct gui_panel *panel, struct gui_draw_buffer *q, const char *t, gui_flags f, gui_float x, gui_float y, gui_float w, gui_float h); void gui_panel_row(struct gui_panel *panel, gui_float height, gui_size cols); void gui_panel_seperator(struct gui_panel *panel, gui_size cols); void gui_panel_text(struct gui_panel *panel, const char *str, gui_size len); -gui_int gui_panel_button(struct gui_panel *panel, const char *str, gui_size len); +gui_size gui_panel_text_wrap(struct gui_panel *panel, const char *str, gui_size len); +gui_int gui_panel_button_text(struct gui_panel *panel, const char *str, gui_size len); +gui_int gui_panel_button_triangle(struct gui_panel *panel, enum gui_direction d); +gui_int gui_panel_button_image(struct gui_panel *panel, gui_texture tex, + struct gui_texCoord from, struct gui_texCoord to); gui_int gui_panel_toggle(struct gui_panel *panel, const char *str, gui_size len, gui_int active); gui_float gui_panel_slider(struct gui_panel *panel, gui_float min, gui_float v, diff --git a/opengl.c b/opengl.c index 2c20f37..14728bb 100644 --- a/opengl.c +++ b/opengl.c @@ -487,9 +487,11 @@ main(int argc, char *argv[]) /* ------------------------- GUI --------------------------*/ gui_begin(&gui.out, buffer, MAX_BUFFER); xw.running = gui_panel_begin(&gui.panel, &gui.out, "Demo", - GUI_PANEL_CLOSEABLE|GUI_PANEL_MINIMIZABLE, 50, 50, 200, 0); + GUI_PANEL_HEADER|GUI_PANEL_CLOSEABLE|GUI_PANEL_MINIMIZABLE, + 20, 20, 200, 0); gui_panel_row(&gui.panel, 30, 1); - if (gui_panel_button(&gui.panel, "button", 6)) + gui_panel_text(&gui.panel, "Text", 4); + if (gui_panel_button_text(&gui.panel, "button", 6)) fprintf(stdout, "button pressed!\n"); slider = gui_panel_slider(&gui.panel, 0.0f, slider, 10.0f, 1.0f); prog = gui_panel_progress(&gui.panel, prog, 100, gui_true);