2590 lines
86 KiB
C
2590 lines
86 KiB
C
#include "iron_ui.h"
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <kinc/graphics4/graphics.h>
|
|
#include <kinc/input/keyboard.h>
|
|
#include <kinc/input/mouse.h>
|
|
#include <kinc/system.h>
|
|
#include <kinc/window.h>
|
|
#include <kinc/log.h>
|
|
#include <kinc/graphics2/g2_ext.h>
|
|
#include "iron_string.h"
|
|
#include "iron_gc.h"
|
|
|
|
static ui_t *current = NULL;
|
|
static ui_theme_t *theme;
|
|
static bool ui_key_repeat = true; // Emulate key repeat for non-character keys
|
|
static bool ui_dynamic_glyph_load = true; // Allow text input fields to push new glyphs into the font atlas
|
|
static float ui_key_repeat_time = 0.0;
|
|
static char ui_text_to_paste[1024];
|
|
static char ui_text_to_copy[1024];
|
|
static ui_t *ui_copy_receiver = NULL;
|
|
static int ui_copy_frame = 0;
|
|
static bool ui_combo_first = true;
|
|
static ui_handle_t *ui_combo_search_handle = NULL;
|
|
ui_t *ui_instances[UI_MAX_INSTANCES];
|
|
int ui_instances_count;
|
|
bool ui_always_redraw_window = true; // Redraw cached window texture each frame or on changes only
|
|
bool ui_touch_scroll = false; // Pan with finger to scroll
|
|
bool ui_touch_hold = false; // Touch and hold finger for right click
|
|
bool ui_touch_tooltip = false; // Show extra tooltips above finger / on-screen keyboard
|
|
bool ui_is_cut = false;
|
|
bool ui_is_copy = false;
|
|
bool ui_is_paste = false;
|
|
void (*ui_on_border_hover)(ui_handle_t *, int) = NULL; // Mouse over window border, use for resizing
|
|
void (*ui_on_text_hover)(void) = NULL; // Mouse over text input, use to set I-cursor
|
|
void (*ui_on_deselect_text)(void) = NULL; // Text editing finished
|
|
void (*ui_on_tab_drop)(ui_handle_t *, int, ui_handle_t *, int) = NULL; // Tab reorder via drag and drop
|
|
#ifdef WITH_EVAL
|
|
float js_eval(char *str);
|
|
#endif
|
|
|
|
float UI_SCALE() {
|
|
return current->ops->scale_factor;
|
|
}
|
|
|
|
float UI_ELEMENT_W() {
|
|
return theme->ELEMENT_W * UI_SCALE();
|
|
}
|
|
|
|
float UI_ELEMENT_H() {
|
|
return theme->ELEMENT_H * UI_SCALE();
|
|
}
|
|
|
|
float UI_ELEMENT_OFFSET() {
|
|
return theme->ELEMENT_OFFSET * UI_SCALE();
|
|
}
|
|
|
|
float UI_ARROW_SIZE() {
|
|
return theme->ARROW_SIZE * UI_SCALE();
|
|
}
|
|
|
|
float UI_BUTTON_H() {
|
|
return theme->BUTTON_H * UI_SCALE();
|
|
}
|
|
|
|
float UI_CHECK_SIZE() {
|
|
return theme->CHECK_SIZE * UI_SCALE();
|
|
}
|
|
|
|
float UI_CHECK_SELECT_SIZE() {
|
|
return theme->CHECK_SELECT_SIZE * UI_SCALE();
|
|
}
|
|
|
|
float UI_FONT_SIZE() {
|
|
return theme->FONT_SIZE * UI_SCALE();
|
|
}
|
|
|
|
float UI_SCROLL_W() {
|
|
return theme->SCROLL_W * UI_SCALE();
|
|
}
|
|
|
|
float UI_SCROLL_MINI_W() {
|
|
return theme->SCROLL_MINI_W * UI_SCALE();
|
|
}
|
|
|
|
float UI_TEXT_OFFSET() {
|
|
return theme->TEXT_OFFSET * UI_SCALE();
|
|
}
|
|
|
|
float UI_TAB_W() {
|
|
return theme->TAB_W * UI_SCALE();
|
|
}
|
|
|
|
float UI_HEADER_DRAG_H() {
|
|
return 15.0 * UI_SCALE();
|
|
}
|
|
|
|
float UI_TOOLTIP_DELAY() {
|
|
return 1.0;
|
|
}
|
|
|
|
ui_t *ui_get_current() {
|
|
return current;
|
|
}
|
|
|
|
void ui_set_current(ui_t *_current) {
|
|
current = _current;
|
|
theme = current->ops->theme;
|
|
}
|
|
|
|
ui_handle_t *ui_handle_create() {
|
|
ui_handle_t *h = (ui_handle_t *)gc_alloc(sizeof(ui_handle_t));
|
|
memset(h, 0, sizeof(ui_handle_t));
|
|
h->redraws = 2;
|
|
h->color = 0xffffffff;
|
|
h->text = "";
|
|
h->init = true;
|
|
return h;
|
|
}
|
|
|
|
ui_handle_t *ui_nest(ui_handle_t *handle, int pos) {
|
|
if (handle->children == NULL) {
|
|
handle->children = any_array_create(0);
|
|
}
|
|
while(pos >= handle->children->length) {
|
|
ui_handle_t *h = ui_handle_create();
|
|
any_array_push(handle->children, h);
|
|
if (pos == handle->children->length - 1) {
|
|
// Return now so init stays true
|
|
return h;
|
|
}
|
|
}
|
|
// This handle already exists, set init to false
|
|
handle->children->buffer[pos]->init = false;
|
|
return handle->children->buffer[pos];
|
|
}
|
|
|
|
void ui_fade_color(float alpha) {
|
|
uint32_t color = arm_g2_get_color();
|
|
uint8_t r = (color & 0x00ff0000) >> 16;
|
|
uint8_t g = (color & 0x0000ff00) >> 8;
|
|
uint8_t b = (color & 0x000000ff);
|
|
uint8_t a = (uint8_t)(255.0 * alpha);
|
|
arm_g2_set_color((a << 24) | (r << 16) | (g << 8) | b);
|
|
}
|
|
|
|
void ui_fill(float x, float y, float w, float h, uint32_t color) {
|
|
arm_g2_set_color(color);
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
arm_g2_fill_rect(current->_x + x * UI_SCALE(), current->_y + y * UI_SCALE() - 1, w * UI_SCALE(), h * UI_SCALE());
|
|
arm_g2_set_color(0xffffffff);
|
|
}
|
|
|
|
void ui_rect(float x, float y, float w, float h, uint32_t color, float strength) {
|
|
arm_g2_set_color(color);
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
arm_g2_draw_rect(current->_x + x * UI_SCALE(), current->_y + y * UI_SCALE(), w * UI_SCALE(), h * UI_SCALE(), strength);
|
|
arm_g2_set_color(0xffffffff);
|
|
}
|
|
|
|
void ui_draw_shadow(float x, float y, float w, float h) {
|
|
if (theme->SHADOWS) {
|
|
// for (int i = 0; i < 8; i++) {
|
|
// float offset = i * UI_SCALE();
|
|
// float a = (8 - i + 1) * 0.01;
|
|
// arm_g2_set_color(((uint8_t)(a * 255) << 24) | (0 << 16) | (0 << 8) | 0);
|
|
// ui_draw_rect(true, x - offset, y - offset, w + offset * 2, h + offset * 2);
|
|
// }
|
|
float max_offset = 4.0 * UI_SCALE();
|
|
for (int i = 0; i < 4; i++) {
|
|
float offset = (max_offset / 4) * (i + 1);
|
|
float a = 0.1 - (0.1 / 4) * i;
|
|
arm_g2_set_color(((uint8_t)(a * 255) << 24) | (0 << 16) | (0 << 8) | 0);
|
|
ui_draw_rect(true, x + offset, y + offset, w + (max_offset - offset) * 2, h + (max_offset - offset) * 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ui_draw_rect(bool fill, float x, float y, float w, float h) {
|
|
float strength = 1.0;
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
x = (int)x;
|
|
y = (int)y;
|
|
w = (int)w;
|
|
h = (int)h;
|
|
|
|
if (fill) {
|
|
int r = current->filled_round_corner_image.width;
|
|
if (theme->ROUND_CORNERS && current->enabled && r > 0 && w >= r * 2.0) {
|
|
y -= 1; // Make it pixel perfect with non-round draw
|
|
h += 1;
|
|
arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x, y, r, r);
|
|
arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x, y + h, r, -r);
|
|
arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x + w, y, -r, r);
|
|
arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x + w, y + h, -r, -r);
|
|
arm_g2_fill_rect(x + r, y, w - r * 2.0, h);
|
|
arm_g2_fill_rect(x, y + r, w, h - r * 2.0);
|
|
}
|
|
else {
|
|
arm_g2_fill_rect(x, y - 1, w, h + 1);
|
|
}
|
|
}
|
|
else {
|
|
int r = current->round_corner_image.width;
|
|
if (theme->ROUND_CORNERS && current->enabled && r > 0) {
|
|
x -= 1;
|
|
w += 1;
|
|
y -= 1;
|
|
h += 1;
|
|
arm_g2_draw_scaled_render_target(¤t->round_corner_image, x, y, r, r);
|
|
arm_g2_draw_scaled_render_target(¤t->round_corner_image, x, y + h, r, -r);
|
|
arm_g2_draw_scaled_render_target(¤t->round_corner_image, x + w, y, -r, r);
|
|
arm_g2_draw_scaled_render_target(¤t->round_corner_image, x + w, y + h, -r, -r);
|
|
arm_g2_fill_rect(x + r, y, w - r * 2.0, strength);
|
|
arm_g2_fill_rect(x + r, y + h - 1, w - r * 2.0, strength);
|
|
arm_g2_fill_rect(x, y + r, strength, h - r * 2.0);
|
|
arm_g2_fill_rect(x + w - 1, y + r, strength, h - r * 2.0);
|
|
}
|
|
else {
|
|
arm_g2_draw_rect(x, y, w, h, strength);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ui_draw_round_bottom(float x, float y, float w) {
|
|
if (theme->ROUND_CORNERS) {
|
|
int r = current->filled_round_corner_image.width;
|
|
int h = 4;
|
|
y -= 1; // Make it pixel perfect with non-round draw
|
|
h += 1;
|
|
arm_g2_set_color(theme->SEPARATOR_COL);
|
|
arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x, y + h, r, -r);
|
|
arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x + w, y + h, -r, -r);
|
|
arm_g2_fill_rect(x + r, y, w - r * 2.0, h);
|
|
}
|
|
}
|
|
|
|
bool ui_is_char(int code) {
|
|
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
|
|
}
|
|
|
|
int ui_check_start(int i, char *text, char **start, int start_count) {
|
|
for (int x = 0; x < start_count; ++x) {
|
|
if (strncmp(text + i, start[x], strlen(start[x])) == 0) {
|
|
return strlen(start[x]);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ui_text_extract_t ui_extract_coloring(char *text, ui_coloring_t *col) {
|
|
ui_text_extract_t res;
|
|
res.colored[0] = '\0';
|
|
res.uncolored[0] = '\0';
|
|
bool coloring = false;
|
|
int start_from = 0;
|
|
int start_length = 0;
|
|
for (int i = 0; i < strlen(text); ++i) {
|
|
bool skip_first = false;
|
|
// Check if upcoming text should be colored
|
|
int length = ui_check_start(i, text, col->start->buffer, col->start->length);
|
|
// Not touching another character
|
|
bool separated_left = i == 0 || !ui_is_char(text[i - 1]);
|
|
bool separated_right = i + length >= strlen(text) || !ui_is_char(text[i + length]);
|
|
bool is_separated = separated_left && separated_right;
|
|
// Start coloring
|
|
if (length > 0 && (!coloring || col->end[0] == '\0') && (!col->separated || is_separated)) {
|
|
coloring = true;
|
|
start_from = i;
|
|
start_length = length;
|
|
if (col->end[0] != '\0' && col->end[0] != '\n') skip_first = true;
|
|
}
|
|
// End coloring
|
|
else if (col->end[0] == '\0') {
|
|
if (i == start_from + start_length) coloring = false;
|
|
}
|
|
else if (strncmp(text + i, col->end, strlen(col->end)) == 0) {
|
|
coloring = false;
|
|
}
|
|
// If true, add current character to colored string
|
|
int len_c = strlen(res.colored);
|
|
int len_uc = strlen(res.uncolored);
|
|
if (coloring && !skip_first) {
|
|
res.colored[len_c] = text[i];
|
|
res.colored[len_c + 1] = '\0';
|
|
res.uncolored[len_uc] = ' ';
|
|
res.uncolored[len_uc + 1] = '\0';
|
|
}
|
|
else {
|
|
res.colored[len_c] = ' ';
|
|
res.colored[len_c + 1] = '\0';
|
|
res.uncolored[len_uc] = text[i];
|
|
res.uncolored[len_uc + 1] = '\0';
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void ui_draw_string(char *text, float x_offset, float y_offset, int align, bool truncation) {
|
|
static char temp[1024];
|
|
if (text == NULL) {
|
|
return;
|
|
}
|
|
if (truncation) {
|
|
assert(strlen(text) < 1024 - 2);
|
|
char *full_text = text;
|
|
strcpy(temp, text);
|
|
text = &temp[0];
|
|
while (strlen(text) > 0 && arm_g2_string_width(current->ops->font->font_, current->font_size, text) > current->_w - 6.0 * UI_SCALE()) {
|
|
text[strlen(text) - 1] = 0;
|
|
}
|
|
if (strlen(text) < strlen(full_text)) {
|
|
strcat(text, "..");
|
|
// Strip more to fit ".."
|
|
while (strlen(text) > 2 && arm_g2_string_width(current->ops->font->font_, current->font_size, text) > current->_w - 10.0 * UI_SCALE()) {
|
|
text[strlen(text) - 3] = 0;
|
|
strcat(text, "..");
|
|
}
|
|
if (current->is_hovered) {
|
|
ui_tooltip(full_text);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ui_dynamic_glyph_load) {
|
|
int len = strlen(text);
|
|
for (int i = 0; i < len; ++i) {
|
|
if (text[i] > 126 && !arm_g2_font_has_glyph((int)text[i])) {
|
|
int glyph = text[i];
|
|
arm_g2_font_add_glyph(glyph);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (x_offset < 0) {
|
|
x_offset = theme->TEXT_OFFSET;
|
|
}
|
|
x_offset *= UI_SCALE();
|
|
arm_g2_set_font(current->ops->font->font_, current->font_size);
|
|
if (align == UI_ALIGN_CENTER) {
|
|
x_offset = current->_w / 2.0 - arm_g2_string_width(current->ops->font->font_, current->font_size, text) / 2.0;
|
|
}
|
|
else if (align == UI_ALIGN_RIGHT) {
|
|
x_offset = current->_w - arm_g2_string_width(current->ops->font->font_, current->font_size, text) - UI_TEXT_OFFSET();
|
|
}
|
|
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
|
|
if (current->text_coloring == NULL) {
|
|
arm_g2_draw_string(text, current->_x + x_offset, current->_y + current->font_offset_y + y_offset);
|
|
}
|
|
else {
|
|
// Monospace fonts only for now
|
|
strcpy(temp, text);
|
|
for (int i = 0; i < current->text_coloring->colorings->length; ++i) {
|
|
ui_coloring_t *coloring = current->text_coloring->colorings->buffer[i];
|
|
ui_text_extract_t result = ui_extract_coloring(temp, coloring);
|
|
if (result.colored[0] != '\0') {
|
|
arm_g2_set_color(coloring->color);
|
|
arm_g2_draw_string(result.colored, current->_x + x_offset, current->_y + current->font_offset_y + y_offset);
|
|
}
|
|
strcpy(temp, result.uncolored);
|
|
}
|
|
arm_g2_set_color(current->text_coloring->default_color);
|
|
arm_g2_draw_string(temp, current->_x + x_offset, current->_y + current->font_offset_y + y_offset);
|
|
}
|
|
}
|
|
|
|
bool ui_get_initial_hover(float elem_h) {
|
|
if (current->scissor && current->input_y < current->_window_y + current->window_header_h) {
|
|
return false;
|
|
}
|
|
return current->enabled && current->input_enabled &&
|
|
current->input_started_x >= current->_window_x + current->_x && current->input_started_x < (current->_window_x + current->_x + current->_w) &&
|
|
current->input_started_y >= current->_window_y + current->_y && current->input_started_y < (current->_window_y + current->_y + elem_h);
|
|
}
|
|
|
|
bool ui_get_hover(float elem_h) {
|
|
if (current->scissor && current->input_y < current->_window_y + current->window_header_h) {
|
|
return false;
|
|
}
|
|
current->is_hovered = current->enabled && current->input_enabled &&
|
|
current->input_x >= current->_window_x + current->_x && current->input_x < (current->_window_x + current->_x + current->_w) &&
|
|
current->input_y >= current->_window_y + current->_y && current->input_y < (current->_window_y + current->_y + elem_h);
|
|
return current->is_hovered;
|
|
}
|
|
|
|
bool ui_get_released(float elem_h) { // Input selection
|
|
current->is_released = current->enabled && current->input_enabled && current->input_released && ui_get_hover(elem_h) && ui_get_initial_hover(elem_h);
|
|
return current->is_released;
|
|
}
|
|
|
|
bool ui_get_pushed(float elem_h) {
|
|
current->is_pushed = current->enabled && current->input_enabled && current->input_down && ui_get_hover(elem_h) && ui_get_initial_hover(elem_h);
|
|
return current->is_pushed;
|
|
}
|
|
|
|
bool ui_get_started(float elem_h) {
|
|
current->is_started = current->enabled && current->input_enabled && current->input_started && ui_get_hover(elem_h);
|
|
return current->is_started;
|
|
}
|
|
|
|
bool ui_is_visible(float elem_h) {
|
|
if (current->current_window == NULL) return true;
|
|
return (current->_y + elem_h > current->window_header_h && current->_y < current->current_window->texture.height);
|
|
}
|
|
|
|
float ui_get_ratio(float ratio, float dyn) {
|
|
return ratio < 0 ? -ratio : ratio * dyn;
|
|
}
|
|
|
|
// Draw the upcoming elements in the same row
|
|
// Negative values will be treated as absolute, positive values as ratio to `window width`
|
|
void ui_row(f32_array_t *ratios) {
|
|
if (ratios->length == 0) {
|
|
current->ratios = NULL;
|
|
return;
|
|
}
|
|
current->ratios = ratios;
|
|
current->current_ratio = 0;
|
|
current->x_before_split = current->_x;
|
|
current->w_before_split = current->_w;
|
|
current->_w = ui_get_ratio(ratios->buffer[current->current_ratio], current->_w);
|
|
}
|
|
|
|
void ui_indent() {
|
|
current->_x += UI_TAB_W();
|
|
current->_w -= UI_TAB_W();
|
|
}
|
|
|
|
void ui_unindent() {
|
|
current->_x -= UI_TAB_W();
|
|
current->_w += UI_TAB_W();
|
|
}
|
|
|
|
void ui_end_element_of_size(float element_size) {
|
|
if (current->current_window == NULL || current->current_window->layout == UI_LAYOUT_VERTICAL) {
|
|
if (current->current_ratio == -1 || (current->ratios != NULL && current->current_ratio == current->ratios->length - 1)) { // New line
|
|
current->_y += element_size;
|
|
|
|
if ((current->ratios != NULL && current->current_ratio == current->ratios->length - 1)) { // Last row element
|
|
current->current_ratio = -1;
|
|
current->ratios = NULL;
|
|
current->_x = current->x_before_split;
|
|
current->_w = current->w_before_split;
|
|
}
|
|
}
|
|
else { // Row
|
|
current->current_ratio++;
|
|
current->_x += current->_w; // More row elements to place
|
|
current->_w = ui_get_ratio(current->ratios->buffer[current->current_ratio], current->w_before_split);
|
|
}
|
|
}
|
|
else { // Horizontal
|
|
current->_x += current->_w + UI_ELEMENT_OFFSET();
|
|
}
|
|
}
|
|
|
|
void ui_end_element() {
|
|
ui_end_element_of_size(UI_ELEMENT_H() + UI_ELEMENT_OFFSET());
|
|
}
|
|
|
|
void ui_resize(ui_handle_t *handle, int w, int h) {
|
|
handle->redraws = 2;
|
|
if (handle->texture.width != 0) {
|
|
kinc_g4_render_target_destroy(&handle->texture);
|
|
}
|
|
if (w < 1) {
|
|
w = 1;
|
|
}
|
|
if (h < 1) {
|
|
h = 1;
|
|
}
|
|
kinc_g4_render_target_init(&handle->texture, w, h, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0);
|
|
}
|
|
|
|
bool ui_input_in_rect(float x, float y, float w, float h) {
|
|
return current->enabled && current->input_enabled &&
|
|
current->input_x >= x && current->input_x < (x + w) &&
|
|
current->input_y >= y && current->input_y < (y + h);
|
|
}
|
|
|
|
bool ui_input_changed() {
|
|
return current->input_dx != 0 || current->input_dy != 0 || current->input_wheel_delta != 0 || current->input_started || current->input_started_r || current->input_released || current->input_released_r || current->input_down || current->input_down_r || current->is_key_pressed;
|
|
}
|
|
|
|
void ui_end_input() {
|
|
if (ui_on_tab_drop != NULL && current->drag_tab_handle != NULL) {
|
|
if (current->input_dx != 0 || current->input_dy != 0) {
|
|
kinc_mouse_set_cursor(1); // Hand
|
|
}
|
|
if (current->input_released) {
|
|
kinc_mouse_set_cursor(0); // Default
|
|
current->drag_tab_handle = NULL;
|
|
}
|
|
}
|
|
|
|
current->is_key_pressed = false;
|
|
current->input_started = false;
|
|
current->input_started_r = false;
|
|
current->input_released = false;
|
|
current->input_released_r = false;
|
|
current->input_dx = 0;
|
|
current->input_dy = 0;
|
|
current->input_wheel_delta = 0;
|
|
current->pen_in_use = false;
|
|
if (ui_key_repeat && current->is_key_down && kinc_time() - ui_key_repeat_time > 0.05) {
|
|
if (current->key_code == KINC_KEY_BACKSPACE || current->key_code == KINC_KEY_DELETE || current->key_code == KINC_KEY_LEFT || current->key_code == KINC_KEY_RIGHT || current->key_code == KINC_KEY_UP || current->key_code == KINC_KEY_DOWN) {
|
|
ui_key_repeat_time = kinc_time();
|
|
current->is_key_pressed = true;
|
|
}
|
|
}
|
|
if (ui_touch_hold && current->input_down && current->input_x == current->input_started_x && current->input_y == current->input_started_y && current->input_started_time > 0 && kinc_time() - current->input_started_time > 0.7) {
|
|
current->touch_hold_activated = true;
|
|
current->input_released_r = true;
|
|
current->input_started_time = 0;
|
|
}
|
|
}
|
|
|
|
void ui_scroll(float delta) {
|
|
current->current_window->scroll_offset -= delta;
|
|
}
|
|
|
|
int ui_line_count(char *str) {
|
|
if (str == NULL) return 0;
|
|
int i = 0;
|
|
int count = 1;
|
|
while (str[i] != '\0') {
|
|
if (str[i] == '\n') count++;
|
|
i++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
char *ui_extract_line(char *str, int line) {
|
|
static char temp[1024];
|
|
int pos = 0;
|
|
int len = strlen(str);
|
|
int line_i = 0;
|
|
for (int i = 0; i < len; ++i) {
|
|
if (str[i] == '\n') {
|
|
line_i++; continue;
|
|
}
|
|
if (line_i < line) {
|
|
continue;
|
|
}
|
|
if (line_i > line) {
|
|
break;
|
|
}
|
|
temp[pos++] = str[i];
|
|
}
|
|
temp[pos] = 0;
|
|
return temp;
|
|
}
|
|
|
|
char *ui_lower_case(char *dest, char *src) {
|
|
int len = strlen(src);
|
|
assert(len < 1024);
|
|
for (int i = 0; i < len; ++i) {
|
|
dest[i] = tolower(src[i]);
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
void ui_draw_tooltip_text(bool bind_global_g) {
|
|
int line_count = ui_line_count(current->tooltip_text);
|
|
float tooltip_w = 0.0;
|
|
for (int i = 0; i < line_count; ++i) {
|
|
float line_tooltip_w = arm_g2_string_width(current->ops->font->font_, current->font_size, ui_extract_line(current->tooltip_text, i));
|
|
if (line_tooltip_w > tooltip_w) {
|
|
tooltip_w = line_tooltip_w;
|
|
}
|
|
}
|
|
current->tooltip_x = fmin(current->tooltip_x, kinc_window_width(0) - tooltip_w - 20);
|
|
if (bind_global_g) arm_g2_restore_render_target();
|
|
float font_height = arm_g2_font_height(current->ops->font->font_, current->font_size);
|
|
float off = 0;
|
|
if (current->tooltip_img != NULL) {
|
|
float w = current->tooltip_img->tex_width;
|
|
if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) {
|
|
w = current->tooltip_img_max_width;
|
|
}
|
|
off = current->tooltip_img->tex_height * (w / current->tooltip_img->tex_width);
|
|
}
|
|
else if (current->tooltip_rt != NULL) {
|
|
float w = current->tooltip_rt->width;
|
|
if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) {
|
|
w = current->tooltip_img_max_width;
|
|
}
|
|
off = current->tooltip_rt->height * (w / current->tooltip_rt->width);
|
|
}
|
|
|
|
int x = current->tooltip_x - 5;
|
|
int y = current->tooltip_y + off - 5;
|
|
int w = tooltip_w + 20 + 10;
|
|
int h = font_height * line_count + 10;
|
|
ui_draw_shadow(x, y, w, h);
|
|
arm_g2_set_color(theme->SEPARATOR_COL);
|
|
arm_g2_fill_rect(x, y, w, h);
|
|
|
|
arm_g2_set_font(current->ops->font->font_, current->font_size);
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
for (int i = 0; i < line_count; ++i) {
|
|
arm_g2_draw_string(ui_extract_line(current->tooltip_text, i), current->tooltip_x + 5, current->tooltip_y + off + i * current->font_size);
|
|
}
|
|
}
|
|
|
|
void ui_draw_tooltip_image(bool bind_global_g) {
|
|
float w = current->tooltip_img->tex_width;
|
|
if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) {
|
|
w = current->tooltip_img_max_width;
|
|
}
|
|
float h = current->tooltip_img->tex_height * (w / current->tooltip_img->tex_width);
|
|
current->tooltip_x = fmin(current->tooltip_x, kinc_window_width(0) - w - 20);
|
|
current->tooltip_y = fmin(current->tooltip_y, kinc_window_height(0) - h - 20);
|
|
if (bind_global_g) {
|
|
arm_g2_restore_render_target();
|
|
}
|
|
arm_g2_set_color(0xff000000);
|
|
arm_g2_fill_rect(current->tooltip_x, current->tooltip_y, w, h);
|
|
arm_g2_set_color(0xffffffff);
|
|
current->tooltip_invert_y ?
|
|
arm_g2_draw_scaled_image(current->tooltip_img, current->tooltip_x, current->tooltip_y + h, w, -h) :
|
|
arm_g2_draw_scaled_image(current->tooltip_img, current->tooltip_x, current->tooltip_y, w, h);
|
|
}
|
|
|
|
void ui_draw_tooltip_rt(bool bind_global_g) {
|
|
float w = current->tooltip_rt->width;
|
|
if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) {
|
|
w = current->tooltip_img_max_width;
|
|
}
|
|
float h = current->tooltip_rt->height * (w / current->tooltip_rt->width);
|
|
current->tooltip_x = fmin(current->tooltip_x, kinc_window_width(0) - w - 20);
|
|
current->tooltip_y = fmin(current->tooltip_y, kinc_window_height(0) - h - 20);
|
|
if (bind_global_g) {
|
|
arm_g2_restore_render_target();
|
|
}
|
|
arm_g2_set_color(0xff000000);
|
|
arm_g2_fill_rect(current->tooltip_x, current->tooltip_y, w, h);
|
|
arm_g2_set_color(0xffffffff);
|
|
current->tooltip_invert_y ?
|
|
arm_g2_draw_scaled_render_target(current->tooltip_rt, current->tooltip_x, current->tooltip_y + h, w, -h) :
|
|
arm_g2_draw_scaled_render_target(current->tooltip_rt, current->tooltip_x, current->tooltip_y, w, h);
|
|
}
|
|
|
|
void ui_draw_tooltip(bool bind_global_g) {
|
|
static char temp[1024];
|
|
if (current->slider_tooltip) {
|
|
if (bind_global_g) {
|
|
arm_g2_restore_render_target();
|
|
}
|
|
arm_g2_set_font(current->ops->font->font_, current->font_size * 2);
|
|
sprintf(temp, "%f", round(current->scroll_handle->value * 100.0) / 100.0);
|
|
string_strip_trailing_zeros(temp);
|
|
char *text = temp;
|
|
float x_off = arm_g2_string_width(current->ops->font->font_, current->font_size * 2.0, text) / 2.0;
|
|
float y_off = arm_g2_font_height(current->ops->font->font_, current->font_size * 2.0);
|
|
float x = fmin(fmax(current->slider_tooltip_x, current->input_x), current->slider_tooltip_x + current->slider_tooltip_w);
|
|
arm_g2_set_color(theme->BUTTON_COL);
|
|
arm_g2_fill_rect(x - x_off, current->slider_tooltip_y - y_off, x_off * 2.0, y_off);
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
arm_g2_draw_string(text, x - x_off, current->slider_tooltip_y - y_off);
|
|
}
|
|
if (ui_touch_tooltip && current->text_selected_handle != NULL) {
|
|
if (bind_global_g) {
|
|
arm_g2_restore_render_target();
|
|
}
|
|
arm_g2_set_font(current->ops->font->font_, current->font_size * 2.0);
|
|
float x_off = arm_g2_string_width(current->ops->font->font_, current->font_size * 2.0, current->text_selected) / 2.0;
|
|
float y_off = arm_g2_font_height(current->ops->font->font_, current->font_size * 2.0) / 2.0;
|
|
float x = kinc_window_width(0) / 2.0;
|
|
float y = kinc_window_height(0) / 3.0;
|
|
arm_g2_set_color(theme->BUTTON_COL);
|
|
arm_g2_fill_rect(x - x_off, y - y_off, x_off * 2.0, y_off * 2.0);
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
arm_g2_draw_string(current->text_selected, x - x_off, y - y_off);
|
|
}
|
|
|
|
if (current->tooltip_text[0] != '\0' || current->tooltip_img != NULL || current->tooltip_rt != NULL) {
|
|
if (ui_input_changed()) {
|
|
current->tooltip_shown = false;
|
|
current->tooltip_wait = current->input_dx == 0 && current->input_dy == 0; // Wait for movement before showing up again
|
|
}
|
|
if (!current->tooltip_shown) {
|
|
current->tooltip_shown = true;
|
|
current->tooltip_x = current->input_x;
|
|
current->tooltip_time = kinc_time();
|
|
}
|
|
if (!current->tooltip_wait && kinc_time() - current->tooltip_time > UI_TOOLTIP_DELAY()) {
|
|
if (current->tooltip_img != NULL) {
|
|
ui_draw_tooltip_image(bind_global_g);
|
|
}
|
|
else if (current->tooltip_rt != NULL) {
|
|
ui_draw_tooltip_rt(bind_global_g);
|
|
}
|
|
if (current->tooltip_text[0] != '\0') {
|
|
ui_draw_tooltip_text(bind_global_g);
|
|
}
|
|
}
|
|
}
|
|
else current->tooltip_shown = false;
|
|
}
|
|
|
|
void ui_draw_combo(bool begin /*= true*/) {
|
|
if (current->combo_selected_handle == NULL) {
|
|
return;
|
|
}
|
|
arm_g2_set_color(theme->SEPARATOR_COL);
|
|
if (begin) {
|
|
arm_g2_restore_render_target();
|
|
}
|
|
|
|
float combo_h = (current->combo_selected_texts->length + (current->combo_selected_label != NULL ? 1 : 0) + (current->combo_search_bar ? 1 : 0)) * UI_ELEMENT_H();
|
|
float dist_top = current->combo_selected_y - combo_h - UI_ELEMENT_H() - current->window_border_top;
|
|
float dist_bottom = kinc_window_height(0) - current->window_border_bottom - (current->combo_selected_y + combo_h );
|
|
bool unroll_up = dist_bottom < 0 && dist_bottom < dist_top;
|
|
|
|
ui_begin_region(current, current->combo_selected_x, current->combo_selected_y, current->combo_selected_w);
|
|
|
|
if (unroll_up) {
|
|
float off = current->combo_selected_label != NULL ? UI_ELEMENT_H() / UI_SCALE() : 0.0;
|
|
ui_draw_shadow(current->_x, current->_y - combo_h - off, current->_w, combo_h);
|
|
}
|
|
else {
|
|
ui_draw_shadow(current->_x, current->_y, current->_w, combo_h);
|
|
}
|
|
|
|
if (current->is_key_pressed || current->input_wheel_delta != 0) {
|
|
int arrow_up = current->is_key_pressed && current->key_code == (unroll_up ? KINC_KEY_DOWN : KINC_KEY_UP);
|
|
int arrow_down = current->is_key_pressed && current->key_code == (unroll_up ? KINC_KEY_UP : KINC_KEY_DOWN);
|
|
int wheel_up = (unroll_up && current->input_wheel_delta > 0) || (!unroll_up && current->input_wheel_delta < 0);
|
|
int wheel_down = (unroll_up && current->input_wheel_delta < 0) || (!unroll_up && current->input_wheel_delta > 0);
|
|
if ((arrow_up || wheel_up) && current->combo_to_submit > 0) {
|
|
int step = 1;
|
|
if (current->combo_search_bar && strlen(current->text_selected) > 0) {
|
|
char search[512];
|
|
char str[512];
|
|
ui_lower_case(search, current->text_selected);
|
|
while (true) {
|
|
ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit - step]);
|
|
if (strstr(str, search) == NULL && current->combo_to_submit - step > 0) {
|
|
++step;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Corner case: current position is the top one according to the search pattern
|
|
ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit - step]);
|
|
if (strstr(str, search) == NULL) {
|
|
step = 0;
|
|
}
|
|
}
|
|
current->combo_to_submit -= step;
|
|
current->submit_combo_handle = current->combo_selected_handle;
|
|
}
|
|
else if ((arrow_down || wheel_down) && current->combo_to_submit < current->combo_selected_texts->length - 1) {
|
|
int step = 1;
|
|
if (current->combo_search_bar && strlen(current->text_selected) > 0) {
|
|
char search[512];
|
|
char str[512];
|
|
ui_lower_case(search, current->text_selected);
|
|
while (true) {
|
|
ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit + step]);
|
|
if (strstr(str, search) == NULL && current->combo_to_submit + step > 0) {
|
|
++step;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Corner case: current position is the top one according to the search pattern
|
|
ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit + step]);
|
|
if (strstr(str, search) == NULL) {
|
|
step = 0;
|
|
}
|
|
}
|
|
|
|
current->combo_to_submit += step;
|
|
current->submit_combo_handle = current->combo_selected_handle;
|
|
}
|
|
if (current->combo_selected_window != NULL) {
|
|
current->combo_selected_window->redraws = 2;
|
|
}
|
|
}
|
|
|
|
current->input_enabled = true;
|
|
int _BUTTON_COL = theme->BUTTON_COL;
|
|
int _ELEMENT_OFFSET = theme->ELEMENT_OFFSET;
|
|
theme->ELEMENT_OFFSET = 0;
|
|
float unroll_right = current->_x + current->combo_selected_w * 2.0 < kinc_window_width(0) - current->window_border_right ? 1 : -1;
|
|
bool reset_position = false;
|
|
char search[512];
|
|
search[0] = '\0';
|
|
if (current->combo_search_bar) {
|
|
if (unroll_up) {
|
|
current->_y -= UI_ELEMENT_H() * 2.0;
|
|
}
|
|
if (ui_combo_first) {
|
|
ui_combo_search_handle->text = "";
|
|
}
|
|
ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL);
|
|
strcpy(search, ui_text_input(ui_combo_search_handle, "", UI_ALIGN_LEFT, true, true));
|
|
ui_lower_case(search, search);
|
|
if (current->is_released) {
|
|
ui_combo_first = true; // Keep combo open
|
|
}
|
|
if (ui_combo_first) {
|
|
#if !defined(KINC_ANDROID) && !defined(KINC_IOS)
|
|
ui_start_text_edit(ui_combo_search_handle, UI_ALIGN_LEFT); // Focus search bar
|
|
#endif
|
|
}
|
|
reset_position = ui_combo_search_handle->changed;
|
|
}
|
|
|
|
for (int i = 0; i < current->combo_selected_texts->length; ++i) {
|
|
char str[512];
|
|
ui_lower_case(str, current->combo_selected_texts->buffer[i]);
|
|
if (strlen(search) > 0 && strstr(str, search) == NULL) {
|
|
continue; // Don't show items that don't fit the current search pattern
|
|
}
|
|
|
|
if (reset_position) { // The search has changed, select first entry that matches
|
|
current->combo_to_submit = current->combo_selected_handle->position = i;
|
|
current->submit_combo_handle = current->combo_selected_handle;
|
|
reset_position = false;
|
|
}
|
|
if (unroll_up) {
|
|
current->_y -= UI_ELEMENT_H() * 2.0;
|
|
}
|
|
theme->BUTTON_COL = i == current->combo_selected_handle->position ?
|
|
theme->HIGHLIGHT_COL :
|
|
theme->SEPARATOR_COL;
|
|
ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL);
|
|
if (ui_button(current->combo_selected_texts->buffer[i], current->combo_selected_align, "")) {
|
|
current->combo_to_submit = i;
|
|
current->submit_combo_handle = current->combo_selected_handle;
|
|
if (current->combo_selected_window != NULL) {
|
|
current->combo_selected_window->redraws = 2;
|
|
}
|
|
break;
|
|
}
|
|
if (current->_y + UI_ELEMENT_H() > kinc_window_height(0) - current->window_border_bottom || current->_y - UI_ELEMENT_H() * 2 < current->window_border_top) {
|
|
current->_x += current->combo_selected_w * unroll_right; // Next column
|
|
current->_y = current->combo_selected_y;
|
|
}
|
|
}
|
|
theme->BUTTON_COL = _BUTTON_COL;
|
|
theme->ELEMENT_OFFSET = _ELEMENT_OFFSET;
|
|
|
|
if (current->combo_selected_label != NULL) { // Unroll down
|
|
if (unroll_up) {
|
|
current->_y -= UI_ELEMENT_H() * 2.0;
|
|
ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL);
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
ui_draw_string(current->combo_selected_label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true);
|
|
current->_y += UI_ELEMENT_H();
|
|
ui_fill(0, 0, current->_w / UI_SCALE(), 1.0 * UI_SCALE(), theme->ACCENT_COL); // Separator
|
|
}
|
|
else {
|
|
ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL);
|
|
ui_fill(0, 0, current->_w / UI_SCALE(), 1.0 * UI_SCALE(), theme->ACCENT_COL); // Separator
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
ui_draw_string(current->combo_selected_label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true);
|
|
current->_y += UI_ELEMENT_H();
|
|
ui_draw_round_bottom(current->_x, current->_y - 1, current->_w);
|
|
}
|
|
}
|
|
|
|
if ((current->input_released || current->input_released_r || current->is_escape_down || current->is_return_down) && !ui_combo_first) {
|
|
current->combo_selected_handle = NULL;
|
|
ui_combo_first = true;
|
|
}
|
|
else {
|
|
ui_combo_first = false;
|
|
}
|
|
current->input_enabled = current->combo_selected_handle == NULL;
|
|
ui_end_region(false);
|
|
}
|
|
|
|
void ui_bake_elements() {
|
|
if (current->check_select_image.width != 0) {
|
|
kinc_g4_render_target_destroy(¤t->check_select_image);
|
|
}
|
|
float r = UI_CHECK_SELECT_SIZE();
|
|
kinc_g4_render_target_init(¤t->check_select_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0);
|
|
arm_g2_set_render_target(¤t->check_select_image);
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0);
|
|
arm_g2_set_color(0xffffffff);
|
|
arm_g2_draw_line(0, r / 2.0, r / 2.0 - 2.0 * UI_SCALE(), r - 2.0 * UI_SCALE(), 2.0 * UI_SCALE());
|
|
arm_g2_draw_line(r / 2.0 - 3.0 * UI_SCALE(), r - 3.0 * UI_SCALE(), r / 2.0 + 5.0 * UI_SCALE(), r - 11.0 * UI_SCALE(), 2.0 * UI_SCALE());
|
|
arm_g2_end();
|
|
|
|
if (current->radio_image.width != 0) {
|
|
kinc_g4_render_target_destroy(¤t->radio_image);
|
|
}
|
|
r = UI_CHECK_SIZE();
|
|
kinc_g4_render_target_init(¤t->radio_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0);
|
|
arm_g2_set_render_target(¤t->radio_image);
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0);
|
|
arm_g2_set_color(0xffaaaaaa);
|
|
arm_g2_fill_circle(r / 2.0, r / 2.0, r / 2.0, 0);
|
|
arm_g2_set_color(0xffffffff);
|
|
arm_g2_draw_circle(r / 2.0, r / 2.0, r / 2.0, 0, 1.0 * UI_SCALE());
|
|
arm_g2_end();
|
|
|
|
if (current->radio_select_image.width != 0) {
|
|
kinc_g4_render_target_destroy(¤t->radio_select_image);
|
|
}
|
|
r = UI_CHECK_SELECT_SIZE();
|
|
kinc_g4_render_target_init(¤t->radio_select_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0);
|
|
arm_g2_set_render_target(¤t->radio_select_image);
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0);
|
|
arm_g2_set_color(0xffaaaaaa);
|
|
arm_g2_fill_circle(r / 2.0, r / 2.0, 4.5 * UI_SCALE(), 0);
|
|
arm_g2_set_color(0xffffffff);
|
|
arm_g2_fill_circle(r / 2.0, r / 2.0, 4.0 * UI_SCALE(), 0);
|
|
arm_g2_end();
|
|
|
|
if (theme->ROUND_CORNERS) {
|
|
if (current->filled_round_corner_image.width != 0) {
|
|
kinc_g4_render_target_destroy(¤t->filled_round_corner_image);
|
|
}
|
|
r = 4.0 * UI_SCALE();
|
|
kinc_g4_render_target_init(¤t->filled_round_corner_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0);
|
|
arm_g2_set_render_target(¤t->filled_round_corner_image);
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0);
|
|
arm_g2_set_color(0xffffffff);
|
|
arm_g2_fill_circle(r, r, r, 0);
|
|
arm_g2_end();
|
|
|
|
if (current->round_corner_image.width != 0) {
|
|
kinc_g4_render_target_destroy(¤t->round_corner_image);
|
|
}
|
|
kinc_g4_render_target_init(¤t->round_corner_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0);
|
|
arm_g2_set_render_target(¤t->round_corner_image);
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0);
|
|
arm_g2_set_color(0xffffffff);
|
|
arm_g2_draw_circle(r, r, r, 0, 1);
|
|
arm_g2_end();
|
|
}
|
|
|
|
arm_g2_restore_render_target();
|
|
current->elements_baked = true;
|
|
}
|
|
|
|
void ui_begin_region(ui_t *ui, int x, int y, int w) {
|
|
ui_set_current(ui);
|
|
if (!current->elements_baked) {
|
|
ui_bake_elements();
|
|
}
|
|
current->changed = false;
|
|
current->current_window = NULL;
|
|
current->tooltip_text[0] = '\0';
|
|
current->tooltip_img = NULL;
|
|
current->tooltip_rt = NULL;
|
|
current->_window_x = 0;
|
|
current->_window_y = 0;
|
|
current->_window_w = w;
|
|
current->_x = x;
|
|
current->_y = y;
|
|
current->_w = w;
|
|
current->_h = 0;
|
|
}
|
|
|
|
void ui_end_region(bool last) {
|
|
ui_draw_tooltip(false);
|
|
current->tab_pressed_handle = NULL;
|
|
if (last) {
|
|
ui_draw_combo(false); // Handle active combo
|
|
ui_end_input();
|
|
}
|
|
}
|
|
|
|
void ui_set_cursor_to_input(int align) {
|
|
float off = align == UI_ALIGN_LEFT ? UI_TEXT_OFFSET() : current->_w - arm_g2_string_width(current->ops->font->font_, current->font_size, current->text_selected);
|
|
float x = current->input_x - (current->_window_x + current->_x + off);
|
|
current->cursor_x = 0;
|
|
while (current->cursor_x < strlen(current->text_selected) && arm_g2_sub_string_width(current->ops->font->font_, current->font_size, current->text_selected, 0, current->cursor_x) < x) {
|
|
current->cursor_x++;
|
|
}
|
|
current->highlight_anchor = current->cursor_x;
|
|
}
|
|
|
|
void ui_start_text_edit(ui_handle_t *handle, int align) {
|
|
current->is_typing = true;
|
|
current->submit_text_handle = current->text_selected_handle;
|
|
strcpy(current->text_to_submit, current->text_selected);
|
|
current->text_selected_handle = handle;
|
|
strcpy(current->text_selected, handle->text);
|
|
current->cursor_x = strlen(handle->text);
|
|
if (current->tab_pressed) {
|
|
current->tab_pressed = false;
|
|
current->is_key_pressed = false; // Prevent text deselect after tab press
|
|
}
|
|
else if (!current->highlight_on_select) { // Set cursor to click location
|
|
ui_set_cursor_to_input(align);
|
|
}
|
|
current->tab_pressed_handle = handle;
|
|
current->highlight_anchor = current->highlight_on_select ? 0 : current->cursor_x;
|
|
kinc_keyboard_show();
|
|
}
|
|
|
|
void ui_submit_text_edit() {
|
|
current->changed = strcmp(current->submit_text_handle->text, current->text_to_submit) != 0;
|
|
current->submit_text_handle->changed = current->changed;
|
|
current->submit_text_handle->text = string_copy(current->text_to_submit);
|
|
current->submit_text_handle = NULL;
|
|
current->text_to_submit[0] = '\0';
|
|
current->text_selected[0] = '\0';
|
|
}
|
|
|
|
void ui_deselect_text(ui_t *ui) {
|
|
if (ui->text_selected_handle == NULL) {
|
|
return;
|
|
}
|
|
ui->submit_text_handle = ui->text_selected_handle;
|
|
strcpy(ui->text_to_submit, ui->text_selected);
|
|
ui->text_selected_handle = NULL;
|
|
ui->is_typing = false;
|
|
if (ui->current_window != NULL) {
|
|
ui->current_window->redraws = 2;
|
|
}
|
|
kinc_keyboard_hide();
|
|
ui->highlight_anchor = ui->cursor_x;
|
|
if (ui_on_deselect_text != NULL) {
|
|
ui_on_deselect_text();
|
|
}
|
|
}
|
|
|
|
void ui_remove_char_at(char *str, int at) {
|
|
int len = strlen(str);
|
|
for (int i = at; i < len; ++i) {
|
|
str[i] = str[i + 1];
|
|
}
|
|
}
|
|
|
|
void ui_remove_chars_at(char *str, int at, int count) {
|
|
for (int i = 0; i < count; ++i) {
|
|
ui_remove_char_at(str, at);
|
|
}
|
|
}
|
|
|
|
void ui_insert_char_at(char *str, int at, char c) {
|
|
int len = strlen(str);
|
|
for (int i = len + 1; i > at; --i) {
|
|
str[i] = str[i - 1];
|
|
}
|
|
str[at] = c;
|
|
}
|
|
|
|
void ui_insert_chars_at(char *str, int at, char *cs) {
|
|
int len = strlen(cs);
|
|
for (int i = 0; i < len; ++i) {
|
|
ui_insert_char_at(str, at + i, cs[i]);
|
|
}
|
|
}
|
|
|
|
void ui_update_text_edit(int align, bool editable, bool live_update) {
|
|
char text[256];
|
|
strcpy(text, current->text_selected);
|
|
if (current->is_key_pressed) { // Process input
|
|
if (current->key_code == KINC_KEY_LEFT) { // Move cursor
|
|
if (current->cursor_x > 0) {
|
|
current->cursor_x--;
|
|
}
|
|
}
|
|
else if (current->key_code == KINC_KEY_RIGHT) {
|
|
if (current->cursor_x < strlen(text)) {
|
|
current->cursor_x++;
|
|
}
|
|
}
|
|
else if (editable && current->key_code == KINC_KEY_BACKSPACE) { // Remove char
|
|
if (current->cursor_x > 0 && current->highlight_anchor == current->cursor_x) {
|
|
ui_remove_char_at(text, current->cursor_x - 1);
|
|
current->cursor_x--;
|
|
}
|
|
else if (current->highlight_anchor < current->cursor_x) {
|
|
int count = current->cursor_x - current->highlight_anchor;
|
|
ui_remove_chars_at(text, current->highlight_anchor, count);
|
|
current->cursor_x = current->highlight_anchor;
|
|
}
|
|
else {
|
|
int count = current->highlight_anchor - current->cursor_x;
|
|
ui_remove_chars_at(text, current->cursor_x, count);
|
|
}
|
|
}
|
|
else if (editable && current->key_code == KINC_KEY_DELETE) {
|
|
if (current->highlight_anchor == current->cursor_x) {
|
|
ui_remove_char_at(text, current->cursor_x);
|
|
}
|
|
else if (current->highlight_anchor < current->cursor_x) {
|
|
int count = current->cursor_x - current->highlight_anchor;
|
|
ui_remove_chars_at(text, current->highlight_anchor, count);
|
|
current->cursor_x = current->highlight_anchor;
|
|
}
|
|
else {
|
|
int count = current->highlight_anchor - current->cursor_x;
|
|
ui_remove_chars_at(text, current->cursor_x, count);
|
|
}
|
|
}
|
|
else if (current->key_code == KINC_KEY_RETURN) { // Deselect
|
|
ui_deselect_text(current);
|
|
}
|
|
else if (current->key_code == KINC_KEY_ESCAPE) { // Cancel
|
|
strcpy(current->text_selected, current->text_selected_handle->text);
|
|
ui_deselect_text(current);
|
|
}
|
|
else if (current->key_code == KINC_KEY_TAB && current->tab_switch_enabled && !current->is_ctrl_down) { // Next field
|
|
current->tab_pressed = true;
|
|
ui_deselect_text(current);
|
|
current->key_code = 0;
|
|
}
|
|
else if (current->key_code == KINC_KEY_HOME) {
|
|
current->cursor_x = 0;
|
|
}
|
|
else if (current->key_code == KINC_KEY_END) {
|
|
current->cursor_x = strlen(text);
|
|
}
|
|
else if (current->is_ctrl_down && current->is_a_down) { // Select all
|
|
current->cursor_x = strlen(text);
|
|
current->highlight_anchor = 0;
|
|
}
|
|
else if (editable && // Write
|
|
current->key_code != KINC_KEY_SHIFT &&
|
|
current->key_code != KINC_KEY_CAPS_LOCK &&
|
|
current->key_code != KINC_KEY_CONTROL &&
|
|
current->key_code != KINC_KEY_META &&
|
|
current->key_code != KINC_KEY_ALT &&
|
|
current->key_code != KINC_KEY_UP &&
|
|
current->key_code != KINC_KEY_DOWN &&
|
|
current->key_char >= 32) {
|
|
ui_remove_chars_at(text, current->highlight_anchor, current->cursor_x - current->highlight_anchor);
|
|
ui_insert_char_at(text, current->highlight_anchor, current->key_char);
|
|
|
|
current->cursor_x = current->cursor_x + 1 > strlen(text) ? strlen(text) : current->cursor_x + 1;
|
|
}
|
|
bool selecting = current->is_shift_down && (current->key_code == KINC_KEY_LEFT || current->key_code == KINC_KEY_RIGHT || current->key_code == KINC_KEY_SHIFT);
|
|
// isCtrlDown && isAltDown is the condition for AltGr was pressed
|
|
// AltGr is part of the German keyboard layout and part of key combinations like AltGr + e -> €
|
|
if (!selecting && (!current->is_ctrl_down || (current->is_ctrl_down && current->is_alt_down))) {
|
|
current->highlight_anchor = current->cursor_x;
|
|
}
|
|
}
|
|
|
|
if (editable && ui_text_to_paste[0] != '\0') { // Process cut copy paste
|
|
ui_remove_chars_at(text, current->highlight_anchor, current->cursor_x - current->highlight_anchor);
|
|
ui_insert_chars_at(text, current->highlight_anchor, ui_text_to_paste);
|
|
current->cursor_x += strlen(ui_text_to_paste);
|
|
current->highlight_anchor = current->cursor_x;
|
|
ui_text_to_paste[0] = 0;
|
|
ui_is_paste = false;
|
|
}
|
|
if (current->highlight_anchor == current->cursor_x) {
|
|
strcpy(ui_text_to_copy, text); // Copy
|
|
}
|
|
else if (current->highlight_anchor < current->cursor_x) {
|
|
int len = current->cursor_x - current->highlight_anchor;
|
|
strncpy(ui_text_to_copy, text + current->highlight_anchor, len);
|
|
ui_text_to_copy[len] = '\0';
|
|
}
|
|
else {
|
|
int len = current->highlight_anchor - current->cursor_x;
|
|
strncpy(ui_text_to_copy, text + current->cursor_x, len);
|
|
ui_text_to_copy[len] = '\0';
|
|
}
|
|
if (editable && ui_is_cut) { // Cut
|
|
if (current->highlight_anchor == current->cursor_x) {
|
|
text[0] = '\0';
|
|
}
|
|
else if (current->highlight_anchor < current->cursor_x) {
|
|
ui_remove_chars_at(text, current->highlight_anchor, current->cursor_x - current->highlight_anchor);
|
|
current->cursor_x = current->highlight_anchor;
|
|
}
|
|
else {
|
|
ui_remove_chars_at(text, current->cursor_x, current->highlight_anchor - current->cursor_x);
|
|
}
|
|
}
|
|
|
|
float off = UI_TEXT_OFFSET();
|
|
float line_height = UI_ELEMENT_H();
|
|
float cursor_height = line_height - current->button_offset_y * 3.0;
|
|
// Draw highlight
|
|
if (current->highlight_anchor != current->cursor_x) {
|
|
float istart = current->cursor_x;
|
|
float iend = current->highlight_anchor;
|
|
if (current->highlight_anchor < current->cursor_x) {
|
|
istart = current->highlight_anchor;
|
|
iend = current->cursor_x;
|
|
}
|
|
|
|
float hlstrw = arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, istart, iend);
|
|
float start_off = arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, 0, istart);
|
|
float hl_start = align == UI_ALIGN_LEFT ? current->_x + start_off + off : current->_x + current->_w - hlstrw - off;
|
|
if (align == UI_ALIGN_RIGHT) {
|
|
hl_start -= arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, iend, strlen(text));
|
|
}
|
|
arm_g2_set_color(theme->ACCENT_COL);
|
|
arm_g2_fill_rect(hl_start, current->_y + current->button_offset_y * 1.5, hlstrw, cursor_height);
|
|
}
|
|
|
|
// Draw cursor
|
|
int str_start = align == UI_ALIGN_LEFT ? 0 : current->cursor_x;
|
|
int str_length = align == UI_ALIGN_LEFT ? current->cursor_x : (strlen(text) - current->cursor_x);
|
|
float strw = arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, str_start, str_length);
|
|
float cursor_x = align == UI_ALIGN_LEFT ? current->_x + strw + off : current->_x + current->_w - strw - off;
|
|
arm_g2_set_color(theme->TEXT_COL); // Cursor
|
|
arm_g2_fill_rect(cursor_x, current->_y + current->button_offset_y * 1.5, 1.0 * UI_SCALE(), cursor_height);
|
|
|
|
strcpy(current->text_selected, text);
|
|
if (live_update && current->text_selected_handle != NULL) {
|
|
current->text_selected_handle->changed = strcmp(current->text_selected_handle->text, current->text_selected) != 0;
|
|
current->text_selected_handle->text = string_copy(current->text_selected);
|
|
}
|
|
}
|
|
|
|
void ui_set_hovered_tab_name(char *name) {
|
|
if (ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, current->_window_h)) {
|
|
strcpy(current->hovered_tab_name, name);
|
|
current->hovered_tab_x = current->_window_x;
|
|
current->hovered_tab_y = current->_window_y;
|
|
current->hovered_tab_w = current->_window_w;
|
|
current->hovered_tab_h = current->_window_h;
|
|
}
|
|
}
|
|
|
|
void ui_draw_tabs() {
|
|
current->input_x = current->restore_x;
|
|
current->input_y = current->restore_y;
|
|
if (current->current_window == NULL) {
|
|
return;
|
|
}
|
|
float tab_x = 0.0;
|
|
float tab_y = 0.0;
|
|
float tab_h_min = UI_BUTTON_H() * 1.1;
|
|
float header_h = current->current_window->drag_enabled ? UI_HEADER_DRAG_H() : 0;
|
|
float tab_h = (theme->FULL_TABS && current->tab_vertical) ? ((current->_window_h - header_h) / current->tab_count) : tab_h_min;
|
|
float orig_y = current->_y;
|
|
current->_y = header_h;
|
|
current->tab_handle->changed = false;
|
|
|
|
if (current->is_ctrl_down && current->is_tab_down) { // Next tab
|
|
current->tab_handle->position++;
|
|
if (current->tab_handle->position >= current->tab_count) {
|
|
current->tab_handle->position = 0;
|
|
}
|
|
current->tab_handle->changed = true;
|
|
current->is_tab_down = false;
|
|
}
|
|
|
|
if (current->tab_handle->position >= current->tab_count) {
|
|
current->tab_handle->position = current->tab_count - 1;
|
|
}
|
|
|
|
arm_g2_set_color(theme->SEPARATOR_COL); // Tab background
|
|
if (current->tab_vertical) {
|
|
arm_g2_fill_rect(0, current->_y, UI_ELEMENT_W(), current->_window_h);
|
|
}
|
|
else {
|
|
arm_g2_fill_rect(0, current->_y, current->_window_w, current->button_offset_y + tab_h + 2);
|
|
}
|
|
|
|
arm_g2_set_color(theme->BUTTON_COL); // Underline tab buttons
|
|
if (current->tab_vertical) {
|
|
arm_g2_fill_rect(UI_ELEMENT_W(), current->_y, 1, current->_window_h);
|
|
}
|
|
else {
|
|
arm_g2_fill_rect(current->button_offset_y, current->_y + current->button_offset_y + tab_h + 2, current->_window_w - current->button_offset_y * 2.0, 1);
|
|
}
|
|
|
|
float base_y = current->tab_vertical ? current->_y : current->_y + 2;
|
|
bool _enabled = current->enabled;
|
|
|
|
for (int i = 0; i < current->tab_count; ++i) {
|
|
current->enabled = current->tab_enabled[i];
|
|
current->_x = tab_x;
|
|
current->_y = base_y + tab_y;
|
|
current->_w = current->tab_vertical ? (UI_ELEMENT_W() - 1 * UI_SCALE()) :
|
|
theme->FULL_TABS ? (current->_window_w / current->tab_count) :
|
|
(arm_g2_string_width(current->ops->font->font_, current->font_size, current->tab_names[i]) + current->button_offset_y * 2.0 + 18.0 * UI_SCALE());
|
|
bool released = ui_get_released(tab_h);
|
|
bool started = ui_get_started(tab_h);
|
|
bool pushed = ui_get_pushed(tab_h);
|
|
bool hover = ui_get_hover(tab_h);
|
|
if (ui_on_tab_drop != NULL) {
|
|
if (started) {
|
|
current->drag_tab_handle = current->tab_handle;
|
|
current->drag_tab_position = i;
|
|
}
|
|
if (current->drag_tab_handle != NULL && hover && current->input_released) {
|
|
ui_on_tab_drop(current->tab_handle, i, current->drag_tab_handle, current->drag_tab_position);
|
|
current->tab_handle->position = i;
|
|
}
|
|
}
|
|
if (released) {
|
|
ui_handle_t *h = ui_nest(current->tab_handle, current->tab_handle->position); // Restore tab scroll
|
|
h->scroll_offset = current->current_window->scroll_offset;
|
|
h = ui_nest(current->tab_handle, i);
|
|
current->tab_scroll = h->scroll_offset;
|
|
current->tab_handle->position = i; // Set new tab
|
|
current->current_window->redraws = 3;
|
|
current->tab_handle->changed = true;
|
|
}
|
|
bool selected = current->tab_handle->position == i;
|
|
|
|
arm_g2_set_color((pushed || hover) ? theme->HOVER_COL :
|
|
current->tab_colors[i] != -1 ? current->tab_colors[i] :
|
|
selected ? theme->WINDOW_BG_COL :
|
|
theme->SEPARATOR_COL);
|
|
if (current->tab_vertical) {
|
|
tab_y += tab_h + 1;
|
|
}
|
|
else {
|
|
tab_x += current->_w + 1;
|
|
}
|
|
// ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w, tab_h); // Round corners
|
|
arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w, tab_h);
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
if (!selected) {
|
|
ui_fade_color(0.65);
|
|
}
|
|
ui_draw_string(current->tab_names[i], theme->TEXT_OFFSET, (tab_h - tab_h_min) / 2.0, (theme->FULL_TABS || !current->tab_vertical) ? UI_ALIGN_CENTER : UI_ALIGN_LEFT, true);
|
|
|
|
if (selected) { // Hide underline for active tab
|
|
if (current->tab_vertical) {
|
|
// Hide underline
|
|
// arm_g2_set_color(theme->WINDOW_BG_COL);
|
|
// arm_g2_fill_rect(current->_x + current->button_offset_y + current->_w - 1, current->_y + current->button_offset_y - 1, 2, tab_h + current->button_offset_y);
|
|
// Highlight
|
|
arm_g2_set_color(theme->HIGHLIGHT_COL);
|
|
arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y - 1, 2, tab_h + current->button_offset_y);
|
|
}
|
|
else {
|
|
// Hide underline
|
|
arm_g2_set_color(theme->WINDOW_BG_COL);
|
|
arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y + tab_h, current->_w, 1);
|
|
// Highlight
|
|
arm_g2_set_color(theme->HIGHLIGHT_COL);
|
|
arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w, 2);
|
|
}
|
|
}
|
|
|
|
// Tab separator
|
|
if (i < current->tab_count - 1) {
|
|
arm_g2_set_color(theme->SEPARATOR_COL - 0x00050505);
|
|
if (current->tab_vertical) {
|
|
arm_g2_fill_rect(current->_x, current->_y + tab_h, current->_w, 1);
|
|
}
|
|
else {
|
|
arm_g2_fill_rect(current->_x + current->button_offset_y + current->_w, current->_y, 1, tab_h);
|
|
}
|
|
}
|
|
}
|
|
|
|
current->enabled = _enabled;
|
|
ui_set_hovered_tab_name(current->tab_names[current->tab_handle->position]);
|
|
|
|
current->_x = 0; // Restore positions
|
|
current->_y = orig_y;
|
|
current->_w = !current->current_window->scroll_enabled ? current->_window_w : current->_window_w - UI_SCROLL_W();
|
|
}
|
|
|
|
void ui_draw_arrow(bool selected) {
|
|
float x = current->_x + current->arrow_offset_x;
|
|
float y = current->_y + current->arrow_offset_y;
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
if (selected) {
|
|
arm_g2_fill_triangle(x, y,
|
|
x + UI_ARROW_SIZE(), y,
|
|
x + UI_ARROW_SIZE() / 2.0, y + UI_ARROW_SIZE());
|
|
}
|
|
else {
|
|
arm_g2_fill_triangle(x, y,
|
|
x, y + UI_ARROW_SIZE(),
|
|
x + UI_ARROW_SIZE(), y + UI_ARROW_SIZE() / 2.0);
|
|
}
|
|
}
|
|
|
|
void ui_draw_tree(bool selected) {
|
|
float SIGN_W = 7.0 * UI_SCALE();
|
|
float x = current->_x + current->arrow_offset_x + 1;
|
|
float y = current->_y + current->arrow_offset_y + 1;
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
if (selected) {
|
|
arm_g2_fill_rect(x, y + SIGN_W / 2.0 - 1, SIGN_W, SIGN_W / 8.0);
|
|
}
|
|
else {
|
|
arm_g2_fill_rect(x, y + SIGN_W / 2.0 - 1, SIGN_W, SIGN_W / 8.0);
|
|
arm_g2_fill_rect(x + SIGN_W / 2.0 - 1, y, SIGN_W / 8.0, SIGN_W);
|
|
}
|
|
}
|
|
|
|
void ui_draw_check(bool selected, bool hover) {
|
|
float x = current->_x + current->check_offset_x;
|
|
float y = current->_y + current->check_offset_y;
|
|
|
|
arm_g2_set_color(selected ? theme->HIGHLIGHT_COL : theme->PRESSED_COL);
|
|
ui_draw_rect(true, x, y, UI_CHECK_SIZE(), UI_CHECK_SIZE()); // Bg
|
|
|
|
arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL);
|
|
ui_draw_rect(false, x, y, UI_CHECK_SIZE(), UI_CHECK_SIZE()); // Bg
|
|
|
|
if (selected) { // Check
|
|
arm_g2_set_color(hover ? theme->TEXT_COL : theme->LABEL_COL);
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
int size = UI_CHECK_SELECT_SIZE();
|
|
arm_g2_draw_scaled_render_target(¤t->check_select_image, x + current->check_select_offset_x, y + current->check_select_offset_y, size, size);
|
|
}
|
|
}
|
|
|
|
void ui_draw_radio(bool selected, bool hover) {
|
|
float x = current->_x + current->radio_offset_x;
|
|
float y = current->_y + current->radio_offset_y;
|
|
arm_g2_set_color(selected ? theme->HIGHLIGHT_COL : hover ? theme->HOVER_COL : theme->BUTTON_COL);
|
|
arm_g2_draw_render_target(¤t->radio_image, x, y); // Circle bg
|
|
|
|
if (selected) { // Check
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
arm_g2_draw_render_target(¤t->radio_select_image, x + current->radio_select_offset_x, y + current->radio_select_offset_y); // Circle
|
|
}
|
|
}
|
|
|
|
void ui_draw_slider(float value, float from, float to, bool filled, bool hover) {
|
|
float x = current->_x + current->button_offset_y;
|
|
float y = current->_y + current->button_offset_y;
|
|
float w = current->_w - current->button_offset_y * 2.0;
|
|
|
|
arm_g2_set_color(theme->PRESSED_COL);
|
|
ui_draw_rect(true, x, y, w, UI_BUTTON_H()); // Bg
|
|
|
|
if (hover) {
|
|
arm_g2_set_color(theme->HOVER_COL);
|
|
ui_draw_rect(false, x, y, w, UI_BUTTON_H()); // Bg
|
|
}
|
|
|
|
arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL);
|
|
float offset = (value - from) / (to - from);
|
|
float bar_w = 8.0 * UI_SCALE(); // Unfilled bar
|
|
float slider_x = filled ? x : x + (w - bar_w) * offset;
|
|
slider_x = fmax(fmin(slider_x, x + (w - bar_w)), x);
|
|
float slider_w = filled ? w * offset : bar_w;
|
|
slider_w = fmax(fmin(slider_w, w), 0);
|
|
ui_draw_rect(true, slider_x, y, slider_w, UI_BUTTON_H());
|
|
}
|
|
|
|
void ui_set_scale(float factor) {
|
|
current->ops->scale_factor = factor;
|
|
current->font_size = UI_FONT_SIZE();
|
|
float font_height = arm_g2_font_height(current->ops->font->font_, current->font_size);
|
|
current->font_offset_y = (UI_ELEMENT_H() - font_height) / 2.0; // Precalculate offsets
|
|
current->arrow_offset_y = (UI_ELEMENT_H() - UI_ARROW_SIZE()) / 2.0;
|
|
current->arrow_offset_x = current->arrow_offset_y;
|
|
current->title_offset_x = (current->arrow_offset_x * 2.0 + UI_ARROW_SIZE()) / UI_SCALE();
|
|
current->button_offset_y = (UI_ELEMENT_H() - UI_BUTTON_H()) / 2.0;
|
|
current->check_offset_y = (UI_ELEMENT_H() - UI_CHECK_SIZE()) / 2.0;
|
|
current->check_offset_x = current->check_offset_y;
|
|
current->check_select_offset_y = (UI_CHECK_SIZE() - UI_CHECK_SELECT_SIZE()) / 2.0;
|
|
current->check_select_offset_x = current->check_select_offset_y;
|
|
current->radio_offset_y = (UI_ELEMENT_H() - UI_CHECK_SIZE()) / 2.0;
|
|
current->radio_offset_x = current->radio_offset_y;
|
|
current->radio_select_offset_y = (UI_CHECK_SIZE() - UI_CHECK_SELECT_SIZE()) / 2.0;
|
|
current->radio_select_offset_x = current->radio_select_offset_y;
|
|
current->elements_baked = false;
|
|
}
|
|
|
|
void ui_init(ui_t *ui, ui_options_t *ops) {
|
|
assert(ui_instances_count < UI_MAX_INSTANCES);
|
|
memset(ui, 0, sizeof(ui_t));
|
|
ui_instances[ui_instances_count++] = ui;
|
|
ui->ops = ops;
|
|
ui_set_current(ui);
|
|
ui_set_scale(ops->scale_factor);
|
|
current->enabled = true;
|
|
current->scroll_enabled = true;
|
|
current->highlight_on_select = true;
|
|
current->tab_switch_enabled = true;
|
|
current->input_enabled = true;
|
|
current->current_ratio = -1;
|
|
current->image_scroll_align = true;
|
|
current->window_ended = true;
|
|
current->restore_x = -1;
|
|
current->restore_y = -1;
|
|
if (ui_combo_search_handle == NULL) {
|
|
ui_combo_search_handle = ui_handle_create();
|
|
gc_root(ui_combo_search_handle);
|
|
}
|
|
}
|
|
|
|
void ui_begin(ui_t *ui) {
|
|
ui_set_current(ui);
|
|
if (!current->elements_baked) {
|
|
ui_bake_elements();
|
|
}
|
|
current->changed = false;
|
|
current->_x = 0; // Reset cursor
|
|
current->_y = 0;
|
|
current->_w = 0;
|
|
current->_h = 0;
|
|
}
|
|
|
|
// Sticky region ignores window scrolling
|
|
void ui_begin_sticky() {
|
|
current->sticky = true;
|
|
current->_y -= current->current_window->scroll_offset;
|
|
if (current->current_window->scroll_enabled) {
|
|
current->_w += UI_SCROLL_W(); // Use full width since there is no scroll bar in sticky region
|
|
}
|
|
}
|
|
|
|
void ui_end_sticky() {
|
|
arm_g2_end();
|
|
current->sticky = false;
|
|
current->scissor = true;
|
|
kinc_g4_scissor(0, current->_y, current->_window_w, current->_window_h - current->_y);
|
|
current->window_header_h += current->_y - current->window_header_h;
|
|
current->_y += current->current_window->scroll_offset;
|
|
current->is_hovered = false;
|
|
if (current->current_window->scroll_enabled) {
|
|
current->_w -= UI_SCROLL_W();
|
|
}
|
|
}
|
|
|
|
void ui_end_window(bool bind_global_g) {
|
|
ui_handle_t *handle = current->current_window;
|
|
if (handle == NULL) return;
|
|
if (handle->redraws > 0 || current->is_scrolling) {
|
|
if (current->scissor) {
|
|
current->scissor = false;
|
|
kinc_g4_disable_scissor();
|
|
}
|
|
|
|
if (current->tab_count > 0) {
|
|
ui_draw_tabs();
|
|
}
|
|
|
|
if (handle->drag_enabled) { // Draggable header
|
|
arm_g2_set_color(theme->SEPARATOR_COL);
|
|
arm_g2_fill_rect(0, 0, current->_window_w, UI_HEADER_DRAG_H());
|
|
}
|
|
|
|
float window_size = handle->layout == UI_LAYOUT_VERTICAL ? current->_window_h - current->window_header_h : current->_window_w - current->window_header_w; // Exclude header
|
|
float full_size = handle->layout == UI_LAYOUT_VERTICAL ? current->_y - current->window_header_h : current->_x - current->window_header_w;
|
|
full_size -= handle->scroll_offset;
|
|
|
|
if (full_size < window_size || !current->scroll_enabled) { // Disable scrollbar
|
|
handle->scroll_enabled = false;
|
|
handle->scroll_offset = 0;
|
|
}
|
|
else { // Draw window scrollbar if necessary
|
|
if (handle->layout == UI_LAYOUT_VERTICAL) {
|
|
handle->scroll_enabled = true;
|
|
}
|
|
if (current->tab_scroll < 0) { // Restore tab
|
|
handle->scroll_offset = current->tab_scroll;
|
|
current->tab_scroll = 0;
|
|
}
|
|
float wy = current->_window_y + current->window_header_h;
|
|
float amount_to_scroll = full_size - window_size;
|
|
float amount_scrolled = -handle->scroll_offset;
|
|
float ratio = amount_scrolled / amount_to_scroll;
|
|
float bar_h = window_size * fabs(window_size / full_size);
|
|
bar_h = fmax(bar_h, UI_ELEMENT_H());
|
|
|
|
float total_scrollable_area = window_size - bar_h;
|
|
float e = amount_to_scroll / total_scrollable_area;
|
|
float bar_y = total_scrollable_area * ratio + current->window_header_h;
|
|
bool bar_focus = ui_input_in_rect(current->_window_x + current->_window_w - UI_SCROLL_W(), bar_y + current->_window_y, UI_SCROLL_W(), bar_h);
|
|
|
|
if (handle->scroll_enabled && current->input_started && bar_focus) { // Start scrolling
|
|
current->scroll_handle = handle;
|
|
current->is_scrolling = true;
|
|
}
|
|
|
|
float scroll_delta = current->input_wheel_delta;
|
|
if (handle->scroll_enabled && ui_touch_scroll && current->input_down && current->input_dy != 0 && current->input_x > current->_window_x + current->window_header_w && current->input_y > current->_window_y + current->window_header_h) {
|
|
current->is_scrolling = true;
|
|
scroll_delta = -current->input_dy / 20.0;
|
|
}
|
|
if (handle == current->scroll_handle) { // Scroll
|
|
ui_scroll(current->input_dy * e);
|
|
}
|
|
else if (scroll_delta != 0 && current->combo_selected_handle == NULL && ui_input_in_rect(current->_window_x, wy, current->_window_w, window_size)) { // Wheel
|
|
ui_scroll(scroll_delta * UI_ELEMENT_H());
|
|
}
|
|
|
|
// Stay in bounds
|
|
if (handle->scroll_offset > 0) {
|
|
handle->scroll_offset = 0;
|
|
}
|
|
else if (full_size + handle->scroll_offset < window_size) {
|
|
handle->scroll_offset = window_size - full_size;
|
|
}
|
|
|
|
if (handle->layout == UI_LAYOUT_VERTICAL) {
|
|
arm_g2_set_color(theme->BUTTON_COL); // Bar
|
|
bool scrollbar_focus = ui_input_in_rect(current->_window_x + current->_window_w - UI_SCROLL_W(), wy, UI_SCROLL_W(), window_size);
|
|
float bar_w = (scrollbar_focus || handle == current->scroll_handle) ? UI_SCROLL_W() : UI_SCROLL_MINI_W();
|
|
ui_draw_rect(true, current->_window_w - bar_w - current->scroll_align, bar_y, bar_w, bar_h);
|
|
}
|
|
}
|
|
|
|
handle->last_max_x = current->_x;
|
|
handle->last_max_y = current->_y;
|
|
if (handle->layout == UI_LAYOUT_VERTICAL) {
|
|
handle->last_max_x += current->_window_w;
|
|
}
|
|
else {
|
|
handle->last_max_y += current->_window_h;
|
|
}
|
|
handle->redraws--;
|
|
}
|
|
|
|
current->window_ended = true;
|
|
|
|
// Draw window texture
|
|
if (ui_always_redraw_window || handle->redraws > -4) {
|
|
if (bind_global_g) {
|
|
arm_g2_restore_render_target();
|
|
}
|
|
arm_g2_set_color(0xffffffff);
|
|
arm_g2_draw_render_target(&handle->texture, current->_window_x, current->_window_y);
|
|
if (handle->redraws <= 0) {
|
|
handle->redraws--;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ui_window_dirty(ui_handle_t *handle, int x, int y, int w, int h) {
|
|
float wx = x + handle->drag_x;
|
|
float wy = y + handle->drag_y;
|
|
float input_changed = ui_input_in_rect(wx, wy, w, h) && ui_input_changed();
|
|
return current->always_redraw || current->is_scrolling || input_changed;
|
|
}
|
|
|
|
bool ui_window(ui_handle_t *handle, int x, int y, int w, int h, bool drag) {
|
|
if (handle->texture.width == 0 || w != handle->texture.width || h != handle->texture.height) {
|
|
ui_resize(handle, w, h);
|
|
}
|
|
|
|
if (!current->window_ended) {
|
|
ui_end_window(true); // End previous window if necessary
|
|
}
|
|
current->window_ended = false;
|
|
|
|
arm_g2_set_render_target(&handle->texture);
|
|
current->current_window = handle;
|
|
current->_window_x = x + handle->drag_x;
|
|
current->_window_y = y + handle->drag_y;
|
|
current->_window_w = w;
|
|
current->_window_h = h;
|
|
current->window_header_w = 0;
|
|
current->window_header_h = 0;
|
|
|
|
if (ui_window_dirty(handle, x, y, w, h)) {
|
|
handle->redraws = 2;
|
|
}
|
|
|
|
if (ui_on_border_hover != NULL) {
|
|
if (ui_input_in_rect(current->_window_x - 4, current->_window_y, 8, current->_window_h)) {
|
|
ui_on_border_hover(handle, 0);
|
|
}
|
|
else if (ui_input_in_rect(current->_window_x + current->_window_w - 4, current->_window_y, 8, current->_window_h)) {
|
|
ui_on_border_hover(handle, 1);
|
|
}
|
|
else if (ui_input_in_rect(current->_window_x, current->_window_y - 4, current->_window_w, 8)) {
|
|
ui_on_border_hover(handle, 2);
|
|
}
|
|
else if (ui_input_in_rect(current->_window_x, current->_window_y + current->_window_h - 4, current->_window_w, 8)) {
|
|
ui_on_border_hover(handle, 3);
|
|
}
|
|
}
|
|
|
|
if (handle->redraws <= 0) {
|
|
return false;
|
|
}
|
|
|
|
if (handle->layout == UI_LAYOUT_VERTICAL) {
|
|
current->_x = 0;
|
|
current->_y = handle->scroll_offset;
|
|
}
|
|
else {
|
|
current->_x = handle->scroll_offset;
|
|
current->_y = 0;
|
|
}
|
|
if (handle->layout == UI_LAYOUT_HORIZONTAL) w = UI_ELEMENT_W();
|
|
current->_w = !handle->scroll_enabled ? w : w - UI_SCROLL_W(); // Exclude scrollbar if present
|
|
current->_h = h;
|
|
current->tooltip_text[0] = 0;
|
|
current->tooltip_img = NULL;
|
|
current->tooltip_rt = NULL;
|
|
current->tab_count = 0;
|
|
|
|
if (theme->FILL_WINDOW_BG) {
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, theme->WINDOW_BG_COL, 0, 0);
|
|
}
|
|
else {
|
|
kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0);
|
|
arm_g2_set_color(theme->WINDOW_BG_COL);
|
|
arm_g2_fill_rect(current->_x, current->_y - handle->scroll_offset, handle->last_max_x, handle->last_max_y);
|
|
}
|
|
|
|
handle->drag_enabled = drag;
|
|
if (drag) {
|
|
if (current->input_started && ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, UI_HEADER_DRAG_H())) {
|
|
current->drag_handle = handle;
|
|
}
|
|
else if (current->input_released) {
|
|
current->drag_handle = NULL;
|
|
}
|
|
if (handle == current->drag_handle) {
|
|
handle->redraws = 2;
|
|
handle->drag_x += current->input_dx;
|
|
handle->drag_y += current->input_dy;
|
|
}
|
|
current->_y += UI_HEADER_DRAG_H(); // Header offset
|
|
current->window_header_h += UI_HEADER_DRAG_H();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ui_button(char *text, int align, char *label/*, kinc_g4_texture_t *icon, int sx, int sy, int sw, int sh*/) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return false;
|
|
}
|
|
bool released = ui_get_released(UI_ELEMENT_H());
|
|
bool pushed = ui_get_pushed(UI_ELEMENT_H());
|
|
bool hover = ui_get_hover(UI_ELEMENT_H());
|
|
if (released) {
|
|
current->changed = true;
|
|
}
|
|
|
|
arm_g2_set_color(pushed ? theme->PRESSED_COL :
|
|
(!theme->FILL_BUTTON_BG && hover) ? theme->HIGHLIGHT_COL :
|
|
hover ? theme->HOVER_COL :
|
|
theme->BUTTON_COL);
|
|
|
|
if (theme->FILL_BUTTON_BG || pushed || hover) {
|
|
ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y,
|
|
current->_w - current->button_offset_y * 2, UI_BUTTON_H());
|
|
}
|
|
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
ui_draw_string(text, theme->TEXT_OFFSET, 0, align, true);
|
|
if (label != NULL) {
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
ui_draw_string(label, theme->TEXT_OFFSET, 0, align == UI_ALIGN_RIGHT ? UI_ALIGN_LEFT : UI_ALIGN_RIGHT, true);
|
|
}
|
|
|
|
/*
|
|
if (icon != NULL) {
|
|
arm_g2_set_color(0xffffffff);
|
|
if (current->image_invert_y) {
|
|
arm_g2_draw_scaled_sub_image(icon, sx, sy, sw, sh, _x + current->button_offset_y, _y - 1 + sh, sw, -sh);
|
|
}
|
|
else {
|
|
arm_g2_draw_scaled_sub_image(icon, sx, sy, sw, sh, _x + current->button_offset_y, _y - 1, sw, sh);
|
|
}
|
|
}
|
|
*/
|
|
|
|
ui_end_element();
|
|
return released;
|
|
}
|
|
|
|
void ui_split_text(char *lines, int align, int bg) {
|
|
int count = ui_line_count(lines);
|
|
for (int i = 0; i < count; ++i) {
|
|
ui_text(ui_extract_line(lines, i), align, bg);
|
|
}
|
|
}
|
|
|
|
int ui_text(char *text, int align, int bg) {
|
|
if (ui_line_count(text) > 1) {
|
|
ui_split_text(text, align, bg);
|
|
return UI_STATE_IDLE;
|
|
}
|
|
float h = fmax(UI_ELEMENT_H(), arm_g2_font_height(current->ops->font->font_, current->font_size));
|
|
if (!ui_is_visible(h)) {
|
|
ui_end_element_of_size(h + UI_ELEMENT_OFFSET());
|
|
return UI_STATE_IDLE;
|
|
}
|
|
bool started = ui_get_started(h);
|
|
bool down = ui_get_pushed(h);
|
|
bool released = ui_get_released(h);
|
|
bool hover = ui_get_hover(h);
|
|
if (bg != 0x0000000) {
|
|
arm_g2_set_color(bg);
|
|
arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H());
|
|
}
|
|
arm_g2_set_color(theme->TEXT_COL);
|
|
ui_draw_string(text, theme->TEXT_OFFSET, 0, align, true);
|
|
|
|
ui_end_element_of_size(h + UI_ELEMENT_OFFSET());
|
|
return started ? UI_STATE_STARTED : released ? UI_STATE_RELEASED : down ? UI_STATE_DOWN : UI_STATE_IDLE;
|
|
}
|
|
|
|
bool ui_tab(ui_handle_t *handle, char *text, bool vertical, uint32_t color) {
|
|
if (current->tab_count == 0) { // First tab
|
|
current->tab_handle = handle;
|
|
current->tab_vertical = vertical;
|
|
current->_w -= current->tab_vertical ? UI_ELEMENT_OFFSET() + UI_ELEMENT_W() - 1 * UI_SCALE() : 0; // Shrink window area by width of vertical tabs
|
|
if (vertical) {
|
|
current->window_header_w += UI_ELEMENT_W();
|
|
}
|
|
else {
|
|
current->window_header_h += UI_BUTTON_H() + current->button_offset_y + UI_ELEMENT_OFFSET();
|
|
}
|
|
current->restore_x = current->input_x; // Mouse in tab header, disable clicks for tab content
|
|
current->restore_y = current->input_y;
|
|
if (!vertical && ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, current->window_header_h)) {
|
|
current->input_x = current->input_y = -1;
|
|
}
|
|
if (vertical) {
|
|
current->_x += current->window_header_w + 6;
|
|
current->_w -= 6;
|
|
}
|
|
else {
|
|
current->_y += current->window_header_h + 3;
|
|
}
|
|
}
|
|
assert(current->tab_count < 16);
|
|
strcpy(current->tab_names[current->tab_count], text);
|
|
current->tab_colors[current->tab_count] = color;
|
|
current->tab_enabled[current->tab_count] = current->enabled;
|
|
current->tab_count++;
|
|
return handle->position == current->tab_count - 1;
|
|
}
|
|
|
|
bool ui_panel(ui_handle_t *handle, char *text, bool is_tree, bool filled) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return handle->selected;
|
|
}
|
|
if (ui_get_released(UI_ELEMENT_H())) {
|
|
handle->selected = !handle->selected;
|
|
handle->changed = current->changed = true;
|
|
}
|
|
|
|
if (is_tree) {
|
|
ui_draw_tree(handle->selected);
|
|
}
|
|
else {
|
|
ui_draw_arrow(handle->selected);
|
|
}
|
|
|
|
arm_g2_set_color(theme->LABEL_COL); // Title
|
|
ui_draw_string(text, current->title_offset_x, 0, UI_ALIGN_LEFT, true);
|
|
|
|
ui_end_element();
|
|
return handle->selected;
|
|
}
|
|
|
|
static int image_width(void *image, bool is_rt) {
|
|
if (is_rt) {
|
|
return ((kinc_g4_render_target_t *)image)->width;
|
|
}
|
|
else {
|
|
return ((kinc_g4_texture_t *)image)->tex_width;
|
|
}
|
|
}
|
|
|
|
static int image_height(void *image, bool is_rt) {
|
|
if (is_rt) {
|
|
return ((kinc_g4_render_target_t *)image)->height;
|
|
}
|
|
else {
|
|
return ((kinc_g4_texture_t *)image)->tex_height;
|
|
}
|
|
}
|
|
|
|
static void draw_scaled_image(void *image, bool is_rt, float dx, float dy, float dw, float dh) {
|
|
if (is_rt) {
|
|
arm_g2_draw_scaled_render_target((kinc_g4_render_target_t *)image, dx, dy, dw, dh);
|
|
}
|
|
else {
|
|
arm_g2_draw_scaled_image((kinc_g4_texture_t *)image, dx, dy, dw, dh);
|
|
}
|
|
}
|
|
|
|
static void draw_scaled_sub_image(void *image, bool is_rt, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) {
|
|
if (is_rt) {
|
|
arm_g2_draw_scaled_sub_render_target((kinc_g4_render_target_t *)image, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
}
|
|
else {
|
|
arm_g2_draw_scaled_sub_image((kinc_g4_texture_t *)image, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
}
|
|
}
|
|
|
|
int ui_sub_image(/*kinc_g4_texture_t kinc_g4_render_target_t*/ void *image, bool is_rt, uint32_t tint, int h, int sx, int sy, int sw, int sh) {
|
|
float iw = (sw > 0 ? sw : image_width(image, is_rt)) * UI_SCALE();
|
|
float ih = (sh > 0 ? sh : image_height(image, is_rt)) * UI_SCALE();
|
|
float w = fmin(iw, current->_w);
|
|
float x = current->_x;
|
|
float scroll = current->current_window != NULL ? current->current_window->scroll_enabled : false;
|
|
float r = current->current_ratio == -1 ? 1.0 : ui_get_ratio(current->ratios->buffer[current->current_ratio], 1);
|
|
if (current->image_scroll_align) { // Account for scrollbar size
|
|
w = fmin(iw, current->_w - current->button_offset_y * 2.0);
|
|
x += current->button_offset_y;
|
|
if (!scroll) {
|
|
w -= UI_SCROLL_W() * r;
|
|
x += UI_SCROLL_W() * r / 2.0;
|
|
}
|
|
}
|
|
else if (scroll) {
|
|
w += UI_SCROLL_W() * r;
|
|
}
|
|
|
|
// Image size
|
|
float ratio = h == -1 ?
|
|
w / iw :
|
|
h / ih;
|
|
if (h == -1) {
|
|
h = ih * ratio;
|
|
}
|
|
else {
|
|
w = iw * ratio;
|
|
}
|
|
|
|
if (!ui_is_visible(h)) {
|
|
ui_end_element_of_size(h);
|
|
return UI_STATE_IDLE;
|
|
}
|
|
bool started = ui_get_started(h);
|
|
bool down = ui_get_pushed(h);
|
|
bool released = ui_get_released(h);
|
|
bool hover = ui_get_hover(h);
|
|
|
|
// Limit input to image width
|
|
// if (current->current_ratio == -1 && (started || down || released || hover)) {
|
|
// if (current->input_x < current->_window_x + current->_x || current->input_x > current->_window_x + current->_x + w) {
|
|
// started = down = released = hover = false;
|
|
// }
|
|
// }
|
|
|
|
arm_g2_set_color(tint);
|
|
if (!current->enabled) {
|
|
ui_fade_color(0.25);
|
|
}
|
|
if (sw > 0) { // Source rect specified
|
|
if (current->image_invert_y) {
|
|
draw_scaled_sub_image(image, is_rt, sx, sy, sw, sh, x, current->_y + h, w, -h);
|
|
}
|
|
else {
|
|
draw_scaled_sub_image(image, is_rt, sx, sy, sw, sh, x, current->_y, w, h);
|
|
}
|
|
}
|
|
else {
|
|
if (current->image_invert_y) {
|
|
draw_scaled_image(image, is_rt, x, current->_y + h, w, -h);
|
|
}
|
|
else {
|
|
draw_scaled_image(image, is_rt, x, current->_y, w, h);
|
|
}
|
|
}
|
|
|
|
ui_end_element_of_size(h);
|
|
return started ? UI_STATE_STARTED : released ? UI_STATE_RELEASED : down ? UI_STATE_DOWN : hover ? UI_STATE_HOVERED : UI_STATE_IDLE;
|
|
}
|
|
|
|
int ui_image(/*kinc_g4_texture_t kinc_g4_render_target_t*/ void *image, bool is_rt, uint32_t tint, int h) {
|
|
return ui_sub_image(image, is_rt, tint, h, 0, 0, image_width(image, is_rt), image_height(image, is_rt));
|
|
}
|
|
|
|
char *ui_text_input(ui_handle_t *handle, char *label, int align, bool editable, bool live_update) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return handle->text;
|
|
}
|
|
|
|
bool hover = ui_get_hover(UI_ELEMENT_H());
|
|
if (hover && ui_on_text_hover != NULL) {
|
|
ui_on_text_hover();
|
|
}
|
|
arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL); // Text bg
|
|
ui_draw_rect(false, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H());
|
|
|
|
bool released = ui_get_released(UI_ELEMENT_H());
|
|
if (current->submit_text_handle == handle && released) { // Keep editing selected text
|
|
current->is_typing = true;
|
|
current->text_selected_handle = current->submit_text_handle;
|
|
current->submit_text_handle = NULL;
|
|
ui_set_cursor_to_input(align);
|
|
}
|
|
bool start_edit = released || current->tab_pressed;
|
|
handle->changed = false;
|
|
|
|
if (current->text_selected_handle != handle && start_edit) {
|
|
ui_start_text_edit(handle, align);
|
|
}
|
|
if (current->text_selected_handle == handle) {
|
|
ui_update_text_edit(align, editable, live_update);
|
|
if (current->current_window != NULL) {
|
|
current->current_window->redraws = 2; // Keep redrawing window while typing
|
|
}
|
|
}
|
|
if (current->submit_text_handle == handle) {
|
|
ui_submit_text_edit();
|
|
}
|
|
|
|
if (label[0] != '\0') {
|
|
arm_g2_set_color(theme->LABEL_COL); // Label
|
|
int label_align = align == UI_ALIGN_RIGHT ? UI_ALIGN_LEFT : UI_ALIGN_RIGHT;
|
|
ui_draw_string(label, label_align == UI_ALIGN_LEFT ? theme->TEXT_OFFSET : 0, 0, label_align, true);
|
|
}
|
|
|
|
arm_g2_set_color(theme->TEXT_COL); // Text
|
|
if (current->text_selected_handle != handle) {
|
|
ui_draw_string(handle->text, theme->TEXT_OFFSET, 0, align, true);
|
|
}
|
|
else {
|
|
ui_draw_string(current->text_selected, theme->TEXT_OFFSET, 0, align, false);
|
|
}
|
|
|
|
ui_end_element();
|
|
return handle->text;
|
|
}
|
|
|
|
bool ui_check(ui_handle_t *handle, char *text, char *label) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return handle->selected;
|
|
}
|
|
if (ui_get_released(UI_ELEMENT_H())) {
|
|
handle->selected = !handle->selected;
|
|
handle->changed = current->changed = true;
|
|
}
|
|
else handle->changed = false;
|
|
|
|
bool hover = ui_get_hover(UI_ELEMENT_H());
|
|
ui_draw_check(handle->selected, hover); // Check
|
|
|
|
arm_g2_set_color(theme->TEXT_COL); // Text
|
|
ui_draw_string(text, current->title_offset_x, 0, UI_ALIGN_LEFT, true);
|
|
|
|
if (label[0] != '\0') {
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
ui_draw_string(label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true);
|
|
}
|
|
|
|
ui_end_element();
|
|
|
|
return handle->selected;
|
|
}
|
|
|
|
bool ui_radio(ui_handle_t *handle, int position, char *text, char *label) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return handle->position == position;
|
|
}
|
|
if (position == 0) {
|
|
handle->changed = false;
|
|
}
|
|
if (ui_get_released(UI_ELEMENT_H())) {
|
|
handle->position = position;
|
|
handle->changed = current->changed = true;
|
|
}
|
|
|
|
bool hover = ui_get_hover(UI_ELEMENT_H());
|
|
ui_draw_radio(handle->position == position, hover); // Radio
|
|
|
|
arm_g2_set_color(theme->TEXT_COL); // Text
|
|
ui_draw_string(text, current->title_offset_x, 0, UI_ALIGN_LEFT, true);
|
|
|
|
if (label[0] != '\0') {
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
ui_draw_string(label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true);
|
|
}
|
|
|
|
ui_end_element();
|
|
|
|
return handle->position == position;
|
|
}
|
|
|
|
int ui_combo(ui_handle_t *handle, char_ptr_array_t *texts, char *label, bool show_label, int align, bool search_bar) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return handle->position;
|
|
}
|
|
if (ui_get_released(UI_ELEMENT_H())) {
|
|
if (current->combo_selected_handle == NULL) {
|
|
current->input_enabled = false;
|
|
current->combo_selected_handle = handle;
|
|
current->combo_selected_window = current->current_window;
|
|
current->combo_selected_align = align;
|
|
current->combo_selected_texts = texts;
|
|
current->combo_selected_label = (char *)label;
|
|
current->combo_selected_x = current->_x + current->_window_x;
|
|
current->combo_selected_y = current->_y + current->_window_y + UI_ELEMENT_H();
|
|
current->combo_selected_w = current->_w;
|
|
current->combo_search_bar = search_bar;
|
|
for (int i = 0; i < texts->length; ++i) { // Adapt combo list width to combo item width
|
|
int w = (int)arm_g2_string_width(current->ops->font->font_, current->font_size, texts->buffer[i]) + 10;
|
|
if (current->combo_selected_w < w) {
|
|
current->combo_selected_w = w;
|
|
}
|
|
}
|
|
if (current->combo_selected_w > current->_w * 2.0) {
|
|
current->combo_selected_w = current->_w * 2.0;
|
|
}
|
|
if (current->combo_selected_w > current->_w) {
|
|
current->combo_selected_w += UI_TEXT_OFFSET();
|
|
}
|
|
current->combo_to_submit = handle->position;
|
|
current->combo_initial_value = handle->position;
|
|
}
|
|
}
|
|
if (handle == current->combo_selected_handle && (current->is_escape_down || current->input_released_r)) {
|
|
handle->position = current->combo_initial_value;
|
|
handle->changed = current->changed = true;
|
|
current->submit_combo_handle = NULL;
|
|
}
|
|
else if (handle == current->submit_combo_handle) {
|
|
handle->position = current->combo_to_submit;
|
|
current->submit_combo_handle = NULL;
|
|
handle->changed = current->changed = true;
|
|
}
|
|
else {
|
|
handle->changed = false;
|
|
}
|
|
|
|
arm_g2_set_color(theme->PRESSED_COL); // Bg
|
|
ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H());
|
|
|
|
bool hover = ui_get_hover(UI_ELEMENT_H());
|
|
arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL);
|
|
ui_draw_rect(false, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H());
|
|
|
|
int x = current->_x + current->_w - current->arrow_offset_x - 8;
|
|
int y = current->_y + current->arrow_offset_y + 3;
|
|
|
|
arm_g2_set_color(theme->HOVER_COL);
|
|
// if (handle == current->combo_selected_handle) {
|
|
// // Flip arrow when combo is open
|
|
// arm_g2_fill_triangle(x, y, x + UI_ARROW_SIZE(), y, x + UI_ARROW_SIZE() / 2.0, y - UI_ARROW_SIZE() / 2.0);
|
|
// }
|
|
// else {
|
|
arm_g2_fill_triangle(x, y, x + UI_ARROW_SIZE(), y, x + UI_ARROW_SIZE() / 2.0, y + UI_ARROW_SIZE() / 2.0);
|
|
// }
|
|
|
|
if (show_label && label[0] != '\0') {
|
|
if (align == UI_ALIGN_LEFT) {
|
|
current->_x -= 15;
|
|
}
|
|
arm_g2_set_color(theme->LABEL_COL);
|
|
ui_draw_string(label, theme->TEXT_OFFSET, 0, align == UI_ALIGN_LEFT ? UI_ALIGN_RIGHT : UI_ALIGN_LEFT, true);
|
|
if (align == UI_ALIGN_LEFT) {
|
|
current->_x += 15;
|
|
}
|
|
}
|
|
|
|
if (align == UI_ALIGN_RIGHT) {
|
|
current->_x -= 15;
|
|
}
|
|
arm_g2_set_color(theme->TEXT_COL); // Value
|
|
if (handle->position < texts->length) {
|
|
ui_draw_string(texts->buffer[handle->position], theme->TEXT_OFFSET, 0, align, true);
|
|
}
|
|
if (align == UI_ALIGN_RIGHT) {
|
|
current->_x += 15;
|
|
}
|
|
|
|
ui_end_element();
|
|
return handle->position;
|
|
}
|
|
|
|
float ui_slider(ui_handle_t *handle, char *text, float from, float to, bool filled, float precision, bool display_value, int align, bool text_edit) {
|
|
static char temp[1024];
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
ui_end_element();
|
|
return handle->value;
|
|
}
|
|
if (ui_get_started(UI_ELEMENT_H())) {
|
|
current->scroll_handle = handle;
|
|
current->is_scrolling = true;
|
|
current->changed = handle->changed = true;
|
|
if (ui_touch_tooltip) {
|
|
current->slider_tooltip = true;
|
|
current->slider_tooltip_x = current->_x + current->_window_x;
|
|
current->slider_tooltip_y = current->_y + current->_window_y;
|
|
current->slider_tooltip_w = current->_w;
|
|
}
|
|
}
|
|
else {
|
|
handle->changed = false;
|
|
}
|
|
|
|
#if !defined(KINC_ANDROID) && !defined(KINC_IOS)
|
|
if (handle == current->scroll_handle && current->input_dx != 0) { // Scroll
|
|
#else
|
|
if (handle == current->scroll_handle) { // Scroll
|
|
#endif
|
|
float range = to - from;
|
|
float slider_x = current->_x + current->_window_x + current->button_offset_y;
|
|
float slider_w = current->_w - current->button_offset_y * 2;
|
|
float step = range / slider_w;
|
|
float value = from + (current->input_x - slider_x) * step;
|
|
handle->value = round(value * precision) / precision;
|
|
if (handle->value < from) {
|
|
handle->value = from; // Stay in bounds
|
|
}
|
|
else if (handle->value > to) {
|
|
handle->value = to;
|
|
}
|
|
handle->changed = current->changed = true;
|
|
}
|
|
|
|
bool hover = ui_get_hover(UI_ELEMENT_H());
|
|
ui_draw_slider(handle->value, from, to, filled, hover); // Slider
|
|
|
|
// Text edit
|
|
bool start_edit = (ui_get_released(UI_ELEMENT_H()) || current->tab_pressed) && text_edit;
|
|
if (start_edit) { // Mouse did not move
|
|
char tmp[256];
|
|
sprintf(tmp, "%.2f", handle->value);
|
|
handle->text = string_copy(tmp);
|
|
string_strip_trailing_zeros(handle->text);
|
|
ui_start_text_edit(handle, UI_ALIGN_LEFT);
|
|
handle->changed = current->changed = true;
|
|
}
|
|
int lalign = align == UI_ALIGN_LEFT ? UI_ALIGN_RIGHT : UI_ALIGN_LEFT;
|
|
if (current->text_selected_handle == handle) {
|
|
ui_update_text_edit(lalign, true, false);
|
|
}
|
|
if (current->submit_text_handle == handle) {
|
|
ui_submit_text_edit();
|
|
#ifdef WITH_EVAL
|
|
handle->value = js_eval(handle->text);
|
|
#else
|
|
handle->value = atof(handle->text);
|
|
#endif
|
|
handle->changed = current->changed = true;
|
|
}
|
|
|
|
arm_g2_set_color(theme->LABEL_COL); // Text
|
|
ui_draw_string(text, theme->TEXT_OFFSET, 0, align, true);
|
|
|
|
if (display_value) {
|
|
arm_g2_set_color(theme->TEXT_COL); // Value
|
|
if (current->text_selected_handle != handle) {
|
|
sprintf(temp, "%.2f", round(handle->value * precision) / precision);
|
|
string_strip_trailing_zeros(temp);
|
|
ui_draw_string(temp, theme->TEXT_OFFSET, 0, lalign, true);
|
|
}
|
|
else {
|
|
ui_draw_string(current->text_selected, theme->TEXT_OFFSET, 0, lalign, true);
|
|
}
|
|
}
|
|
|
|
ui_end_element();
|
|
return handle->value;
|
|
}
|
|
|
|
void ui_separator(int h, bool fill) {
|
|
if (!ui_is_visible(UI_ELEMENT_H())) {
|
|
current->_y += h * UI_SCALE();
|
|
return;
|
|
}
|
|
if (fill) {
|
|
arm_g2_set_color(theme->SEPARATOR_COL);
|
|
arm_g2_fill_rect(current->_x, current->_y, current->_w, h * UI_SCALE());
|
|
}
|
|
current->_y += h * UI_SCALE();
|
|
}
|
|
|
|
void ui_tooltip(char *text) {
|
|
assert(strlen(text) < 512);
|
|
strcpy(current->tooltip_text, text);
|
|
current->tooltip_y = current->_y + current->_window_y;
|
|
}
|
|
|
|
void ui_tooltip_image(kinc_g4_texture_t *image, int max_width) {
|
|
current->tooltip_img = image;
|
|
current->tooltip_img_max_width = max_width;
|
|
current->tooltip_invert_y = current->image_invert_y;
|
|
current->tooltip_y = current->_y + current->_window_y;
|
|
}
|
|
|
|
void ui_tooltip_render_target(kinc_g4_render_target_t *image, int max_width) {
|
|
current->tooltip_rt = image;
|
|
current->tooltip_img_max_width = max_width;
|
|
current->tooltip_invert_y = current->image_invert_y;
|
|
current->tooltip_y = current->_y + current->_window_y;
|
|
}
|
|
|
|
void ui_end(bool last) {
|
|
if (!current->window_ended) {
|
|
ui_end_window(true);
|
|
}
|
|
ui_draw_combo(true); // Handle active combo
|
|
ui_draw_tooltip(true);
|
|
current->tab_pressed_handle = NULL;
|
|
if (last) {
|
|
ui_end_input();
|
|
}
|
|
}
|
|
|
|
void ui_set_input_position(ui_t *ui, int x, int y) {
|
|
ui->input_dx += x - ui->input_x;
|
|
ui->input_dy += y - ui->input_y;
|
|
ui->input_x = x;
|
|
ui->input_y = y;
|
|
}
|
|
|
|
// Useful for drag and drop operations
|
|
char *ui_hovered_tab_name() {
|
|
return ui_input_in_rect(current->hovered_tab_x, current->hovered_tab_y, current->hovered_tab_w, current->hovered_tab_h) ? current->hovered_tab_name : "";
|
|
}
|
|
|
|
void ui_mouse_down(ui_t *ui, int button, int x, int y) {
|
|
if (ui->pen_in_use) {
|
|
return;
|
|
}
|
|
if (button == 0) {
|
|
ui->input_started = ui->input_down = true;
|
|
}
|
|
else {
|
|
ui->input_started_r = ui->input_down_r = true;
|
|
}
|
|
ui->input_started_time = kinc_time();
|
|
#if defined(KINC_ANDROID) || defined(KINC_IOS)
|
|
ui_set_input_position(ui, x, y);
|
|
#endif
|
|
ui->input_started_x = x;
|
|
ui->input_started_y = y;
|
|
}
|
|
|
|
void ui_mouse_move(ui_t *ui, int x, int y, int movement_x, int movement_y) {
|
|
#if !defined(KINC_ANDROID) && !defined(KINC_IOS)
|
|
ui_set_input_position(ui, x, y);
|
|
#endif
|
|
}
|
|
|
|
void ui_mouse_up(ui_t *ui, int button, int x, int y) {
|
|
if (ui->pen_in_use) {
|
|
return;
|
|
}
|
|
|
|
if (ui->touch_hold_activated) {
|
|
ui->touch_hold_activated = false;
|
|
return;
|
|
}
|
|
|
|
if (ui->is_scrolling) { // Prevent action when scrolling is active
|
|
ui->is_scrolling = false;
|
|
ui->scroll_handle = NULL;
|
|
ui->slider_tooltip = false;
|
|
if (x == ui->input_started_x && y == ui->input_started_y) { // Mouse not moved
|
|
if (button == 0) ui->input_released = true;
|
|
else ui->input_released_r = true;
|
|
}
|
|
}
|
|
else {
|
|
if (button == 0) {
|
|
ui->input_released = true;
|
|
}
|
|
else {
|
|
ui->input_released_r = true;
|
|
}
|
|
}
|
|
|
|
if (button == 0) {
|
|
ui->input_down = false;
|
|
}
|
|
else {
|
|
ui->input_down_r = false;
|
|
}
|
|
#if defined(KINC_ANDROID) || defined(KINC_IOS)
|
|
ui_set_input_position(ui, x, y);
|
|
#endif
|
|
ui_deselect_text(ui);
|
|
}
|
|
|
|
void ui_mouse_wheel(ui_t *ui, int delta) {
|
|
ui->input_wheel_delta = delta;
|
|
}
|
|
|
|
void ui_pen_down(ui_t *ui, int x, int y, float pressure) {
|
|
#if defined(KINC_ANDROID) || defined(KINC_IOS)
|
|
return;
|
|
#endif
|
|
|
|
ui_mouse_down(ui, 0, x, y);
|
|
}
|
|
|
|
void ui_pen_up(ui_t *ui, int x, int y, float pressure) {
|
|
#if defined(KINC_ANDROID) || defined(KINC_IOS)
|
|
return;
|
|
#endif
|
|
|
|
if (ui->input_started) {
|
|
ui->input_started = false;
|
|
ui->pen_in_use = true;
|
|
return;
|
|
}
|
|
ui_mouse_up(ui, 0, x, y);
|
|
ui->pen_in_use = true; // On pen release, additional mouse down & up events are fired at once - filter those out
|
|
}
|
|
|
|
void ui_pen_move(ui_t *ui, int x, int y, float pressure) {
|
|
#if defined(KINC_IOS)
|
|
// Listen to pen hover if no other input is active
|
|
if (pressure == 0.0) {
|
|
if (!ui->input_down && !ui->input_down_r) {
|
|
ui_set_input_position(ui, x, y);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(KINC_ANDROID) || defined(KINC_IOS)
|
|
return;
|
|
#endif
|
|
|
|
ui_mouse_move(ui, x, y, 0, 0);
|
|
}
|
|
|
|
void ui_key_down(ui_t *ui, int key_code) {
|
|
ui->key_code = key_code;
|
|
ui->is_key_pressed = true;
|
|
ui->is_key_down = true;
|
|
ui_key_repeat_time = kinc_time() + 0.4;
|
|
switch (key_code) {
|
|
case KINC_KEY_SHIFT: ui->is_shift_down = true; break;
|
|
case KINC_KEY_CONTROL: ui->is_ctrl_down = true; break;
|
|
#ifdef KINC_DARWIN
|
|
case KINC_KEY_META: ui->is_ctrl_down = true; break;
|
|
#endif
|
|
case KINC_KEY_ALT: ui->is_alt_down = true; break;
|
|
case KINC_KEY_BACKSPACE: ui->is_backspace_down = true; break;
|
|
case KINC_KEY_DELETE: ui->is_delete_down = true; break;
|
|
case KINC_KEY_ESCAPE: ui->is_escape_down = true; break;
|
|
case KINC_KEY_RETURN: ui->is_return_down = true; break;
|
|
case KINC_KEY_TAB: ui->is_tab_down = true; break;
|
|
case KINC_KEY_A: ui->is_a_down = true; break;
|
|
case KINC_KEY_SPACE: ui->key_char = ' '; break;
|
|
#ifdef UI_ANDROID_RMB // Detect right mouse button on Android..
|
|
case KINC_KEY_BACK: if (!ui->input_down_r) ui_mouse_down(ui, 1, ui->input_x, ui->input_y); break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void ui_key_up(ui_t *ui, int key_code) {
|
|
ui->is_key_down = false;
|
|
switch (key_code) {
|
|
case KINC_KEY_SHIFT: ui->is_shift_down = false; break;
|
|
case KINC_KEY_CONTROL: ui->is_ctrl_down = false; break;
|
|
#ifdef KINC_DARWIN
|
|
case KINC_KEY_META: ui->is_ctrl_down = false; break;
|
|
#endif
|
|
case KINC_KEY_ALT: ui->is_alt_down = false; break;
|
|
case KINC_KEY_BACKSPACE: ui->is_backspace_down = false; break;
|
|
case KINC_KEY_DELETE: ui->is_delete_down = false; break;
|
|
case KINC_KEY_ESCAPE: ui->is_escape_down = false; break;
|
|
case KINC_KEY_RETURN: ui->is_return_down = false; break;
|
|
case KINC_KEY_TAB: ui->is_tab_down = false; break;
|
|
case KINC_KEY_A: ui->is_a_down = false; break;
|
|
#ifdef UI_ANDROID_RMB
|
|
case KINC_KEY_BACK: ui_mouse_down(ui, 1, ui->input_x, ui->input_y); break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void ui_key_press(ui_t *ui, unsigned key_char) {
|
|
ui->key_char = key_char;
|
|
ui->is_key_pressed = true;
|
|
}
|
|
|
|
#if defined(KINC_ANDROID) || defined(KINC_IOS)
|
|
static float ui_pinch_distance = 0.0;
|
|
static float ui_pinch_total = 0.0;
|
|
static bool ui_pinch_started = false;
|
|
|
|
void ui_touch_down(ui_t *ui, int index, int x, int y) {
|
|
// Reset movement delta on touch start
|
|
if (index == 0) {
|
|
ui->input_dx = 0;
|
|
ui->input_dy = 0;
|
|
ui->input_x = x;
|
|
ui->input_y = y;
|
|
}
|
|
// Two fingers down - right mouse button
|
|
else if (index == 1) {
|
|
ui->input_down = false;
|
|
ui_mouse_down(ui, 1, ui->input_x, ui->input_y);
|
|
ui_pinch_started = true;
|
|
ui_pinch_total = 0.0;
|
|
ui_pinch_distance = 0.0;
|
|
}
|
|
// Three fingers down - middle mouse button
|
|
else if (index == 2) {
|
|
ui->input_down_r = false;
|
|
ui_mouse_down(ui, 2, ui->input_x, ui->input_y);
|
|
}
|
|
}
|
|
|
|
void ui_touch_up(ui_t *ui, int index, int x, int y) {
|
|
if (index == 1) {
|
|
ui_mouse_up(ui, 1, ui->input_x, ui->input_y);
|
|
}
|
|
}
|
|
|
|
void ui_touch_move(ui_t *ui, int index, int x, int y) {
|
|
if (index == 0) {
|
|
ui_set_input_position(ui, x, y);
|
|
}
|
|
|
|
// Pinch to zoom - mouse wheel
|
|
if (index == 1) {
|
|
float last_distance = ui_pinch_distance;
|
|
float dx = ui->input_x - x;
|
|
float dy = ui->input_y - y;
|
|
ui_pinch_distance = sqrtf(dx * dx + dy * dy);
|
|
ui_pinch_total += last_distance != 0 ? last_distance - ui_pinch_distance : 0;
|
|
if (!ui_pinch_started) {
|
|
ui->input_wheel_delta = ui_pinch_total / 50;
|
|
if (ui->input_wheel_delta != 0) {
|
|
ui_pinch_total = 0.0;
|
|
}
|
|
}
|
|
ui_pinch_started = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
char *ui_copy() {
|
|
ui_is_copy = true;
|
|
return &ui_text_to_copy[0];
|
|
}
|
|
|
|
char *ui_cut() {
|
|
ui_is_cut = true;
|
|
return ui_copy();
|
|
}
|
|
|
|
void ui_paste(char *s) {
|
|
ui_is_paste = true;
|
|
strcpy(ui_text_to_paste, s);
|
|
}
|
|
|
|
void ui_theme_default(ui_theme_t *t) {
|
|
t->WINDOW_BG_COL = 0xff292929;
|
|
t->HOVER_COL = 0xff434343;
|
|
t->ACCENT_COL = 0xff606060;
|
|
t->BUTTON_COL = 0xff383838;
|
|
t->PRESSED_COL = 0xff222222;
|
|
t->TEXT_COL = 0xffe8e8e8;
|
|
t->LABEL_COL = 0xffc8c8c8;
|
|
t->SEPARATOR_COL = 0xff202020;
|
|
t->HIGHLIGHT_COL = 0xff205d9c;
|
|
t->FONT_SIZE = 13;
|
|
t->ELEMENT_W = 100;
|
|
t->ELEMENT_H = 24;
|
|
t->ELEMENT_OFFSET = 4;
|
|
t->ARROW_SIZE = 5;
|
|
t->BUTTON_H = 22;
|
|
t->CHECK_SIZE = 16;
|
|
t->CHECK_SELECT_SIZE = 12;
|
|
t->SCROLL_W = 9;
|
|
t->SCROLL_MINI_W = 3;
|
|
t->TEXT_OFFSET = 8;
|
|
t->TAB_W = 6;
|
|
t->FILL_WINDOW_BG = true;
|
|
t->FILL_BUTTON_BG = true;
|
|
t->LINK_STYLE = UI_LINK_STYLE_LINE;
|
|
t->FULL_TABS = false;
|
|
t->ROUND_CORNERS = true;
|
|
t->SHADOWS = true;
|
|
}
|