From c8260e22de1e6e4e29fb8e8552d53cd5246efd90 Mon Sep 17 00:00:00 2001 From: Z0RIK <113785827+Z0RIK@users.noreply.github.com> Date: Sat, 29 Jul 2023 23:18:02 +0600 Subject: [PATCH] Added example of a calculator app with custom input box (#310) Turned my previous pull request into an example, as you suggested. --- examples/Makefile | 1 + examples/custom_input_box/custom_input_box.c | 260 +++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 examples/custom_input_box/custom_input_box.c diff --git a/examples/Makefile b/examples/Makefile index 150c7b24..cacf46ff 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -348,6 +348,7 @@ endif EXAMPLES = \ controls_test_suite/controls_test_suite \ custom_file_dialog/custom_file_dialog \ + custom_input_box/custom_input_box\ image_exporter/image_exporter \ image_importer_raw/image_importer_raw \ property_list/property_list \ diff --git a/examples/custom_input_box/custom_input_box.c b/examples/custom_input_box/custom_input_box.c new file mode 100644 index 00000000..bb2b9919 --- /dev/null +++ b/examples/custom_input_box/custom_input_box.c @@ -0,0 +1,260 @@ +/******************************************************************************************* +* +* raygui - basic calculator app with custom input box for float values +* +* DEPENDENCIES: +* raylib 4.5 - Windowing/input management and drawing. +* raygui 3.5 - Immediate-mode GUI controls. +* +* COMPILATION (Windows - MinGW): +* gcc -o $(NAME_PART).exe $(FILE_NAME) -I../../src -lraylib -lopengl32 -lgdi32 -std=c99 +* +**********************************************************************************************/ + +#include + +#define RAYGUI_IMPLEMENTATION +#include + +int guiFloatingPointIndex = 0; // Global variable shared by all GuiFLoatBox() + +float TextToFloat(const char* text); // Helper function that converts text to float +int GuiFloatBox(Rectangle bounds, const char* text, float* value, int minValue, int maxValue, bool editMode); // Custom input box that works with float values. Basicly GuiValueBox(), but with some changes + +int main() +{ + InitWindow(250, 100, "Basic calculator"); + + // General variables + SetTargetFPS(60); + + float variableA = 0.0f; + float variableB = 0.0f; + float result = 0.0f; + char operation[2]; + operation[0] = '+'; + operation[1] = '\0'; + + bool variableAMode = false; + bool variableBMode = false; + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (GuiFloatBox((Rectangle){ 10, 10, 100, 20 }, NULL, &variableA, -1000000.0, 1000000.0, variableAMode)) variableAMode = !variableAMode; + if (GuiFloatBox((Rectangle){ 140, 10, 100, 20 }, NULL, &variableB, -1000000.0, 1000000.0, variableBMode)) variableBMode = !variableBMode; + + if (GuiButton((Rectangle){ 10, 70, 50, 20 }, "+")) + { + result = variableA + variableB; + operation[0] = '+'; + } + if (GuiButton((Rectangle){ 70, 70, 50, 20 }, "-")) + { + result = variableA - variableB; + operation[0] = '-'; + } + if (GuiButton((Rectangle){ 130, 70, 50, 20 }, "*")) + { + result = variableA * variableB; + operation[0] = '*'; + } + if (GuiButton((Rectangle){ 190, 70, 50, 20 }, "/")) + { + result = variableA / variableB; + operation[0] = '/'; + } + + DrawText(operation, 123, 15, 10, DARKGRAY); + + GuiFloatBox((Rectangle){ 55, 40, 135, 20 }, "= ", &result, -2000000.0, 2000000.0, false); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + CloseWindow(); +} + +// Get float value from text +float TextToFloat(const char* text) +{ + float value = 0.0f; + float floatingPoint = 0.0f; + int sign = 1; + + // deal with the sign + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1; + text++; + } + + // convert text to float + for (int i = 0; (((text[i] >= '0') && (text[i] <= '9')) || text[i] == '.'); i++) + { + if (text[i] == '.') + { + if (floatingPoint > 0.0f) break; + + floatingPoint = 10.0f; + continue; + } + if (floatingPoint > 0.0f) // after encountering decimal separator + { + value += (float)(text[i] - '0') / floatingPoint; + floatingPoint *= 10.0f; + } + else // before decimal separator + value = value * 10.0f + (float)(text[i] - '0'); + } + + return value * sign; +} + +// Float Box control, updates input text with numbers +int GuiFloatBox(Rectangle bounds, const char* text, float* value, int minValue, int maxValue, bool editMode) +{ +#if !defined(RAYGUI_VALUEBOX_MAX_CHARS) +#define RAYGUI_VALUEBOX_MAX_CHARS 32 +#endif + + int result = 0; + GuiState state = guiState; + + char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + { + Vector2 mousePoint = GetMousePosition(); + + if (*value >= 0) sprintf(textValue, "+%.3f", *value); + else sprintf(textValue, "%.3f", *value); + + bool valueHasChanged = false; + + int keyCount = (int)strlen(textValue) - guiFloatingPointIndex; + + if (editMode) + { + state = STATE_PRESSED; + + // Only allow keys in range [48..57] + if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (GetTextWidth(textValue) < bounds.width) + { + int key = GetCharPressed(); + if ((key >= 48) && (key <= 57) && guiFloatingPointIndex) + { + if (guiFloatingPointIndex && guiFloatingPointIndex != 4) guiFloatingPointIndex--; + + textValue[keyCount] = (char)key; + textValue[++keyCount] = '\0'; + valueHasChanged = true; + } + } + } + + // Delete text + if (keyCount > 0) + { + if (IsKeyPressed(KEY_BACKSPACE)) + { + if (guiFloatingPointIndex < 4) guiFloatingPointIndex++; + + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + } + + // Change sign + if (IsKeyPressed(KEY_MINUS)) + { + if (textValue[0] == '+') textValue[0] = '-'; + else if (textValue[0] == '-') textValue[0] = '+'; + valueHasChanged = true; + } + + // Add decimal separator + if ((IsKeyPressed(KEY_COMMA) || IsKeyPressed(KEY_PERIOD)) && guiFloatingPointIndex == 4) + { + guiFloatingPointIndex--; + valueHasChanged = true; + } + + if (valueHasChanged) + { + *value = TextToFloat(textValue); + } + + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + { + guiFloatingPointIndex = 0; + result = 1; + } + } + else + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + sprintf(textValue, "%.3f", *value); + + if (state == STATE_PRESSED) + { + baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + textValue[(int)strlen(textValue) - guiFloatingPointIndex] = '\0'; + } + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + // WARNING: BLANK color does not work properly with Fade() + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state * 3))), guiAlpha), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state * 3))), guiAlpha)); + + // Draw cursor + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = { bounds.x + GetTextWidth(textValue) / 2 + bounds.width / 2 + 1, bounds.y + 2 * GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4 * GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; + GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) ? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + return result; +}