diff --git a/demo/demo.c b/demo/demo.c index f681457..73c0435 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -295,7 +295,7 @@ color_picker(struct zr_context *panel, struct color_picker* control, zr_layout_row_dynamic(&popup, 30, 2); for (i = 0; i < 4; ++i, iter++) { zr_float t; - *iter = (zr_byte)zr_spinner(&popup, 0, *iter, 255, 1, NULL); + *iter = (zr_byte)zr_spinner_int(&popup, 0, *iter, 255, 1, NULL); t = *iter; t = zr_slider(&popup, 0, t, 255, 10); *iter = (zr_byte)t; @@ -509,7 +509,9 @@ struct state { zr_float slider; zr_size progressbar; zr_int spinner; + zr_float spinnerf; zr_state spinner_active; + zr_state spinnerf_active; zr_size item_current; zr_size shelf_selection; zr_bool toggle; @@ -608,7 +610,8 @@ widget_panel(struct zr_context *panel, struct state *demo) /* item selection */ if (!demo->scaleable) zr_layout_row_static(panel, 30, 150, 1); else zr_layout_row_dynamic(panel, 30, 1); - demo->spinner = zr_spinner(panel, 0, demo->spinner, 250, 10, &demo->spinner_active); + demo->spinner = zr_spinner_int(panel, 0, demo->spinner, 250, 10, &demo->spinner_active); + demo->spinnerf = zr_spinner_float(panel, 0.0f, demo->spinnerf, 8.0f, 0.5f, &demo->spinnerf_active); /* combo boxes */ if (!demo->scaleable) zr_layout_row_static(panel, 30, 150, 1); @@ -665,8 +668,8 @@ properties_tab(struct zr_context *panel, struct zr_style *config) for (i = 0; i <= ZR_PROPERTY_SCROLLBAR_SIZE; ++i) { zr_int tx, ty; zr_label(panel, properties[i], ZR_TEXT_LEFT); - tx = zr_spinner(panel,0,(zr_int)config->properties[i].x, 20, 1, NULL); - ty = zr_spinner(panel,0,(zr_int)config->properties[i].y, 20, 1, NULL); + tx = zr_spinner_int(panel,0,(zr_int)config->properties[i].x, 20, 1, NULL); + ty = zr_spinner_int(panel,0,(zr_int)config->properties[i].y, 20, 1, NULL); config->properties[i].x = (float)tx; config->properties[i].y = (float)ty; } @@ -683,7 +686,7 @@ round_tab(struct zr_context *panel, struct zr_style *config) for (i = 0; i < ZR_ROUNDING_MAX; ++i) { zr_int t; zr_label(panel, rounding[i], ZR_TEXT_LEFT); - t = zr_spinner(panel,0,(zr_int)config->rounding[i], 20, 1, NULL); + t = zr_spinner_int(panel,0,(zr_int)config->rounding[i], 20, 1, NULL); config->rounding[i] = (float)t; } } @@ -770,7 +773,8 @@ init_demo(struct demo_gui *gui) win->slider = 2.0f; win->progressbar = 50; win->spinner = 100; - win->value = 0.6f; + win->spinnerf = 0.5f; + win->value = 0.5f; } /* ----------------------------------------------------------------- diff --git a/zahnrad.c b/zahnrad.c index e50f38b..6028cc6 100644 --- a/zahnrad.c +++ b/zahnrad.c @@ -38,6 +38,7 @@ #define ZR_4_DIV_PI_SQRT (0.405284735f) #define ZR_UTF_INVALID 0xFFFD #define ZR_MAX_NUMBER_BUFFER 64 +#define ZR_MAX_FLOAT_PRECISION 2 #define ZR_UNUSED(x) ((void)(x)) #define ZR_LERP(a, b, t) ((a) + ((b) - (a)) * (t)) @@ -61,6 +62,7 @@ enum zr_tree_node_symbol {ZR_TREE_NODE_BULLET, ZR_TREE_NODE_TRIANGLE}; static const struct zr_rect zr_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; +static const double ZR_DOUBLE_PRECISION = 0.00000000000001; /* ============================================================== * ALIGNMENT * =============================================================== */ @@ -231,6 +233,59 @@ zr_strtoi(zr_int *number, const char *buffer, zr_size len) return 1; } +static zr_int +zr_strtof(zr_float *number, const char *buffer) +{ + zr_int i; + zr_float m; + zr_bool div; + zr_int pow; + const zr_char *p = buffer; + zr_float floatvalue = 0; + + ZR_ASSERT(number); + ZR_ASSERT(buffer); + if (!number || !buffer) return 0; + *number = 0; + + while( *p && *p != '.' && *p != 'e' ) { + floatvalue = floatvalue * 10.0f + (zr_float) (*p - '0'); + p++; + } + + if ( *p == '.' ) { + p++; + for(m = 0.1f; *p && *p != 'e'; p++ ) { + floatvalue = floatvalue + (zr_float) (*p - '0') * m; + m *= 0.1f; + } + } + if ( *p == 'e' ) { + p++; + if ( *p == '-' ) { + div = zr_true; + p++; + } + else if ( *p == '+' ) { + div = zr_false; + p++; + } + else div = zr_false; + + for ( pow = 0; *p; p++ ) + pow = pow * 10 + (int) (*p - '0'); + + for ( m = 1.0, i = 0; i < pow; i++ ) + m *= 10.0f; + + if ( div ) + floatvalue /= m; + else floatvalue *= m; + } + *number = floatvalue; + return 1; +} + static zr_size zr_itos(char *buffer, zr_int num) { @@ -262,6 +317,155 @@ zr_itos(char *buffer, zr_int num) } while (num); return len; } + +static zr_double +zr_pow(double x, int n) +{ + /* check the sign of n */ + zr_double r = 1; + int plus = n >= 0; + n = (plus) ? n : -n; + while (n > 0) { + if ((n & 1) == 1) + r *= x; + n /= 2; + x *= x; + } + return plus ? r : 1.0 / r; +} + +static zr_int +zr_isinf(double x) +{ + union {zr_ulong u; double f;} ieee754; + ieee754.f = x; + return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) == 0x7ff00000 && + ( (unsigned)ieee754.u == 0 ); +} + +static int +zr_isnan(double x) +{ + union {zr_ulong u; double f;} ieee754; + ieee754.f = x; + return ( (unsigned)(ieee754.u >> 32) & 0x7fffffff ) + + ( (unsigned)ieee754.u != 0 ) > 0x7ff00000; +} + +static zr_double +zr_fabs(zr_double x) +{ + if (x < 0) return -x; + return x; +} + +static zr_double +zr_floor(zr_double x) +{ + return (zr_double)((zr_int)x - ((x < 0.0) ? 1 : 0)); +} + +static zr_int +zr_log10(zr_double n) +{ + zr_int neg; + zr_int ret; + zr_int exp = 0; + + neg = (n < 0) ? 1 : 0; + ret = (neg) ? (zr_int)-n : (zr_int)n; + while ((ret / 10) > 0) { + ret /= 10; + exp++; + } + if (neg) exp = -exp; + return exp; +} + +static zr_size +zr_dtos(char *s, zr_double n) +{ + zr_int useExp; + zr_int digit, m, m1; + zr_char *c = s; + zr_int neg; + + if (zr_isnan(n)) { + s[0] = 'n'; s[1] = 'a'; s[2] = 'n'; s[3] = '\0'; + return 3; + } else if (zr_isinf(n)) { + s[0] = 'i'; s[1] = 'n'; s[2] = 'f'; s[3] = '\0'; + return 3; + } else if (n == 0.0) { + s[0] = '0'; s[1] = '\0'; + return 1; + } + + neg = (n < 0); + if (neg) n = -n; + + /* calculate magnitude */ + m = zr_log10(n); + useExp = (m >= 14 || (neg && m >= 9) || m <= -9); + if (neg) *(c++) = '-'; + + /* set up for scientific notation */ + if (useExp) { + if (m < 0) + m -= 1; + n = n / zr_pow(10.0, m); + m1 = m; + m = 0; + } + if (m < 1.0) { + m = 0; + } + + /* convert the number */ + while (n > ZR_DOUBLE_PRECISION || m >= 0) { + zr_double tmp; + zr_double weight = zr_pow(10.0, m); + if (weight > 0 && !zr_isinf(weight)) { + zr_double t = (zr_double)n / weight; + tmp = zr_floor(t); + digit = (zr_int)tmp; + n -= (digit * weight); + *(c++) = (zr_char)('0' + (zr_char)digit); + } + if (m == 0 && n > 0) + *(c++) = '.'; + m--; + } + + if (useExp) { + /* convert the exponent */ + int i, j; + *(c++) = 'e'; + if (m1 > 0) { + *(c++) = '+'; + } else { + *(c++) = '-'; + m1 = -m1; + } + m = 0; + while (m1 > 0) { + *(c++) = (zr_char)('0' + (zr_char)(m1 % 10)); + m1 /= 10; + m++; + } + c -= m; + for (i = 0, j = m-1; idown) return zr_true; return zr_false; } + /* * ============================================================== * @@ -4732,8 +4937,9 @@ zr_widget_spinner_base(struct zr_command_buffer *out, struct zr_rect r, return ret; } + zr_int -zr_widget_spinner(struct zr_command_buffer *out, struct zr_rect r, +zr_widget_spinner_int(struct zr_command_buffer *out, struct zr_rect r, const struct zr_spinner *s, zr_int min, zr_int value, zr_int max, zr_int step, zr_state *active, const struct zr_input *in, const struct zr_user_font *font) @@ -4765,6 +4971,70 @@ zr_widget_spinner(struct zr_command_buffer *out, struct zr_rect r, if (active) *active = is_active; return value; } + +static zr_size +zr_string_float_limit(char *string, zr_int prec) +{ + zr_int dot = 0; + char *c = string; + while (*c) { + if (*c == '.') { + dot = 1; + c++; + continue; + } + if (dot == (prec+1)) { + *c = 0; + break; + } + if (dot > 0) dot++; + c++; + } + return (zr_size)(c - string); +} + +zr_float +zr_widget_spinner_float(struct zr_command_buffer *out, struct zr_rect r, + const struct zr_spinner *s, zr_float min, zr_float value, zr_float max, + zr_float step, zr_state *active, const struct zr_input *in, + const struct zr_user_font *font) +{ + zr_int res; + char string[ZR_MAX_NUMBER_BUFFER]; + zr_size len, old_len; + zr_state is_active; + + ZR_ASSERT(out); + ZR_ASSERT(s); + ZR_ASSERT(font); + if (!out || !s || !font) return value; + + /* make sure given values are correct */ + value = CLAMP(min, value, max); + len = zr_dtos(string, value); + len = zr_string_float_limit(string, ZR_MAX_FLOAT_PRECISION); + is_active = (active) ? *active : zr_false; + old_len = len; + + res = zr_widget_spinner_base(out, r, s, string, &len, ZR_INPUT_FLOAT, &is_active, in, font); + if (res) { + zr_int val; + zr_float f = (zr_float)zr_pow(10.0, ZR_MAX_FLOAT_PRECISION); + value += (res > 0) ? step : -step; + value = CLAMP(min, value, max); + val = (zr_int)(value * f); + val = ((val + (zr_int)((step*f)*0.5f)) / (zr_int)(step*f)) * (zr_int)(step*f); + value = (zr_float)val / f; + } + + if (old_len != len) { + zr_float old = value; + zr_string_float_limit(string, ZR_MAX_FLOAT_PRECISION); + zr_strtof(&value, string); + } + if (active) *active = is_active; + return value; +} /* * ============================================================== * @@ -4798,7 +5068,7 @@ zr_style_default_rounding(struct zr_style *style) static void zr_style_default_color(struct zr_style *style) { - style->colors[ZR_COLOR_TEXT] = zr_rgba(135, 135, 135, 255); + style->colors[ZR_COLOR_TEXT] = zr_rgba(165, 165, 165, 255); style->colors[ZR_COLOR_TEXT_HOVERING] = zr_rgba(120, 120, 120, 255); style->colors[ZR_COLOR_TEXT_ACTIVE] = zr_rgba(100, 100, 100, 255); style->colors[ZR_COLOR_WINDOW] = zr_rgba(45, 45, 45, 255); @@ -6925,7 +7195,7 @@ zr_spinner_base(struct zr_context *layout, struct zr_rect *bounds, } zr_int -zr_spinner(struct zr_context *layout, zr_int min, zr_int value, +zr_spinner_int(struct zr_context *layout, zr_int min, zr_int value, zr_int max, zr_int step, zr_state *active) { struct zr_rect bounds; @@ -6938,7 +7208,25 @@ zr_spinner(struct zr_context *layout, zr_int min, zr_int value, if (!state) return value; out = layout->buffer; i = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : layout->input; - return zr_widget_spinner(out, bounds, &spinner, min, value, max, step, active, + return zr_widget_spinner_int(out, bounds, &spinner, min, value, max, step, active, + i, &layout->style->font); +} + +zr_float +zr_spinner_float(struct zr_context *layout, zr_float min, zr_float value, + zr_float max, zr_float step, zr_state *active) +{ + struct zr_rect bounds; + struct zr_spinner spinner; + struct zr_command_buffer *out; + const struct zr_input *i; + enum zr_widget_state state; + + state = zr_spinner_base(layout, &bounds, &spinner); + if (!state) return value; + out = layout->buffer; + i = (state == ZR_WIDGET_ROM || layout->flags & ZR_WINDOW_ROM) ? 0 : layout->input; + return zr_widget_spinner_float(out, bounds, &spinner, min, value, max, step, active, i, &layout->style->font); } /* diff --git a/zahnrad.h b/zahnrad.h index b83ec33..a9b043f 100644 --- a/zahnrad.h +++ b/zahnrad.h @@ -1781,21 +1781,24 @@ zr_size zr_edit_box_len(struct zr_edit_box*); the toolkit so everything has to be stored byte the user. Widget function API - zr_widget_text -- draws a string inside a box - zr_widget_button_text -- button widget with text content - zr_widget_button_image -- button widget with icon content - zr_widget_button_triangle -- button widget with triangle content - zr_widget_button_text_triangle -- button widget with triangle and text content - zr_widget_button_text_image -- button widget with image and text content - zr_widget_toggle -- either a checkbox or radiobutton widget - zr_widget_slider -- floating point slider widget - zr_widget_progress -- unsigned integer progressbar widget - zr_widget_editbox -- Editbox widget for complex user input - zr_widget_edit -- Editbox wiget for basic user input - zr_widget_edit_filtered -- Editbox with utf8 gylph filter capabilities - zr_widget_spinner -- integer spinner widget - zr_widget_scrollbarv -- vertical scrollbar widget imeplementation - zr_widget_scrollbarh -- horizontal scrollbar widget imeplementation + zr_widget_text -- draws a string inside a box + zr_widget_button_text -- button widget with text content + zr_widget_button_image -- button widget with icon content + zr_widget_button_triangle -- button widget with triangle content + zr_widget_button_text_triangle -- button widget with triangle and text content + zr_widget_button_text_image -- button widget with image and text content + zr_widget_toggle -- either a checkbox or radiobutton widget + zr_widget_slider -- floating point slider widget + zr_widget_progress -- unsigned integer progressbar widget + zr_widget_editbox -- Editbox widget for complex user input + zr_widget_edit -- Editbox wiget for basic user input + zr_widget_edit_filtered -- Editbox with utf8 gylph filter capabilities + zr_widget_spinner_int -- integer spinner widget + zr_widget_spinner_float -- float spinner widget + zr_widget_drag_int -- integer dragging widget + zr_widget_drag_float -- float dragging widget + zr_widget_scrollbarv -- vertical scrollbar widget imeplementation + zr_widget_scrollbarh -- horizontal scrollbar widget imeplementation */ enum zr_text_align { ZR_TEXT_LEFT, @@ -2222,7 +2225,7 @@ zr_size zr_widget_edit_filtered(struct zr_command_buffer*, struct zr_rect, - state of the editbox with either active or inactive - returns the size of the buffer in bytes after the modification */ -zr_int zr_widget_spinner(struct zr_command_buffer*, struct zr_rect, +zr_int zr_widget_spinner_int(struct zr_command_buffer*, struct zr_rect, const struct zr_spinner*, zr_int min, zr_int value, zr_int max, zr_int step, zr_state *active, const struct zr_input*, const struct zr_user_font*); @@ -2240,6 +2243,25 @@ zr_int zr_widget_spinner(struct zr_command_buffer*, struct zr_rect, Output: - returns the from the user input updated spinner value */ +zr_float zr_widget_spinner_float(struct zr_command_buffer*, struct zr_rect, + const struct zr_spinner*, zr_float min, zr_float value, + zr_float max, zr_float step, zr_state *active, + const struct zr_input*, const struct zr_user_font*); +/* this function executes a float spinner widget + Input: + - output command buffer for draw commands + - bounds of the spinner widget + - visual widget style structure describing the spinner + - minimal spinner value that will no be underflown + - spinner value that will be updated + - maximal spinner value that will no be overflown + - spinner input state with either active or inactive + - input structure to update the slider with + - font structure for text drawing + Output: + - returns the from the user input updated spinner value +*/ + zr_float zr_widget_scrollbarv(struct zr_command_buffer*, struct zr_rect, zr_float offset, zr_float target, zr_float step, const struct zr_scrollbar*, @@ -3307,7 +3329,7 @@ zr_size zr_edit_filtered(struct zr_context*, zr_char *buffer, zr_size len, - length of the buffer after user input update - current state of the editbox with active(zr_true) or inactive(zr_false) */ -zr_int zr_spinner(struct zr_context*, zr_int min, zr_int value, zr_int max, +zr_int zr_spinner_int(struct zr_context*, zr_int min, zr_int value, zr_int max, zr_int step, zr_state *active); /* this function creates a integer spinner widget Input: @@ -3320,6 +3342,19 @@ zr_int zr_spinner(struct zr_context*, zr_int min, zr_int value, zr_int max, - the from user input updated spinner value - current state of the editbox with active(zr_true) or inactive(zr_false) */ +zr_float zr_spinner_float(struct zr_context*, zr_float min, zr_float value, zr_float max, + zr_float step, zr_state *active); +/* this function creates a float spinner widget + Input: + - min value that will not be underflown + - current spinner value to be updated by user input + - max value that will not be overflown + - spinner value modificaton stepping intervall + - current state of the spinner with active as currently modfied by user input + Output: + - the from user input updated spinner value + - current state of the editbox with active(zr_true) or inactive(zr_false) +*/ /* -------------------------------------------------------------- * COMBO BOX * --------------------------------------------------------------