#include "gui.h" #include #define RAYGUI_MAX_CONTROLS 16 // Maximum number of standard controls #define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of standard properties #define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties #define MAX_LINE_BUFFER_SIZE 256 static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 }; static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) // // Check if two rectangles are equal, used to validate a slider bounds as an id #ifndef CHECK_BOUNDS_ID #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height)) #endif typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; void GuiLoadStyleDefault(void) { // We set this variable first to avoid cyclic function calls // when calling GuiSetStyle() and GuiGetStyle() guiStyleLoaded = true; // Initialize default LIGHT style property values GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff); GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff); GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff); GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff); GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff); GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff); GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); // WARNING: Some controls use other values GuiSetStyle(DEFAULT, TEXT_PADDING, 0); // WARNING: Some controls use other values GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); // WARNING: Some controls use other values // Initialize control-specific property values // NOTE: Those properties are in default list but require specific values by control type GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(BUTTON, BORDER_WIDTH, 2); GuiSetStyle(SLIDER, TEXT_PADDING, 4); GuiSetStyle(CHECKBOX, TEXT_PADDING, 4); GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(SPINNER, TEXT_PADDING, 0); GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(STATUSBAR, TEXT_PADDING, 8); GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); // Initialize extended property values // NOTE: By default, extended property values are initialized to 0 GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property GuiSetStyle(TOGGLE, GROUP_PADDING, 2); GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); GuiSetStyle(SLIDER, SLIDER_PADDING, 1); GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1); GuiSetStyle(CHECKBOX, CHECK_PADDING, 1); GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32); GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, 4); GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0); GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12); GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28); GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2); GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12); GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8); GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16); GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8); GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8); GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); if (guiFont.texture.id != GetFontDefault().texture.id) { // Unload previous font texture UnloadTexture(guiFont.texture); // Setup default raylib font guiFont = GetFontDefault(); // Setup default raylib font rectangle Rectangle whiteChar = { 41, 46, 2, 8 }; SetShapesTexture(guiFont.texture, whiteChar); } } void GuiSetStyle(int control, int property, int value) { if (!guiStyleLoaded) GuiLoadStyleDefault(); guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; // Default properties are propagated to all controls if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE)) { for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; } } int GuiGetStyle(int control, int property) { if (!guiStyleLoaded) GuiLoadStyleDefault(); return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property]; } static int GetTextWidth(const char* text) { #if !defined(ICON_TEXT_PADDING) #define ICON_TEXT_PADDING 4 #endif Vector2 textSize = { 0 }; int textIconOffset = 0; if ((text != NULL) && (text[0] != '\0')) { if (text[0] == '#') { for (int i = 1; (text[i] != '\0') && (i < 5); i++) { if (text[i] == '#') { textIconOffset = i; break; } } } text += textIconOffset; // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); // Custom MeasureText() implementation if ((guiFont.texture.id > 0) && (text != NULL)) { // Get size in bytes of text, considering end of line and line break int size = 0; for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++) { if ((text[i] != '\0') && (text[i] != '\n')) size++; else break; } float scaleFactor = fontSize/(float)guiFont.baseSize; textSize.y = (float)guiFont.baseSize*scaleFactor; float glyphWidth = 0.0f; for (int i = 0, codepointSize = 0; i < size; i += codepointSize) { int codepoint = GetCodepointNext(&text[i], &codepointSize); int codepointIndex = GetGlyphIndex(guiFont, codepoint); if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING)); textSize.x += glyphWidth; } } } return (int)textSize.x; } const char** GetTextLines(const char* text, int* count) { #define RAYGUI_MAX_TEXT_LINES 128 static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings int textSize = (int)strlen(text); lines[0] = text; //int len = 0; *count = 1; //int lineSize = 0; // Stores current line size, not returned for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { //lineSize = len; k++; lines[k] = &text[i + 1]; // WARNING: next value is valid? //len = 0; *count += 1; } //else len++; } //lines[*count - 1].size = len; return lines; } static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color) { if (color.a > 0) { // Draw rectangle filled with color DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color); } if (borderWidth > 0) { // Draw rectangle border lines with color DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, borderColor); DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, borderColor); } } static void GuiDrawText(const char* text, Rectangle bounds, int alignment, Color tint) { #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect #if !defined(ICON_TEXT_PADDING) #define ICON_TEXT_PADDING 4 #endif // We process the text lines one by one if ((text != NULL) && (text[0] != '\0')) { // Get text lines ('\n' delimiter) to process lines individually // NOTE: We can't use GuiTextSplit() because it can be already use before calling // GuiDrawText() and static buffer would be overriden :( int lineCount = 0; const char **lines = GetTextLines(text, &lineCount); //Rectangle textBounds = GetTextBounds(LABEL, bounds); float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); float posOffsetY = 0; for (int i = 0; i < lineCount; i++) { // Get text position depending on alignment and iconId //--------------------------------------------------------------------------------- Vector2 position = { bounds.x, bounds.y }; // TODO: We get text size after icon has been processed // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? int textSizeX = GetTextWidth(lines[i]); // Check guiTextAlign global variables switch (alignment) { case TEXT_ALIGN_LEFT: { position.x = bounds.x; position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); } break; case TEXT_ALIGN_CENTER: { position.x = bounds.x + bounds.width/2 - textSizeX/2; position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); } break; case TEXT_ALIGN_RIGHT: { position.x = bounds.x + bounds.width - textSizeX; position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); } break; default: break; } // NOTE: Make sure we get pixel-perfect coordinates, // In case of decimals we got weird text positioning position.x = (float)((int)position.x); position.y = (float)((int)position.y); //--------------------------------------------------------------------------------- // Draw text (with icon if available) //--------------------------------------------------------------------------------- //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); // Get size in bytes of text, // considering end of line and line break int size = 0; for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ } float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; int textOffsetY = 0; float textOffsetX = 0.0f; for (int c = 0, codepointSize = 0; c < size; c += codepointSize) { int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); int index = GetGlyphIndex(guiFont, codepoint); // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // but we need to draw all of the bad bytes using the '?' symbol moving one byte if (codepoint == 0x3f) codepointSize = 1; if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint else { if ((codepoint != ' ') && (codepoint != '\t')) { // TODO: Draw only required text glyphs fitting the bounds.width, '...' can be appended at the end of the text if (textOffsetX < bounds.width) { DrawTextCodepoint(guiFont, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint); } } if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); } } posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; // TODO: GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)? //--------------------------------------------------------------------------------- } } } static Rectangle GetTextBounds(int control, Rectangle bounds) { Rectangle textBounds = bounds; textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH); textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH); // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT switch (control) { case COMBOBOX: textBounds.width -= (GuiGetStyle(control, COMBO_BUTTON_WIDTH) + GuiGetStyle(control, COMBO_BUTTON_SPACING)); break; //case VALUEBOX: break; // NOTE: ValueBox text value always centered, text padding applies to label default: { if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); else textBounds.x += GuiGetStyle(control, TEXT_PADDING); textBounds.width -= 2 * GuiGetStyle(control, TEXT_PADDING); } break; } // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER return textBounds; } //------- End of raygui section---------// void UI_button(const UIComp_t* comp, const char* text) { GuiDrawRectangle(comp->bbox, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (comp->state*3))), comp->alpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (comp->state*3))), comp->alpha)); GuiDrawText(text, GetTextBounds(BUTTON, comp->bbox), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (comp->state*3))), comp->alpha)); } // Slider control with pro parameters // NOTE: Other GuiSlider*() controls use this one int GuiSliderPro(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth) { Rectangle bounds = comp->bbox; int result = 0; GuiState state = comp->state; float temp = (maxValue - minValue)/2.0f; if (value == NULL) value = &temp; int sliderValue = (int)(((*value - 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 >> 1)); 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) && !guiLocked) if (state != STATE_DISABLED) { Vector2 mousePoint = GetMousePosition(); if (comp->pressed) // Keep dragging outside of bounds { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { //if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { state = STATE_PRESSED; // Get equivalent value and slider position from mousePosition.x *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; } } //else //{ // guiSliderDragging = false; // guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; //} } else if (CheckCollisionPointRec(mousePoint, bounds)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; //guiSliderDragging = true; //guiSliderActive = bounds; // Store bounds as an identifier when dragging starts if (!CheckCollisionPointRec(mousePoint, slider)) { // Get equivalent value and slider position from mousePosition.x *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + (sliderWidth >> 1) )))/(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 state = STATE_FOCUSED; } if (*value > maxValue) *value = maxValue; else if (*value < minValue) *value = 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), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); // Draw slider internal bar (depends on state) if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED))); else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED))); // 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) >> 1); GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } 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) >> 1); GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } //-------------------------------------------------------------------- return result; } //------------------------------------------------------------------------------------ // Controls Functions Definitions (local) //------------------------------------------------------------------------------------ float GuiVerticalSliderPro(const UIComp_t* comp, const char *textTop, const char *textBottom, float* in_value, float minValue, float maxValue, int sliderHeight) { float value = *in_value; GuiState state = comp->state; Rectangle bounds = comp->bbox; 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 >> 1; 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) if (state != STATE_DISABLED) { 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 >> 1)) / (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))), comp->alpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), comp->alpha)); // 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)), comp->alpha)); else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), comp->alpha)); // 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))), comp->alpha)); } 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))), comp->alpha)); } //-------------------------------------------------------------------- *in_value = value; return value; } float UI_vert_slider(const UIComp_t* comp, const char *textTop, const char *textBottom, float* value, float minValue, float maxValue) { return GuiVerticalSliderPro(comp, textTop, textBottom, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH)); } // Slider control extended, returns selected value and has text float UI_slider(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) { return GuiSliderPro(comp, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH)); } void init_UI(void) { GuiLoadStyleDefault(); }