From f19f61b778f5586a1926b8d75d93faff5721c120 Mon Sep 17 00:00:00 2001 From: Konstantin8105 Date: Wed, 23 Nov 2022 12:35:50 +0300 Subject: [PATCH] step --- examples/gui/button/go.mod | 4 +- examples/gui/button/main.go | 2 +- raygui/README.md | 5 + raygui/button.go | 38 ++++ raygui/checkbox.go | 40 ++++ raygui/combobox.go | 75 +++++++ raygui/go.mod | 8 +- raygui/label.go | 21 ++ raygui/progressbar.go | 23 ++ raygui/raygui.go | 103 ++++++--- raygui/slider.go | 76 +++++++ raygui/sliderbar.go | 76 +++++++ raygui/spinner.go | 177 +++++++++++++++ raygui/style.go | 419 ++++++++++++++++++++++++++++++++++++ raygui/textbox.go | 64 ++++++ raygui/togglebutton.go | 48 +++++ raygui/togglegroup.go | 17 ++ raygui3_5/go.mod | 3 + raygui3_5/raygui.go | 40 ++++ 19 files changed, 1202 insertions(+), 37 deletions(-) create mode 100644 raygui/README.md create mode 100644 raygui/button.go create mode 100644 raygui/checkbox.go create mode 100644 raygui/combobox.go create mode 100644 raygui/label.go create mode 100644 raygui/progressbar.go create mode 100644 raygui/slider.go create mode 100644 raygui/sliderbar.go create mode 100644 raygui/spinner.go create mode 100644 raygui/style.go create mode 100644 raygui/textbox.go create mode 100644 raygui/togglebutton.go create mode 100644 raygui/togglegroup.go create mode 100644 raygui3_5/go.mod create mode 100644 raygui3_5/raygui.go diff --git a/examples/gui/button/go.mod b/examples/gui/button/go.mod index 5d22fef..38805c6 100644 --- a/examples/gui/button/go.mod +++ b/examples/gui/button/go.mod @@ -4,8 +4,8 @@ go 1.19 require ( github.com/Konstantin8105/raylib-go/raygui v0.0.0-20221122151443-e8a384ed1346 - github.com/Konstantin8105/raylib-go/raylib v0.0.0-20221122153952-2f5d9114177b + github.com/Konstantin8105/raylib-go/raylib v0.0.0-20221122155035-fe6d2c0ed32a ) replace github.com/Konstantin8105/raylib-go/raylib => ../../../raylib -replace github.com/Konstantin8105/raylib-go/raygui => ../../../raygui +replace github.com/Konstantin8105/raylib-go/raygui => ../../../raygui3_5 diff --git a/examples/gui/button/main.go b/examples/gui/button/main.go index 52b60df..f56d60f 100644 --- a/examples/gui/button/main.go +++ b/examples/gui/button/main.go @@ -8,7 +8,7 @@ import ( ) func main() { - rl.InitWindow(800, 450, "raylib [physics] example - box2d") + rl.InitWindow(800, 450, "raygui - button") rl.SetTargetFPS(60) diff --git a/raygui/README.md b/raygui/README.md new file mode 100644 index 0000000..bcbaaff --- /dev/null +++ b/raygui/README.md @@ -0,0 +1,5 @@ +## raygui [![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/raygui?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/raygui) + +raygui is simple and easy-to-use IMGUI (immediate mode GUI API) library. + +![screenshot](https://goo.gl/ieeaLj) diff --git a/raygui/button.go b/raygui/button.go new file mode 100644 index 0000000..741e3e9 --- /dev/null +++ b/raygui/button.go @@ -0,0 +1,38 @@ +package raygui + +import "github.com/gen2brain/raylib-go/raylib" + +// buttonColoring describes the per-state properties for a Button control. +type buttonColoring struct { + Border, Inside, Text Property +} + +// buttonColors lists the styling for each supported state. +var buttonColors = map[ControlState]buttonColoring{ + Normal: {ButtonDefaultBorderColor, ButtonDefaultInsideColor, ButtonDefaultTextColor}, + Clicked: {ButtonDefaultBorderColor, ButtonDefaultInsideColor, ButtonDefaultTextColor}, + Focused: {ButtonHoverBorderColor, ButtonHoverInsideColor, ButtonHoverTextColor}, + Pressed: {ButtonPressedBorderColor, ButtonPressedInsideColor, ButtonPressedTextColor}, +} + +// Button - Button element, returns true when clicked +func Button(bounds rl.Rectangle, text string) bool { + textHeight := int32(style[GlobalTextFontsize]) + textWidth := rl.MeasureText(text, textHeight) + + ConstrainRectangle(&bounds, textWidth, textWidth+GetStyle32(ButtonTextPadding), textHeight, textHeight+GetStyle32(ButtonTextPadding)/2) + + // Determine what state we're in and whether its valid. + state := GetInteractionState(bounds) + colors, exist := buttonColors[state] + if !exist { + return false + } + + // Draw control + b := bounds.ToInt32() + DrawBorderedRectangle(b, GetStyle32(ButtonBorderWidth), GetStyleColor(colors.Border), GetStyleColor(colors.Inside)) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, textHeight)/2)), b.Y+((b.Height/2)-(textHeight/2)), textHeight, GetStyleColor(colors.Text)) + + return state == Clicked +} diff --git a/raygui/checkbox.go b/raygui/checkbox.go new file mode 100644 index 0000000..99a8c6a --- /dev/null +++ b/raygui/checkbox.go @@ -0,0 +1,40 @@ +package raygui + +import "github.com/gen2brain/raylib-go/raylib" + +// checkboxColoring describes the per-state properties for a CheckBox control. +type checkboxColoring struct { + Border, Inside Property +} + +// checkboxColors lists the styling for each supported state. +var checkboxColors = map[ControlState]checkboxColoring{ + Normal: {CheckboxDefaultBorderColor, CheckboxDefaultInsideColor}, + Clicked: {CheckboxDefaultBorderColor, CheckboxDefaultInsideColor}, + Pressed: {CheckboxClickBorderColor, CheckboxClickInsideColor}, + Focused: {CheckboxHoverBorderColor, CheckboxHoverInsideColor}, +} + +// CheckBox - Check Box element, returns true when active +func CheckBox(bounds rl.Rectangle, checked bool) bool { + state := GetInteractionState(bounds) + colors, exists := checkboxColors[state] + if !exists { + return checked + } + + // Update control + if state == Clicked { + checked = !checked + } + + // Render control + box := bounds.ToInt32() + DrawBorderedRectangle(box, GetStyle32(ToggleBorderWidth), GetStyleColor(colors.Border), GetStyleColor(colors.Inside)) + if checked { + // Show the inner button. + DrawInsetRectangle(box, GetStyle32(CheckboxInsideWidth), GetStyleColor(CheckboxDefaultActiveColor)) + } + + return checked +} diff --git a/raygui/combobox.go b/raygui/combobox.go new file mode 100644 index 0000000..ce45adf --- /dev/null +++ b/raygui/combobox.go @@ -0,0 +1,75 @@ +package raygui + +import ( + "fmt" + + "github.com/gen2brain/raylib-go/raylib" +) + +// comboboxColoring describes the per-state colors for a Combobox control. +type comboboxColoring struct { + Border Property + Inside Property + List Property + Text Property +} + +// comboboxColors lists the styling for each supported state. +var comboboxColors = map[ControlState]comboboxColoring{ + Normal: {ComboboxDefaultBorderColor, ComboboxDefaultInsideColor, ComboboxDefaultListTextColor, ComboboxDefaultTextColor}, + Clicked: {ComboboxDefaultBorderColor, ComboboxDefaultInsideColor, ComboboxDefaultListTextColor, ComboboxDefaultTextColor}, + Focused: {ComboboxHoverBorderColor, ComboboxHoverInsideColor, ComboboxHoverListTextColor, ComboboxHoverTextColor}, + Pressed: {ComboboxPressedBorderColor, ComboboxPressedInsideColor, ComboboxPressedListTextColor, ComboboxPressedTextColor}, +} + +// ComboBox draws a simplified version of a ComboBox allowing the user to select a string +// from a list accompanied by an N/M counter. The widget does not provide a drop-down/completion +// or any input support. +func ComboBox(bounds rl.Rectangle, comboText []string, active int) int { + // Reject invalid selections and disable rendering. + comboCount := len(comboText) + if active < 0 || active >= comboCount { + rl.TraceLog(rl.LogWarning, "ComboBox active expects 0 <= active <= %d", comboCount) + return -1 + } + + // Calculate text dimensions. + textHeight := GetStyle32(GlobalTextFontsize) + activeText := comboText[active] + textWidth := rl.MeasureText(activeText, textHeight) + + // Ensure box is large enough. + ConstrainRectangle(&bounds, textWidth, textWidth+GetStyle32(ToggleTextPadding), textHeight, textHeight+GetStyle32(ToggleTextPadding)) + b := bounds.ToInt32() + + // Generate the worst-case sizing of the counter so we can avoid resizing it as the numbers go up/down. + clickWidth := rl.MeasureText(fmt.Sprintf("%d/%d", comboCount, comboCount), b.Height) + + // Counter shows the index of the selection and the maximum number, e.g. "1/3". + counter := rl.NewRectangle(bounds.X+bounds.Width+float32(style[ComboboxPadding]), bounds.Y, float32(clickWidth), float32(b.Height)) + c := counter.ToInt32() + + // Determine if the user is interacting with the control, and if so, which state it is in. + state := GetInteractionState(bounds, counter) + colors, exists := comboboxColors[state] + if !exists { + return active + } + + // Update the control when the user releases the mouse over it. + if state == Clicked { + // increment but wrap to 0 on reaching end-of-list. + active = (active + 1) % comboCount + } + + // Render the box itself + DrawBorderedRectangle(b, GetStyle32(ComboboxBorderWidth), GetStyleColor(colors.Border), GetStyleColor(colors.Inside)) + rl.DrawText(activeText, b.X+((b.Width/2)-(rl.MeasureText(activeText, textHeight)/2)), b.Y+((b.Height/2)-(textHeight/2)), textHeight, GetStyleColor(colors.Text)) + + // Render the accompanying "clicks" box showing the element counter. + DrawBorderedRectangle(c, GetStyle32(ComboboxBorderWidth), GetStyleColor(colors.Border), GetStyleColor(colors.Inside)) + counterText := fmt.Sprintf("%d/%d", active+1, comboCount) + rl.DrawText(counterText, c.X+((c.Width/2)-(rl.MeasureText(counterText, textHeight)/2)), c.Y+((c.Height/2)-(textHeight/2)), textHeight, GetStyleColor(colors.List)) + + return active +} diff --git a/raygui/go.mod b/raygui/go.mod index b893ee8..e054032 100644 --- a/raygui/go.mod +++ b/raygui/go.mod @@ -1,3 +1,7 @@ -module github.com/Konstantin8105/raylib-go/raygui +module github.com/gen2brain/raylib-go/raygui -go 1.19 +go 1.16 + +replace github.com/gen2brain/raylib-go/raylib => ../raylib + +require github.com/gen2brain/raylib-go/raylib v0.0.0-20211111173445-914ca1ffdc4d diff --git a/raygui/label.go b/raygui/label.go new file mode 100644 index 0000000..ba2dd94 --- /dev/null +++ b/raygui/label.go @@ -0,0 +1,21 @@ +package raygui + +import rl "github.com/gen2brain/raylib-go/raylib" + +// Label - Label element, show text +func Label(bounds rl.Rectangle, text string) { + LabelEx(bounds, text, rl.GetColor(uint(style[LabelTextColor])), rl.NewColor(0, 0, 0, 0), rl.NewColor(0, 0, 0, 0)) +} + +// LabelEx - Label element extended, configurable colors +func LabelEx(bounds rl.Rectangle, text string, textColor, border, inner rl.Color) { + textHeight := GetStyle32(GlobalTextFontsize) + textWidth := rl.MeasureText(text, textHeight) + + ConstrainRectangle(&bounds, textWidth, textWidth+GetStyle32(LabelTextPadding), textHeight, textHeight+GetStyle32(LabelTextPadding)/2) + + // Draw control + b := bounds.ToInt32() + DrawBorderedRectangle(b, GetStyle32(LabelBorderWidth), border, inner) + rl.DrawText(text, b.X+((b.Width/2)-(textWidth/2)), b.Y+((b.Height/2)-(textHeight/2)), textHeight, textColor) +} diff --git a/raygui/progressbar.go b/raygui/progressbar.go new file mode 100644 index 0000000..a9410b8 --- /dev/null +++ b/raygui/progressbar.go @@ -0,0 +1,23 @@ +package raygui + +import "github.com/gen2brain/raylib-go/raylib" + +// ProgressBar - Progress Bar element, shows current progress value +func ProgressBar(bounds rl.Rectangle, value float32) { + b := bounds.ToInt32() + if value > 1.0 { + value = 1.0 + } else if value < 0.0 { + value = 0.0 + } + + borderWidth := GetStyle32(ProgressbarBorderWidth) + progressBar := InsetRectangle(b, borderWidth) // backing rectangle + progressWidth := int32(value * float32(progressBar.Width)) // how much should be replaced with progress + progressValue := rl.RectangleInt32{progressBar.X, progressBar.Y, progressWidth, progressBar.Height} + + // Draw control + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, GetStyleColor(ProgressbarBorderColor)) + rl.DrawRectangle(progressBar.X, progressBar.Y, progressBar.Width, progressBar.Height, GetStyleColor(ProgressbarInsideColor)) + rl.DrawRectangle(progressValue.X, progressValue.Y, progressValue.Width, progressValue.Height, GetStyleColor(ProgressbarProgressColor)) +} diff --git a/raygui/raygui.go b/raygui/raygui.go index cf29678..6224a8a 100644 --- a/raygui/raygui.go +++ b/raygui/raygui.go @@ -1,40 +1,79 @@ +// Package raygui - Simple and easy-to-use IMGUI (immediate mode GUI API) library + package raygui -/* -#cgo CFLAGS: -DRAYGUI_IMPLEMENTATION -#include "../raylib/raygui.h" -#include -*/ -import "C" - import ( - "unsafe" - rl "github.com/Konstantin8105/raylib-go/raylib" + rl "github.com/gen2brain/raylib-go/raylib" ) -// int GuiToggleGroup(Rectangle bounds, const char *text, int active) -func ToggleGroup(bounds rl.Rectangle, text string, active int) int { - var cbounds C.struct_Rectangle - cbounds.x = C.float(bounds.X) - cbounds.y = C.float(bounds.Y) - cbounds.width = C.float(bounds.Width) - cbounds.height = C.float(bounds.Height) - ctext := C.CString(text) - defer C.free(unsafe.Pointer(ctext)) - cactive := C.int(active) - res := C.GuiToggleGroup(cbounds, ctext, cactive) - return int(res) +// GUI controls states +type ControlState int + +const ( + Disabled ControlState = iota + // Normal is the default state for rendering GUI elements. + Normal + // Focused indicates the mouse is hovering over the GUI element. + Focused + // Pressed indicates the mouse is hovering over the GUI element and LMB is pressed down. + Pressed + // Clicked indicates the mouse is hovering over the GUI element and LMB has just been released. + Clicked +) + +// IsColliding will return true if 'point' is within any of the given rectangles. +func IsInAny(point rl.Vector2, rectangles ...rl.Rectangle) bool { + for _, rect := range rectangles { + if rl.CheckCollisionPointRec(point, rect) { + return true + } + } + return false } -// bool GuiButton(Rectangle bounds, const char *text) -func Button(bounds rl.Rectangle, text string) bool { - var cbounds C.struct_Rectangle - cbounds.x = C.float(bounds.X) - cbounds.y = C.float(bounds.Y) - cbounds.width = C.float(bounds.Width) - cbounds.height = C.float(bounds.Height) - ctext := C.CString(text) - defer C.free(unsafe.Pointer(ctext)) - res := C.GuiButton(cbounds, ctext) - return bool(res) +// GetInteractionState determines the current state of a control based on mouse position and +// button states. +func GetInteractionState(rectangles ...rl.Rectangle) ControlState { + switch { + case !IsInAny(rl.GetMousePosition(), rectangles...): + return Normal + case rl.IsMouseButtonDown(rl.MouseLeftButton): + return Pressed + case rl.IsMouseButtonReleased(rl.MouseLeftButton) || rl.IsMouseButtonPressed(rl.MouseLeftButton): + return Clicked + default: + return Focused + } +} + +// Constrain rectangle will ensure that if width/height are below given minimums, they will +// be set to an ideal minimum. +func ConstrainRectangle(bounds *rl.Rectangle, minWidth, idealWidth, minHeight, idealHeight int32) { + if int32(bounds.Width) < minWidth { + bounds.Width = float32(idealWidth) + } + if int32(bounds.Height) < minHeight { + bounds.Height = float32(idealHeight) + } +} + +// InsetRectangle returns the dimensions of a rectangle inset by a margin within an outer rectangle. +func InsetRectangle(outer rl.RectangleInt32, inset int32) rl.RectangleInt32 { + return rl.RectangleInt32{ + X: outer.X + inset, Y: outer.Y + inset, + Width: outer.Width - 2*inset, Height: outer.Height - 2*inset, + } +} + +// DrawInsetRectangle is a helper to draw a box inset by a margin of an outer container. +func DrawInsetRectangle(outer rl.RectangleInt32, inset int32, color rl.Color) { + inside := InsetRectangle(outer, inset) + rl.DrawRectangle(inside.X, inside.Y, inside.Width, inside.Height, color) +} + +// DrawBorderedRectangle is a helper to draw a box with a border around it. +func DrawBorderedRectangle(bounds rl.RectangleInt32, borderWidth int32, borderColor, insideColor rl.Color) { + inside := InsetRectangle(bounds, borderWidth) + rl.DrawRectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height, borderColor) + rl.DrawRectangle(inside.X, inside.Y, inside.Width, inside.Height, insideColor) } diff --git a/raygui/slider.go b/raygui/slider.go new file mode 100644 index 0000000..a011818 --- /dev/null +++ b/raygui/slider.go @@ -0,0 +1,76 @@ +package raygui + +import rl "github.com/gen2brain/raylib-go/raylib" + +// Slider - Slider element, returns selected value +func Slider(bounds rl.Rectangle, value, minValue, maxValue float32) float32 { + b := bounds.ToInt32() + sliderPos := float32(0) + state := Normal + + buttonTravelDistance := float32(0) + mousePoint := rl.GetMousePosition() + + // Update control + if value < minValue { + value = minValue + } else if value >= maxValue { + value = maxValue + } + + sliderPos = (value - minValue) / (maxValue - minValue) + + sliderButton := rl.RectangleInt32{} + sliderButton.Width = (b.Width-(2*int32(style[SliderButtonBorderWidth])))/10 - 8 + sliderButton.Height = b.Height - (2 * int32(style[SliderBorderWidth]+2*style[SliderButtonBorderWidth])) + + sliderButtonMinPos := b.X + int32(style[SliderBorderWidth]) + int32(style[SliderButtonBorderWidth]) + sliderButtonMaxPos := b.X + b.Width - (int32(style[SliderBorderWidth]) + int32(style[SliderButtonBorderWidth]) + sliderButton.Width) + + buttonTravelDistance = float32(sliderButtonMaxPos - sliderButtonMinPos) + + sliderButton.X = b.X + int32(style[SliderBorderWidth]) + int32(style[SliderButtonBorderWidth]) + int32(sliderPos*buttonTravelDistance) + sliderButton.Y = b.Y + int32(style[SliderBorderWidth]) + int32(style[SliderButtonBorderWidth]) + + if rl.CheckCollisionPointRec(mousePoint, bounds) { + state = Focused + + if rl.IsMouseButtonDown(rl.MouseLeftButton) { + state = Pressed + } + + if state == Pressed && rl.IsMouseButtonDown(rl.MouseLeftButton) { + sliderButton.X = int32(mousePoint.X) - sliderButton.Width/2 + + if sliderButton.X <= sliderButtonMinPos { + sliderButton.X = sliderButtonMinPos + } else if sliderButton.X >= sliderButtonMaxPos { + sliderButton.X = sliderButtonMaxPos + } + + sliderPos = float32(sliderButton.X-sliderButtonMinPos) / buttonTravelDistance + } + } else { + state = Normal + } + + // Draw control + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(uint(style[SliderBorderColor]))) + rl.DrawRectangle(b.X+int32(style[SliderBorderWidth]), b.Y+int32(style[SliderBorderWidth]), b.Width-(2*int32(style[SliderBorderWidth])), b.Height-(2*int32(style[SliderBorderWidth])), rl.GetColor(uint(style[SliderInsideColor]))) + + switch state { + case Normal: + rl.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, rl.GetColor(uint(style[SliderDefaultColor]))) + break + case Focused: + rl.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, rl.GetColor(uint(style[SliderHoverColor]))) + break + case Pressed: + rl.DrawRectangle(sliderButton.X, sliderButton.Y, sliderButton.Width, sliderButton.Height, rl.GetColor(uint(style[SliderActiveColor]))) + break + default: + break + } + + return minValue + (maxValue-minValue)*sliderPos +} diff --git a/raygui/sliderbar.go b/raygui/sliderbar.go new file mode 100644 index 0000000..2ad9265 --- /dev/null +++ b/raygui/sliderbar.go @@ -0,0 +1,76 @@ +package raygui + +import rl "github.com/gen2brain/raylib-go/raylib" + +// SliderBar - Slider Bar element, returns selected value +func SliderBar(bounds rl.Rectangle, value, minValue, maxValue float32) float32 { + b := bounds.ToInt32() + state := Normal + + mousePoint := rl.GetMousePosition() + + fixedValue := float32(0) + fixedMinValue := float32(0) + + fixedValue = value - minValue + maxValue = maxValue - minValue + fixedMinValue = 0 + + // Update control + if fixedValue <= fixedMinValue { + fixedValue = fixedMinValue + } else if fixedValue >= maxValue { + fixedValue = maxValue + } + + sliderBar := rl.RectangleInt32{} + + sliderBar.X = b.X + int32(style[SliderBorderWidth]) + sliderBar.Y = b.Y + int32(style[SliderBorderWidth]) + sliderBar.Width = int32((fixedValue * (float32(b.Width) - 2*float32(style[SliderBorderWidth]))) / (maxValue - fixedMinValue)) + sliderBar.Height = b.Height - 2*int32(style[SliderBorderWidth]) + + if rl.CheckCollisionPointRec(mousePoint, bounds) { + state = Focused + + if rl.IsMouseButtonDown(rl.MouseLeftButton) { + state = Pressed + + sliderBar.Width = (int32(mousePoint.X) - b.X - int32(style[SliderBorderWidth])) + + if int32(mousePoint.X) <= (b.X + int32(style[SliderBorderWidth])) { + sliderBar.Width = 0 + } else if int32(mousePoint.X) >= (b.X + b.Width - int32(style[SliderBorderWidth])) { + sliderBar.Width = b.Width - 2*int32(style[SliderBorderWidth]) + } + } + } else { + state = Normal + } + + fixedValue = (float32(sliderBar.Width) * (maxValue - fixedMinValue)) / (float32(b.Width) - 2*float32(style[SliderBorderWidth])) + + // Draw control + rl.DrawRectangle(b.X, b.Y, b.Width, b.Height, rl.GetColor(uint(style[SliderbarBorderColor]))) + rl.DrawRectangle(b.X+int32(style[SliderBorderWidth]), b.Y+int32(style[SliderBorderWidth]), b.Width-(2*int32(style[SliderBorderWidth])), b.Height-(2*int32(style[SliderBorderWidth])), rl.GetColor(uint(style[SliderbarInsideColor]))) + + switch state { + case Normal: + rl.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, rl.GetColor(uint(style[SliderbarDefaultColor]))) + break + case Focused: + rl.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, rl.GetColor(uint(style[SliderbarHoverColor]))) + break + case Pressed: + rl.DrawRectangle(sliderBar.X, sliderBar.Y, sliderBar.Width, sliderBar.Height, rl.GetColor(uint(style[SliderbarActiveColor]))) + break + default: + break + } + + if minValue < 0 && maxValue > 0 { + rl.DrawRectangle((b.X+int32(style[SliderBorderWidth]))-int32(minValue*(float32(b.Width-(int32(style[SliderBorderWidth])*2))/maxValue)), sliderBar.Y, 1, sliderBar.Height, rl.GetColor(uint(style[SliderbarZeroLineColor]))) + } + + return fixedValue + minValue +} diff --git a/raygui/spinner.go b/raygui/spinner.go new file mode 100644 index 0000000..8527538 --- /dev/null +++ b/raygui/spinner.go @@ -0,0 +1,177 @@ +package raygui + +import ( + "fmt" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +// For spinner +var ( + framesCounter int + framesCounter2 int + valueSpeed bool +) + +// Spinner - Spinner element, returns selected value +func Spinner(bounds rl.Rectangle, value, minValue, maxValue int) int { + b := bounds.ToInt32() + state := Normal + + mousePoint := rl.GetMousePosition() + labelBoxBound := rl.RectangleInt32{b.X + b.Width/4 + 1, b.Y, b.Width / 2, b.Height} + leftButtonBound := rl.RectangleInt32{b.X, b.Y, b.Width / 4, b.Height} + rightButtonBound := rl.RectangleInt32{b.X + b.Width - b.Width/4 + 1, b.Y, b.Width / 4, b.Height} + + textHeight := int32(style[GlobalTextFontsize]) + textWidth := rl.MeasureText(fmt.Sprintf("%d", value), textHeight) + + buttonSide := 0 + + // Update control + if rl.CheckCollisionPointRec(mousePoint, leftButtonBound.ToFloat32()) || rl.CheckCollisionPointRec(mousePoint, rightButtonBound.ToFloat32()) || rl.CheckCollisionPointRec(mousePoint, labelBoxBound.ToFloat32()) { + if rl.IsKeyDown(rl.KeyLeft) { + state = Pressed + buttonSide = 1 + + if value > minValue { + value-- + } + } else if rl.IsKeyDown(rl.KeyRight) { + state = Pressed + buttonSide = 2 + + if value < maxValue { + value++ + } + } + } + + if rl.CheckCollisionPointRec(mousePoint, leftButtonBound.ToFloat32()) { + buttonSide = 1 + state = Focused + + if rl.IsMouseButtonDown(rl.MouseLeftButton) { + if !valueSpeed { + if value > minValue { + value-- + } + valueSpeed = true + } else { + framesCounter++ + } + + state = Pressed + + if value > minValue { + if framesCounter >= 30 { + value-- + } + } + } + } else if rl.CheckCollisionPointRec(mousePoint, rightButtonBound.ToFloat32()) { + buttonSide = 2 + state = Focused + + if rl.IsMouseButtonDown(rl.MouseLeftButton) { + if !valueSpeed { + if value < maxValue { + value++ + } + valueSpeed = true + } else { + framesCounter++ + } + + state = Pressed + + if value < maxValue { + if framesCounter >= 30 { + value++ + } + } + } + } else if !rl.CheckCollisionPointRec(mousePoint, labelBoxBound.ToFloat32()) { + buttonSide = 0 + } + + if rl.IsMouseButtonUp(rl.MouseLeftButton) { + valueSpeed = false + framesCounter = 0 + } + + // Draw control + switch state { + case Normal: + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(uint(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(uint(style[SpinnerDefaultButtonInsideColor]))) + + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(uint(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(uint(style[SpinnerDefaultButtonInsideColor]))) + + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", textHeight))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", textHeight))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultSymbolColor]))) + + rl.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, rl.GetColor(uint(style[SpinnerLabelBorderColor]))) + rl.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, rl.GetColor(uint(style[SpinnerLabelInsideColor]))) + + rl.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultTextColor]))) + break + case Focused: + if buttonSide == 1 { + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(uint(style[SpinnerHoverButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(uint(style[SpinnerHoverButtonInsideColor]))) + + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(uint(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(uint(style[SpinnerDefaultButtonInsideColor]))) + + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", textHeight))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerHoverSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", textHeight))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultSymbolColor]))) + } else if buttonSide == 2 { + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(uint(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(uint(style[SpinnerDefaultButtonInsideColor]))) + + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(uint(style[SpinnerHoverButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(uint(style[SpinnerHoverButtonInsideColor]))) + + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", textHeight))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", textHeight))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerHoverSymbolColor]))) + } + + rl.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, rl.GetColor(uint(style[SpinnerLabelBorderColor]))) + rl.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, rl.GetColor(uint(style[SpinnerLabelInsideColor]))) + + rl.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerHoverTextColor]))) + break + case Pressed: + if buttonSide == 1 { + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(uint(style[SpinnerPressedButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(uint(style[SpinnerPressedButtonInsideColor]))) + + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(uint(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(uint(style[SpinnerDefaultButtonInsideColor]))) + + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", textHeight))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerPressedSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", textHeight))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultSymbolColor]))) + } else if buttonSide == 2 { + rl.DrawRectangle(leftButtonBound.X, leftButtonBound.Y, leftButtonBound.Width, leftButtonBound.Height, rl.GetColor(uint(style[SpinnerDefaultButtonBorderColor]))) + rl.DrawRectangle(leftButtonBound.X+2, leftButtonBound.Y+2, leftButtonBound.Width-4, leftButtonBound.Height-4, rl.GetColor(uint(style[SpinnerDefaultButtonInsideColor]))) + + rl.DrawRectangle(rightButtonBound.X, rightButtonBound.Y, rightButtonBound.Width, rightButtonBound.Height, rl.GetColor(uint(style[SpinnerPressedButtonBorderColor]))) + rl.DrawRectangle(rightButtonBound.X+2, rightButtonBound.Y+2, rightButtonBound.Width-4, rightButtonBound.Height-4, rl.GetColor(uint(style[SpinnerPressedButtonInsideColor]))) + + rl.DrawText("-", leftButtonBound.X+(leftButtonBound.Width/2-(rl.MeasureText("+", textHeight))/2), leftButtonBound.Y+(leftButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerDefaultSymbolColor]))) + rl.DrawText("+", rightButtonBound.X+(rightButtonBound.Width/2-(rl.MeasureText("-", textHeight))/2), rightButtonBound.Y+(rightButtonBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerPressedSymbolColor]))) + } + + rl.DrawRectangle(labelBoxBound.X, labelBoxBound.Y, labelBoxBound.Width, labelBoxBound.Height, rl.GetColor(uint(style[SpinnerLabelBorderColor]))) + rl.DrawRectangle(labelBoxBound.X+1, labelBoxBound.Y+1, labelBoxBound.Width-2, labelBoxBound.Height-2, rl.GetColor(uint(style[SpinnerLabelInsideColor]))) + + rl.DrawText(fmt.Sprintf("%d", value), labelBoxBound.X+(labelBoxBound.Width/2-textWidth/2), labelBoxBound.Y+(labelBoxBound.Height/2-(textHeight/2)), textHeight, rl.GetColor(uint(style[SpinnerPressedTextColor]))) + break + default: + break + } + + return value +} diff --git a/raygui/style.go b/raygui/style.go new file mode 100644 index 0000000..2a76b0a --- /dev/null +++ b/raygui/style.go @@ -0,0 +1,419 @@ +// GUI element appearance can be dynamically configured through Property values, the set of which +// forms a theme called the Style. +package raygui + +import ( + "bufio" + "fmt" + "io/ioutil" + "strconv" + "strings" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +// Property - GUI property +type Property int32 + +// GUI properties enumeration +const ( + GlobalBaseColor Property = iota + GlobalBorderColor + GlobalTextColor + GlobalTextFontsize + GlobalBorderWidth + GlobalBackgroundColor + GlobalLinesColor + LabelBorderWidth + LabelTextColor + LabelTextPadding + ButtonBorderWidth + ButtonTextPadding + ButtonDefaultBorderColor + ButtonDefaultInsideColor + ButtonDefaultTextColor + ButtonHoverBorderColor + ButtonHoverInsideColor + ButtonHoverTextColor + ButtonPressedBorderColor + ButtonPressedInsideColor + ButtonPressedTextColor + ToggleTextPadding + ToggleBorderWidth + ToggleDefaultBorderColor + ToggleDefaultInsideColor + ToggleDefaultTextColor + ToggleHoverBorderColor + ToggleHoverInsideColor + ToggleHoverTextColor + TogglePressedBorderColor + TogglePressedInsideColor + TogglePressedTextColor + ToggleActiveBorderColor + ToggleActiveInsideColor + ToggleActiveTextColor + TogglegroupPadding + SliderBorderWidth + SliderButtonBorderWidth + SliderBorderColor + SliderInsideColor + SliderDefaultColor + SliderHoverColor + SliderActiveColor + SliderbarBorderColor + SliderbarInsideColor + SliderbarDefaultColor + SliderbarHoverColor + SliderbarActiveColor + SliderbarZeroLineColor + ProgressbarBorderColor + ProgressbarInsideColor + ProgressbarProgressColor + ProgressbarBorderWidth + SpinnerLabelBorderColor + SpinnerLabelInsideColor + SpinnerDefaultButtonBorderColor + SpinnerDefaultButtonInsideColor + SpinnerDefaultSymbolColor + SpinnerDefaultTextColor + SpinnerHoverButtonBorderColor + SpinnerHoverButtonInsideColor + SpinnerHoverSymbolColor + SpinnerHoverTextColor + SpinnerPressedButtonBorderColor + SpinnerPressedButtonInsideColor + SpinnerPressedSymbolColor + SpinnerPressedTextColor + ComboboxPadding + boundsWidth + boundsHeight + ComboboxBorderWidth + ComboboxDefaultBorderColor + ComboboxDefaultInsideColor + ComboboxDefaultTextColor + ComboboxDefaultListTextColor + ComboboxHoverBorderColor + ComboboxHoverInsideColor + ComboboxHoverTextColor + ComboboxHoverListTextColor + ComboboxPressedBorderColor + ComboboxPressedInsideColor + ComboboxPressedTextColor + ComboboxPressedListBorderColor + ComboboxPressedListInsideColor + ComboboxPressedListTextColor + CheckboxDefaultBorderColor + CheckboxDefaultInsideColor + CheckboxHoverBorderColor + CheckboxHoverInsideColor + CheckboxClickBorderColor + CheckboxClickInsideColor + CheckboxDefaultActiveColor + CheckboxInsideWidth + TextboxBorderWidth + TextboxBorderColor + TextboxInsideColor + TextboxTextColor + TextboxLineColor + TextboxTextFontsize + + // Add new properties above. + NumProperties +) + +// GUI property names (to read/write style text files) +var propertyName = [NumProperties]string{ + "GLOBAL_BASE_COLOR", + "GLOBAL_BORDER_COLOR", + "GLOBAL_TEXT_COLOR", + "GLOBAL_TEXT_FONTSIZE", + "GLOBAL_BORDER_WIDTH", + "BACKGROUND_COLOR", + "LINES_COLOR", + "LABEL_BORDER_WIDTH", + "LABEL_TEXT_COLOR", + "LABEL_TEXT_PADDING", + "BUTTON_BORDER_WIDTH", + "BUTTON_TEXT_PADDING", + "BUTTON_DEFAULT_BORDER_COLOR", + "BUTTON_DEFAULT_INSIDE_COLOR", + "BUTTON_DEFAULT_TEXT_COLOR", + "BUTTON_HOVER_BORDER_COLOR", + "BUTTON_HOVER_INSIDE_COLOR", + "BUTTON_HOVER_TEXT_COLOR", + "BUTTON_PRESSED_BORDER_COLOR", + "BUTTON_PRESSED_INSIDE_COLOR", + "BUTTON_PRESSED_TEXT_COLOR", + "TOGGLE_TEXT_PADDING", + "TOGGLE_BORDER_WIDTH", + "TOGGLE_DEFAULT_BORDER_COLOR", + "TOGGLE_DEFAULT_INSIDE_COLOR", + "TOGGLE_DEFAULT_TEXT_COLOR", + "TOGGLE_HOVER_BORDER_COLOR", + "TOGGLE_HOVER_INSIDE_COLOR", + "TOGGLE_HOVER_TEXT_COLOR", + "TOGGLE_PRESSED_BORDER_COLOR", + "TOGGLE_PRESSED_INSIDE_COLOR", + "TOGGLE_PRESSED_TEXT_COLOR", + "TOGGLE_ACTIVE_BORDER_COLOR", + "TOGGLE_ACTIVE_INSIDE_COLOR", + "TOGGLE_ACTIVE_TEXT_COLOR", + "TOGGLEGROUP_PADDING", + "SLIDER_BORDER_WIDTH", + "SLIDER_BUTTON_BORDER_WIDTH", + "SLIDER_BORDER_COLOR", + "SLIDER_INSIDE_COLOR", + "SLIDER_DEFAULT_COLOR", + "SLIDER_HOVER_COLOR", + "SLIDER_ACTIVE_COLOR", + "SLIDERBAR_BORDER_COLOR", + "SLIDERBAR_INSIDE_COLOR", + "SLIDERBAR_DEFAULT_COLOR", + "SLIDERBAR_HOVER_COLOR", + "SLIDERBAR_ACTIVE_COLOR", + "SLIDERBAR_ZERO_LINE_COLOR", + "PROGRESSBAR_BORDER_COLOR", + "PROGRESSBAR_INSIDE_COLOR", + "PROGRESSBAR_PROGRESS_COLOR", + "PROGRESSBAR_BORDER_WIDTH", + "SPINNER_LABEL_BORDER_COLOR", + "SPINNER_LABEL_INSIDE_COLOR", + "SPINNER_DEFAULT_BUTTON_BORDER_COLOR", + "SPINNER_DEFAULT_BUTTON_INSIDE_COLOR", + "SPINNER_DEFAULT_SYMBOL_COLOR", + "SPINNER_DEFAULT_TEXT_COLOR", + "SPINNER_HOVER_BUTTON_BORDER_COLOR", + "SPINNER_HOVER_BUTTON_INSIDE_COLOR", + "SPINNER_HOVER_SYMBOL_COLOR", + "SPINNER_HOVER_TEXT_COLOR", + "SPINNER_PRESSED_BUTTON_BORDER_COLOR", + "SPINNER_PRESSED_BUTTON_INSIDE_COLOR", + "SPINNER_PRESSED_SYMBOL_COLOR", + "SPINNER_PRESSED_TEXT_COLOR", + "COMBOBOX_PADDING", + "COMBOBOX_BUTTON_WIDTH", + "COMBOBOX_BUTTON_HEIGHT", + "COMBOBOX_BORDER_WIDTH", + "COMBOBOX_DEFAULT_BORDER_COLOR", + "COMBOBOX_DEFAULT_INSIDE_COLOR", + "COMBOBOX_DEFAULT_TEXT_COLOR", + "COMBOBOX_DEFAULT_LIST_TEXT_COLOR", + "COMBOBOX_HOVER_BORDER_COLOR", + "COMBOBOX_HOVER_INSIDE_COLOR", + "COMBOBOX_HOVER_TEXT_COLOR", + "COMBOBOX_HOVER_LIST_TEXT_COLOR", + "COMBOBOX_PRESSED_BORDER_COLOR", + "COMBOBOX_PRESSED_INSIDE_COLOR", + "COMBOBOX_PRESSED_TEXT_COLOR", + "COMBOBOX_PRESSED_LIST_BORDER_COLOR", + "COMBOBOX_PRESSED_LIST_INSIDE_COLOR", + "COMBOBOX_PRESSED_LIST_TEXT_COLOR", + "CHECKBOX_DEFAULT_BORDER_COLOR", + "CHECKBOX_DEFAULT_INSIDE_COLOR", + "CHECKBOX_HOVER_BORDER_COLOR", + "CHECKBOX_HOVER_INSIDE_COLOR", + "CHECKBOX_CLICK_BORDER_COLOR", + "CHECKBOX_CLICK_INSIDE_COLOR", + "CHECKBOX_STATUS_ACTIVE_COLOR", + "CHECKBOX_INSIDE_WIDTH", + "TEXTBOX_BORDER_WIDTH", + "TEXTBOX_BORDER_COLOR", + "TEXTBOX_INSIDE_COLOR", + "TEXTBOX_TEXT_COLOR", + "TEXTBOX_LINE_COLOR", + "TEXTBOX_TEXT_FONTSIZE", +} + +// Current GUI style (default light). +var style = [NumProperties]int64{ + 0xf5f5f5ff, // GLOBAL_BASE_COLOR + 0xf5f5f5ff, // GLOBAL_BORDER_COLOR + 0xf5f5f5ff, // GLOBAL_TEXT_COLOR + 10, // GLOBAL_TEXT_FONTSIZE + 1, // GLOBAL_BORDER_WIDTH + 0xf5f5f5ff, // BACKGROUND_COLOR + 0x90abb5ff, // LINES_COLOR + 1, // LABEL_BORDER_WIDTH + 0x4d4d4dff, // LABEL_TEXT_COLOR + 20, // LABEL_TEXT_PADDING + 2, // BUTTON_BORDER_WIDTH + 20, // BUTTON_TEXT_PADDING + 0x828282ff, // BUTTON_DEFAULT_BORDER_COLOR + 0xc8c8c8ff, // BUTTON_DEFAULT_INSIDE_COLOR + 0x4d4d4dff, // BUTTON_DEFAULT_TEXT_COLOR + 0xc8c8c8ff, // BUTTON_HOVER_BORDER_COLOR + 0xffffffff, // BUTTON_HOVER_INSIDE_COLOR + 0x868686ff, // BUTTON_HOVER_TEXT_COLOR + 0x7bb0d6ff, // BUTTON_PRESSED_BORDER_COLOR + 0xbcecffff, // BUTTON_PRESSED_INSIDE_COLOR + 0x5f9aa7ff, // BUTTON_PRESSED_TEXT_COLOR + 20, // TOGGLE_TEXT_PADDING + 1, // TOGGLE_BORDER_WIDTH + 0x828282ff, // TOGGLE_DEFAULT_BORDER_COLOR + 0xc8c8c8ff, // TOGGLE_DEFAULT_INSIDE_COLOR + 0x828282ff, // TOGGLE_DEFAULT_TEXT_COLOR + 0xc8c8c8ff, // TOGGLE_HOVER_BORDER_COLOR + 0xffffffff, // TOGGLE_HOVER_INSIDE_COLOR + 0x828282ff, // TOGGLE_HOVER_TEXT_COLOR + 0xbdd7eaff, // TOGGLE_PRESSED_BORDER_COLOR + 0xddf5ffff, // TOGGLE_PRESSED_INSIDE_COLOR + 0xafccd3ff, // TOGGLE_PRESSED_TEXT_COLOR + 0x7bb0d6ff, // TOGGLE_ACTIVE_BORDER_COLOR + 0xbcecffff, // TOGGLE_ACTIVE_INSIDE_COLOR + 0x5f9aa7ff, // TOGGLE_ACTIVE_TEXT_COLOR + 3, // TOGGLEGROUP_PADDING + 1, // SLIDER_BORDER_WIDTH + 1, // SLIDER_BUTTON_BORDER_WIDTH + 0x828282ff, // SLIDER_BORDER_COLOR + 0xc8c8c8ff, // SLIDER_INSIDE_COLOR + 0xbcecffff, // SLIDER_DEFAULT_COLOR + 0xffffffff, // SLIDER_HOVER_COLOR + 0xddf5ffff, // SLIDER_ACTIVE_COLOR + 0x828282ff, // SLIDERBAR_BORDER_COLOR + 0xc8c8c8ff, // SLIDERBAR_INSIDE_COLOR + 0xbcecffff, // SLIDERBAR_DEFAULT_COLOR + 0xffffffff, // SLIDERBAR_HOVER_COLOR + 0xddf5ffff, // SLIDERBAR_ACTIVE_COLOR + 0x828282ff, // SLIDERBAR_ZERO_LINE_COLOR + 0x828282ff, // PROGRESSBAR_BORDER_COLOR + 0xc8c8c8ff, // PROGRESSBAR_INSIDE_COLOR + 0xbcecffff, // PROGRESSBAR_PROGRESS_COLOR + 2, // PROGRESSBAR_BORDER_WIDTH + 0x828282ff, // SPINNER_LABEL_BORDER_COLOR + 0xc8c8c8ff, // SPINNER_LABEL_INSIDE_COLOR + 0x828282ff, // SPINNER_DEFAULT_BUTTON_BORDER_COLOR + 0xc8c8c8ff, // SPINNER_DEFAULT_BUTTON_INSIDE_COLOR + 0x000000ff, // SPINNER_DEFAULT_SYMBOL_COLOR + 0x000000ff, // SPINNER_DEFAULT_TEXT_COLOR + 0xc8c8c8ff, // SPINNER_HOVER_BUTTON_BORDER_COLOR + 0xffffffff, // SPINNER_HOVER_BUTTON_INSIDE_COLOR + 0x000000ff, // SPINNER_HOVER_SYMBOL_COLOR + 0x000000ff, // SPINNER_HOVER_TEXT_COLOR + 0x7bb0d6ff, // SPINNER_PRESSED_BUTTON_BORDER_COLOR + 0xbcecffff, // SPINNER_PRESSED_BUTTON_INSIDE_COLOR + 0x5f9aa7ff, // SPINNER_PRESSED_SYMBOL_COLOR + 0x000000ff, // SPINNER_PRESSED_TEXT_COLOR + 1, // COMBOBOX_PADDING + 30, // COMBOBOX_BUTTON_WIDTH + 20, // COMBOBOX_BUTTON_HEIGHT + 1, // COMBOBOX_BORDER_WIDTH + 0x828282ff, // COMBOBOX_DEFAULT_BORDER_COLOR + 0xc8c8c8ff, // COMBOBOX_DEFAULT_INSIDE_COLOR + 0x828282ff, // COMBOBOX_DEFAULT_TEXT_COLOR + 0x828282ff, // COMBOBOX_DEFAULT_LIST_TEXT_COLOR + 0xc8c8c8ff, // COMBOBOX_HOVER_BORDER_COLOR + 0xffffffff, // COMBOBOX_HOVER_INSIDE_COLOR + 0x828282ff, // COMBOBOX_HOVER_TEXT_COLOR + 0x828282ff, // COMBOBOX_HOVER_LIST_TEXT_COLOR + 0x7bb0d6ff, // COMBOBOX_PRESSED_BORDER_COLOR + 0xbcecffff, // COMBOBOX_PRESSED_INSIDE_COLOR + 0x5f9aa7ff, // COMBOBOX_PRESSED_TEXT_COLOR + 0x0078acff, // COMBOBOX_PRESSED_LIST_BORDER_COLOR + 0x66e7ffff, // COMBOBOX_PRESSED_LIST_INSIDE_COLOR + 0x0078acff, // COMBOBOX_PRESSED_LIST_TEXT_COLOR + 0x828282ff, // CHECKBOX_DEFAULT_BORDER_COLOR + 0xffffffff, // CHECKBOX_DEFAULT_INSIDE_COLOR + 0xc8c8c8ff, // CHECKBOX_HOVER_BORDER_COLOR + 0xffffffff, // CHECKBOX_HOVER_INSIDE_COLOR + 0x66e7ffff, // CHECKBOX_CLICK_BORDER_COLOR + 0xddf5ffff, // CHECKBOX_CLICK_INSIDE_COLOR + 0xbcecffff, // CHECKBOX_STATUS_ACTIVE_COLOR + 1, // CHECKBOX_INSIDE_WIDTH + 1, // TEXTBOX_BORDER_WIDTH + 0x828282ff, // TEXTBOX_BORDER_COLOR + 0xf5f5f5ff, // TEXTBOX_INSIDE_COLOR + 0x000000ff, // TEXTBOX_TEXT_COLOR + 0x000000ff, // TEXTBOX_LINE_COLOR + 10, // TEXTBOX_TEXT_FONTSIZE +} + +// LoadGuiStyle will load a GUI style from a file. See SaveGuiStyle. +func LoadGuiStyle(fileName string) { + file, err := rl.OpenAsset(fileName) + if err != nil { + rl.TraceLog(rl.LogWarning, "[%s] GUI style file could not be opened: %w", fileName, err) + return + } + defer file.Close() + + var lines []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) != 2 { + continue + } + + id := fields[0] + value := fields[1] + + for i := 0; i < len(propertyName); i++ { + if id == propertyName[i] { + if strings.HasPrefix(value, "0x") { + value = value[2:] + } + + v, err := strconv.ParseInt(value, 16, 64) + if err == nil { + style[i] = int64(v) + } + } + } + } +} + +// SaveGuiStyle will write the current GUI style to a file in a format suitable for loading via LoadGuiStyle. +func SaveGuiStyle(fileName string) { + var styleFile string + for i := 0; i < len(propertyName); i++ { + styleFile += fmt.Sprintf("%-40s0x%x\n", propertyName[i], GetStyleProperty(Property(i))) + } + + if err := ioutil.WriteFile(fileName, []byte(styleFile), 0644); err != nil { + rl.TraceLog(rl.LogWarning, "[%s] GUI style file could not be written: %w", fileName, err) + } +} + +// SetStyleProperty - Set one style property +func SetStyleProperty(guiProperty Property, value int64) { + style[guiProperty] = value +} + +// SetStyleColor - Set one style property to a color value +func SetStyleColor(guiProperty Property, value rl.Color) { + style[guiProperty] = int64(rl.ColorToInt(value)) +} + +// GetStyleProperty - Get one style property +func GetStyleProperty(guiProperty Property) int64 { + return style[int(guiProperty)] +} + +// BackgroundColor will return the current background color +func BackgroundColor() rl.Color { + return rl.GetColor(uint(style[GlobalBackgroundColor])) +} + +// LinesColor will return the current color for lines +func LinesColor() rl.Color { + return rl.GetColor(uint(style[GlobalLinesColor])) +} + +// TextColor will return the current color for normal state +func TextColor() rl.Color { + return rl.GetColor(uint(style[GlobalTextColor])) +} + +// GetStyle32 will return the int32 for a given property of the current style +func GetStyle32(property Property) int32 { + return int32(style[property]) +} + +// GetPropColor will return the Color value for a given property of the current style +func GetStyleColor(property Property) rl.Color { + return rl.GetColor(uint(style[property])) +} diff --git a/raygui/textbox.go b/raygui/textbox.go new file mode 100644 index 0000000..69b9dd4 --- /dev/null +++ b/raygui/textbox.go @@ -0,0 +1,64 @@ +package raygui + +import ( + "fmt" + "time" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +var backspaceHeld = false +var nextBackspace = time.Now() + +// BackspaceRepeatDelay controls the time backspace must be held down before it will repeat. +var BackspaceRepeatDelay = 300 * time.Millisecond + +// BackspaceRepeatInterval controls how frequently backspace registers after the initial delay. +var BackspaceRepeatInterval = 60 * time.Millisecond + +// TextBox - Text Box element, updates input text +func TextBox(bounds rl.Rectangle, text string) string { + b := bounds.ToInt32() + + letter := int32(-1) + + // Update control + state := GetInteractionState(bounds) + borderColor := TextboxBorderColor + if state == Pressed || state == Focused { + borderColor = ToggleActiveBorderColor + + framesCounter2++ + letter = rl.GetKeyPressed() + if letter != -1 { + if letter >= 32 && letter < 127 { + text = fmt.Sprintf("%s%c", text, letter) + } + } + + backspacing := rl.IsKeyPressed(rl.KeyBackspace) + if backspacing { + nextBackspace = time.Now().Add(BackspaceRepeatDelay) + } else if rl.IsKeyDown(rl.KeyBackspace) { + backspacing = time.Since(nextBackspace) >= 0 + if backspacing { + nextBackspace = time.Now().Add(BackspaceRepeatInterval) + } + } + if backspacing && len(text) > 0 { + text = text[:len(text)-1] + } + } + + DrawBorderedRectangle(b, GetStyle32(TextboxBorderWidth), GetStyleColor(borderColor), GetStyleColor(TextboxInsideColor)) + rl.DrawText(text, b.X+2, b.Y+int32(style[TextboxBorderWidth])+b.Height/2-int32(style[TextboxTextFontsize])/2, int32(style[TextboxTextFontsize]), GetStyleColor(TextboxTextColor)) + + if state == Focused || state == Pressed { + // Draw a cursor, when focused. + if (framesCounter2/20)%2 == 0 { + rl.DrawRectangle(b.X+4+rl.MeasureText(text, int32(style[GlobalTextFontsize])), b.Y+2, 1, b.Height-4, rl.GetColor(uint(style[TextboxLineColor]))) + } + } + + return text +} diff --git a/raygui/togglebutton.go b/raygui/togglebutton.go new file mode 100644 index 0000000..3848b62 --- /dev/null +++ b/raygui/togglebutton.go @@ -0,0 +1,48 @@ +package raygui + +import "github.com/gen2brain/raylib-go/raylib" + +// togglebuttonColoring describes the per-state colors for a ToggleBox control. +type togglebuttonColoring struct { + Border, Inside, Text Property +} + +// togglebuttonColors lists the styling for each supported state. +var togglebuttonColors = map[ControlState]togglebuttonColoring{ + Normal: {ToggleDefaultBorderColor, ToggleDefaultInsideColor, ToggleDefaultTextColor}, + // Hijacking 'Clicked' for the 'active' state. + Clicked: {ToggleActiveBorderColor, ToggleActiveInsideColor, ToggleDefaultTextColor}, + Pressed: {TogglePressedBorderColor, TogglePressedInsideColor, TogglePressedTextColor}, + Focused: {ToggleHoverBorderColor, ToggleHoverInsideColor, ToggleHoverTextColor}, +} + +// ToggleButton - Toggle Button element, returns true when active +func ToggleButton(bounds rl.Rectangle, text string, active bool) bool { + textHeight := int32(style[GlobalTextFontsize]) + textWidth := rl.MeasureText(text, textHeight) + + ConstrainRectangle(&bounds, textWidth, textWidth+GetStyle32(ToggleTextPadding), textHeight, textHeight+GetStyle32(ToggleTextPadding)) + + state := GetInteractionState(bounds) + if state == Clicked { + active = !active + state = Normal + } + + // Hijack 'Clicked' as the 'active' state + if state == Normal && active { + state = Clicked + } + + colors, exists := togglebuttonColors[state] + if !exists { + return active + } + + // Draw control + b := bounds.ToInt32() + DrawBorderedRectangle(b, GetStyle32(ToggleBorderWidth), GetStyleColor(colors.Border), GetStyleColor(colors.Inside)) + rl.DrawText(text, b.X+((b.Width/2)-(rl.MeasureText(text, textHeight)/2)), b.Y+((b.Height/2)-(textHeight/2)), textHeight, GetStyleColor(colors.Text)) + + return active +} diff --git a/raygui/togglegroup.go b/raygui/togglegroup.go new file mode 100644 index 0000000..33ccaa1 --- /dev/null +++ b/raygui/togglegroup.go @@ -0,0 +1,17 @@ +package raygui + +import "github.com/gen2brain/raylib-go/raylib" + +// ToggleGroup - Toggle Group element, returns toggled button index +func ToggleGroup(bounds rl.Rectangle, toggleText []string, active int) int { + padding := float32(style[TogglegroupPadding]) + for i := 0; i < len(toggleText); i++ { + if i == active { + ToggleButton(rl.NewRectangle(bounds.X+float32(i)*(bounds.Width+padding), bounds.Y, bounds.Width, bounds.Height), toggleText[i], true) + } else if ToggleButton(rl.NewRectangle(bounds.X+float32(i)*(bounds.Width+padding), bounds.Y, bounds.Width, bounds.Height), toggleText[i], false) { + active = i + } + } + + return active +} diff --git a/raygui3_5/go.mod b/raygui3_5/go.mod new file mode 100644 index 0000000..b893ee8 --- /dev/null +++ b/raygui3_5/go.mod @@ -0,0 +1,3 @@ +module github.com/Konstantin8105/raylib-go/raygui + +go 1.19 diff --git a/raygui3_5/raygui.go b/raygui3_5/raygui.go new file mode 100644 index 0000000..4a56d45 --- /dev/null +++ b/raygui3_5/raygui.go @@ -0,0 +1,40 @@ +package raygui3_5 + +/* +#cgo CFLAGS: -DRAYGUI_IMPLEMENTATION +#include "../raylib/raygui.h" +#include +*/ +import "C" + +import ( + "unsafe" + rl "github.com/Konstantin8105/raylib-go/raylib" +) + +// int GuiToggleGroup(Rectangle bounds, const char *text, int active) +func ToggleGroup(bounds rl.Rectangle, text string, active int) int { + var cbounds C.struct_Rectangle + cbounds.x = C.float(bounds.X) + cbounds.y = C.float(bounds.Y) + cbounds.width = C.float(bounds.Width) + cbounds.height = C.float(bounds.Height) + ctext := C.CString(text) + defer C.free(unsafe.Pointer(ctext)) + cactive := C.int(active) + res := C.GuiToggleGroup(cbounds, ctext, cactive) + return int(res) +} + +// bool GuiButton(Rectangle bounds, const char *text) +func Button(bounds rl.Rectangle, text string) bool { + var cbounds C.struct_Rectangle + cbounds.x = C.float(bounds.X) + cbounds.y = C.float(bounds.Y) + cbounds.width = C.float(bounds.Width) + cbounds.height = C.float(bounds.Height) + ctext := C.CString(text) + defer C.free(unsafe.Pointer(ctext)) + res := C.GuiButton(cbounds, ctext) + return bool(res) +}