diff --git a/examples/Makefile b/examples/Makefile index e21e849..7496453 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -353,7 +353,8 @@ EXAMPLES = \ property_list/property_list \ portable_window/portable_window \ scroll_panel/scroll_panel \ - style_selector/style_selector + style_selector/style_selector \ + custom_sliders/custom_sliders CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) diff --git a/examples/custom_sliders/custom_sliders.c b/examples/custom_sliders/custom_sliders.c new file mode 100644 index 0000000..bda983d --- /dev/null +++ b/examples/custom_sliders/custom_sliders.c @@ -0,0 +1,489 @@ +/******************************************************************************************* +* +* raygui - custom sliders +* +* DEPENDENCIES: +* raylib 4.0 - Windowing/input management and drawing. +* raygui 3.0 - Immediate-mode GUI controls. +* +* COMPILATION (Windows - MinGW): +* gcc -o $(NAME_PART).exe $(FILE_NAME) -I../../src -lraylib -lopengl32 -lgdi32 -std=c99 +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) +* +**********************************************************************************************/ + +#include "raylib.h" + +#define RAYGUI_IMPLEMENTATION +#include "../../src/raygui.h" + +//---------------------------------------------------------------------------------- +// Controls Functions Declaration +//---------------------------------------------------------------------------------- + +float GuiVerticalSlider(Rectangle bounds, const char *textTop, const char *textBottom, float value, float minValue, float maxValue); +float GuiVerticalSliderBar(Rectangle bounds, const char *textTop, const char *textBottom, float value, float minValue, float maxValue); +float GuiVerticalSliderPro(Rectangle bounds, const char *textTop, const char *textBottom, float value, float minValue, float maxValue, int sliderHeight); + +bool GuiSliderOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode); +bool GuiSliderBarOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode); +float GuiSliderProOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth, bool editMode); + +bool GuiVerticalSliderOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode); +bool GuiVerticalSliderBarOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode); +float GuiVerticalSliderProOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, int sliderHeight, bool editMode); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main() +{ + // Initialization + //--------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "ustom sliders"); + + // layout_name: controls initialization + //---------------------------------------------------------------------------------- + float value = 0.5f; + bool Slider_EditMode = false; + bool VerticalSlider_EditMode = false; + bool VerticalSliderBar_EditMode = false; + //---------------------------------------------------------------------------------- + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Implement required update logic + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); + + if (VerticalSlider_EditMode || VerticalSliderBar_EditMode) + GuiLock(); + else + GuiUnlock(); + + char buffer[128]; + snprintf(buffer, 128, "%.2f", value); + + // raygui: controls drawing + //---------------------------------------------------------------------------------- + GuiGroupBox((Rectangle){ 66, 24, 276, 312 }, "STANDARD"); + value = GuiSlider((Rectangle){ 96, 48, 216, 16 }, buffer, buffer, value, 0.0f, 1.0f); + value = GuiVerticalSlider((Rectangle){ 120, 120, 24, 192 }, buffer, buffer, value, 0.0f, 1.0f); + value = GuiVerticalSliderBar((Rectangle){ 264, 120, 24, 192 }, buffer, buffer, value, 0.0f, 1.0f); + + GuiGroupBox((Rectangle){ 378, 24, 276, 312 }, "OWNING"); + if(GuiSliderOwning((Rectangle){ 408, 48, 216, 16 }, buffer, buffer, &value, 0.0f, 1.0f, Slider_EditMode)) + Slider_EditMode = !Slider_EditMode; + if(GuiVerticalSliderOwning((Rectangle){ 432, 120, 24, 192 }, buffer, buffer, &value, 0.0f, 1.0f, VerticalSlider_EditMode)) + VerticalSlider_EditMode = !VerticalSlider_EditMode; + if(GuiVerticalSliderBarOwning((Rectangle){ 576, 120, 24, 192 }, buffer, buffer, &value, 0.0f, 1.0f, VerticalSliderBar_EditMode)) + VerticalSliderBar_EditMode = !VerticalSliderBar_EditMode; + + //---------------------------------------------------------------------------------- + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//------------------------------------------------------------------------------------ +// Controls Functions Definitions (local) +//------------------------------------------------------------------------------------ + + +float GuiVerticalSliderPro(Rectangle bounds, const char *textTop, const char *textBottom, float value, float minValue, float maxValue, int sliderHeight) +{ + GuiState state = (GuiState)GuiGetState(); + + int sliderValue = (int)(((value - minValue)/(maxValue - minValue)) * (bounds.height - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH))); + + Rectangle slider = { + bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + bounds.y + bounds.height - sliderValue, + bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING), + 0.0f, + }; + + if (sliderHeight > 0) // Slider + { + slider.y -= sliderHeight/2; + slider.height = (float)sliderHeight; + } + else if (sliderHeight == 0) // SliderBar + { + slider.y -= GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.height = (float)sliderValue; + } + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + + // Get equivalent value and slider position from mousePoint.x + float normalizedValue = (bounds.y + bounds.height - mousePoint.y - (float)(sliderHeight / 2)) / (bounds.height - (float)sliderHeight); + value = (maxValue - minValue) * normalizedValue + minValue; + + if (sliderHeight > 0) slider.y = mousePoint.y - slider.height / 2; // Slider + else if (sliderHeight == 0) // SliderBar + { + slider.y = mousePoint.y; + slider.height = bounds.y + bounds.height - slider.y - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + } + else state = STATE_FOCUSED; + } + + if (value > maxValue) value = maxValue; + else if (value < minValue) value = minValue; + } + + + // Bar limits check + if (sliderHeight > 0) // Slider + { + if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.y + slider.height) >= (bounds.y + bounds.height)) slider.y = bounds.y + bounds.height - slider.height - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderHeight == 0) // SliderBar + { + if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH))) + { + slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.height = bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + } + + //-------------------------------------------------------------------- + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + + // Draw slider internal bar (depends on state) + if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == STATE_FOCUSED) + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + + // Draw top/bottom text if provided + if (textTop != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(textTop); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2; + textBounds.y = bounds.y - textBounds.height - GuiGetStyle(SLIDER, TEXT_PADDING); + + GuiDrawText(textTop, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + + if (textBottom != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(textBottom); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height + GuiGetStyle(SLIDER, TEXT_PADDING); + + GuiDrawText(textBottom, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return value; +} + +float GuiVerticalSlider(Rectangle bounds, const char *textTop, const char *textBottom, float value, float minValue, float maxValue) +{ + return GuiVerticalSliderPro(bounds, textTop, textBottom, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH)); +} + +float GuiVerticalSliderBar(Rectangle bounds, const char *textTop, const char *textBottom, float value, float minValue, float maxValue) +{ + return GuiVerticalSliderPro(bounds, textTop, textBottom, value, minValue, maxValue, 0); +} + + +float GuiSliderProOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth, bool editMode) +{ + GuiState state = (GuiState)GuiGetState(); + + float tempValue = *value; + bool pressed = false; + + int sliderValue = (int)(((tempValue - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + + Rectangle slider = { + bounds.x, + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + 0, + bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + if (sliderWidth > 0) // Slider + { + slider.x += (sliderValue - sliderWidth/2); + slider.width = (float)sliderWidth; + } + else if (sliderWidth == 0) // SliderBar + { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = (float)sliderValue; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && (editMode || !guiLocked)) + { + Vector2 mousePoint = GetMousePosition(); + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + pressed = true; + } + } + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) && editMode) + { + pressed = true; + } + if (editMode) + { + state = STATE_PRESSED; + tempValue = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + + if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider + else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + } + + if (tempValue > maxValue) tempValue = maxValue; + else if (tempValue < minValue) tempValue = minValue; + } + + + // Bar limits check + if (sliderWidth > 0) // Slider + { + if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderWidth == 0) // SliderBar + { + if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + + //-------------------------------------------------------------------- + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + + // Draw slider internal bar (depends on state) + if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == STATE_FOCUSED) + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + *value = tempValue; + return pressed; +} + +bool GuiSliderOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode) +{ + return GuiSliderProOwning(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH), editMode); +} + +bool GuiSliderBarOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode) +{ + return GuiSliderProOwning(bounds, textLeft, textRight, value, minValue, maxValue, 0, editMode); +} + + + +float GuiVerticalSliderProOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, int sliderHeight, bool editMode) +{ + GuiState state = (GuiState)GuiGetState(); + + float tempValue = *value; + bool pressed = false; + + int sliderValue = (int)(((tempValue - minValue)/(maxValue - minValue)) * (bounds.height - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH))); + + Rectangle slider = { + bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + bounds.y + bounds.height - sliderValue, + bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING), + 0.0f, + }; + + if (sliderHeight > 0) // Slider + { + slider.y -= sliderHeight/2; + slider.height = (float)sliderHeight; + } + else if (sliderHeight == 0) // SliderBar + { + slider.y -= GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.height = (float)sliderValue; + } + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && (editMode || !guiLocked)) + { + Vector2 mousePoint = GetMousePosition(); + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + pressed = true; + } + } + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) && editMode) + { + pressed = true; + } + if (editMode) + { + state = STATE_PRESSED; + + float normalizedValue = (bounds.y + bounds.height - mousePoint.y - (float)(sliderHeight / 2)) / (bounds.height - (float)sliderHeight); + tempValue = (maxValue - minValue) * normalizedValue + minValue; + + if (sliderHeight > 0) slider.y = mousePoint.y - slider.height / 2; // Slider + else if (sliderHeight == 0) // SliderBar + { + slider.y = mousePoint.y; + slider.height = bounds.y + bounds.height - slider.y - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + } + + if (tempValue > maxValue) tempValue = maxValue; + else if (tempValue < minValue) tempValue = minValue; + } + + + // Bar limits check + if (sliderHeight > 0) // Slider + { + if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.y + slider.height) >= (bounds.y + bounds.height)) slider.y = bounds.y + bounds.height - slider.height - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderHeight == 0) // SliderBar + { + if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH))) + { + slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.height = bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + } + + //-------------------------------------------------------------------- + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + + // Draw slider internal bar (depends on state) + if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == STATE_FOCUSED) + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + + // Draw top/bottom text if provided + if (textTop != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(textTop); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2; + textBounds.y = bounds.y - textBounds.height - GuiGetStyle(SLIDER, TEXT_PADDING); + + GuiDrawText(textTop, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + + if (textBottom != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(textBottom); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height + GuiGetStyle(SLIDER, TEXT_PADDING); + + GuiDrawText(textBottom, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + *value = tempValue; + return pressed; +} + +bool GuiVerticalSliderOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode) +{ + return GuiVerticalSliderProOwning(bounds, textTop, textBottom, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH), editMode); +} + +bool GuiVerticalSliderBarOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode) +{ + return GuiVerticalSliderProOwning(bounds, textTop, textBottom, value, minValue, maxValue, 0, editMode); +} \ No newline at end of file