Commits
Diff
diff --git a/adapters/raylib/main.go b/adapters/raylib/main.go
index 490bea7..945e221 100644
--- a/adapters/raylib/main.go
+++ b/adapters/raylib/main.go
@@ -6,0 +7,4 @@ import (
+ "hash/fnv"
+ "image"
+ ic "image/color"
+ "math"
@@ -15 +19 @@ func Init() *adapter.Adapter {
- wm := NewWindowManager(&a)
+ wm := NewWindowManager()
@@ -18,7 +21,0 @@ func Init() *adapter.Adapter {
- a.Library.UnloadCallback = func(key string) {
- t, exists := wm.Textures[key]
- if exists {
- rl.UnloadTexture(*t)
- delete(wm.Textures, key)
- }
- }
@@ -31 +28 @@ func Init() *adapter.Adapter {
- wm.Draw(state)
+ wm.Draw(state, &a)
@@ -38,11 +35,21 @@ type WindowManager struct {
- FPSCounterOn bool
- FPS int32
- FPSCounter fps.FPSCounter
- Textures map[string]*rl.Texture2D
- Width int32
- Height int32
- CurrentEvents map[int]bool
- MousePosition []int
- MouseState bool
- ContextState bool
- Adapter *adapter.Adapter
+ FPSCounterOn bool
+ FPS int32
+ FPSCounter fps.FPSCounter
+ Textures map[int]Texture
+ CanvasTextures map[int]CanvasTexture
+ Width int32
+ Height int32
+ CurrentEvents map[int]bool
+ MousePosition []int
+ MouseState bool
+ ContextState bool
+}
+
+type Texture struct {
+ Hash uint64
+ Image rl.Texture2D
+}
+
+type CanvasTexture struct {
+ Hash uint64
+ Image rl.Texture2D
@@ -52 +59 @@ type WindowManager struct {
-func NewWindowManager(a *adapter.Adapter) *WindowManager {
+func NewWindowManager() *WindowManager {
@@ -60 +66,0 @@ func NewWindowManager(a *adapter.Adapter) *WindowManager {
- Adapter: a,
@@ -81 +87,4 @@ func (wm *WindowManager) LoadTextures(nodes []element.State) {
- wm.Textures = make(map[string]*rl.Texture2D)
+ wm.Textures = make(map[int]Texture)
+ }
+ if wm.CanvasTextures == nil {
+ wm.CanvasTextures = make(map[int]CanvasTexture)
@@ -84,8 +93,12 @@ func (wm *WindowManager) LoadTextures(nodes []element.State) {
- for _, node := range nodes {
- if len(node.Textures) > 0 {
- for _, key := range node.Textures {
- _, exists := wm.Textures[key]
- texture, inLibrary := wm.Adapter.Library.Get(key)
- if !exists && inLibrary {
- textureLoaded := rl.LoadTextureFromImage(rl.NewImageFromImage(texture))
- wm.Textures[key] = &textureLoaded
+ for i, node := range nodes {
+ if node.Texture != nil {
+ hash := computeImageHash(node.Texture)
+ currentTexture, exists := wm.Textures[i]
+ if !exists || currentTexture.Hash != hash {
+ if exists {
+ rl.UnloadTexture(currentTexture.Image)
+ }
+ texture := rl.LoadTextureFromImage(rl.NewImageFromImage(node.Texture))
+ wm.Textures[i] = Texture{
+ Hash: hash,
+ Image: texture,
@@ -93,0 +107 @@ func (wm *WindowManager) LoadTextures(nodes []element.State) {
+ }
@@ -94,0 +109,13 @@ func (wm *WindowManager) LoadTextures(nodes []element.State) {
+ if node.Canvas != nil && node.Canvas.Context != nil {
+ hash := computeImageHash(node.Canvas.Context)
+ currentCanvasTexture, exists := wm.CanvasTextures[i]
+ if !exists || currentCanvasTexture.Hash != hash {
+ if exists {
+ rl.UnloadTexture(currentCanvasTexture.Image)
+ }
+ texture := rl.LoadTextureFromImage(rl.NewImageFromImage(node.Canvas.Context))
+ wm.CanvasTextures[i] = CanvasTexture{
+ Hash: hash,
+ Image: texture,
+ }
+ }
@@ -100 +127 @@ func (wm *WindowManager) LoadTextures(nodes []element.State) {
-func (wm *WindowManager) Draw(nodes []element.State) {
+func (wm *WindowManager) Draw(nodes []element.State, a *adapter.Adapter) {
@@ -101,0 +129 @@ func (wm *WindowManager) Draw(nodes []element.State) {
+ // !TODO: Only Draw whats in fov
@@ -103 +131,12 @@ func (wm *WindowManager) Draw(nodes []element.State) {
- wm.GetEvents()
+ cw := rl.GetScreenWidth()
+ ch := rl.GetScreenHeight()
+ if cw != int(wm.Width) || ch != int(wm.Height) {
+ e := element.Event{
+ Name: "windowresize",
+ Data: map[string]int{"width": cw, "height": ch},
+ }
+ wm.Width = int32(cw)
+ wm.Height = int32(ch)
+ a.DispatchEvent(e)
+ }
+ wm.GetEvents(a)
@@ -105 +144 @@ func (wm *WindowManager) Draw(nodes []element.State) {
- for _, node := range nodes {
+ for i, node := range nodes {
@@ -107,0 +147 @@ func (wm *WindowManager) Draw(nodes []element.State) {
+ p := node.Padding
@@ -117,7 +157,7 @@ func (wm *WindowManager) Draw(nodes []element.State) {
- if node.Textures != nil {
- for _, v := range node.Textures {
- texture, exists := wm.Textures[v]
- if exists {
- rl.DrawTexture(*texture, int32(node.X), int32(node.Y), rl.White)
- }
- }
+ if node.Canvas != nil {
+ rl.DrawTexture(wm.CanvasTextures[i].Image, int32(node.X), int32(node.Y), rl.White)
+ }
+
+ if node.Texture != nil {
+ r, g, b, a := node.Color.RGBA()
+ rl.DrawTexture(wm.Textures[i].Image, int32(node.X+p.Left+node.Border.Left.Width), int32(node.Y+p.Top+node.Border.Top.Width), ic.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)})
@@ -142,0 +183,25 @@ func (wm *WindowManager) Draw(nodes []element.State) {
+// computeImageHash calculates a hash of the image data
+func computeImageHash(img *image.RGBA) uint64 {
+ percentage := 10.0
+ if percentage <= 0 || percentage > 100 {
+ percentage = 100 // Ensure valid percentage range
+ }
+
+ totalPixels := len(img.Pix) / 4 // Each pixel consists of 4 bytes (RGBA)
+
+ if totalPixels == 0 {
+ return 0
+ } else {
+ hasher := fnv.New64a()
+ step := int(math.Max(1, float64(totalPixels)/(float64(totalPixels)*(percentage/100))))
+
+ // Process a subset of the image data based on the percentage
+ for i := 0; i < len(img.Pix); i += step * 4 {
+ hasher.Write(img.Pix[i : i+4])
+ }
+
+ return hasher.Sum64()
+ }
+
+}
+
@@ -163,14 +228 @@ func DrawRoundedRect(x, y, width, height float32, topLeftRadius, topRightRadius,
-func (wm *WindowManager) GetEvents() {
- cw := rl.GetScreenWidth()
- ch := rl.GetScreenHeight()
- if cw != int(wm.Width) || ch != int(wm.Height) {
- e := element.Event{
- Name: "windowresize",
- Data: map[string]int{"width": cw, "height": ch},
- }
- wm.Width = int32(cw)
- wm.Height = int32(ch)
- wm.Adapter.DispatchEvent(e)
- }
-
- // Other keys
+func (wm *WindowManager) GetEvents(a *adapter.Adapter) {
@@ -188 +240 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(keydown)
+ a.DispatchEvent(keydown)
@@ -195 +247 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(keyup)
+ a.DispatchEvent(keyup)
@@ -203 +255 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(element.Event{
+ a.DispatchEvent(element.Event{
@@ -213 +265 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(element.Event{
+ a.DispatchEvent(element.Event{
@@ -218 +270 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(element.Event{
+ a.DispatchEvent(element.Event{
@@ -228 +280 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(element.Event{
+ a.DispatchEvent(element.Event{
@@ -233 +285 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(element.Event{
+ a.DispatchEvent(element.Event{
@@ -243 +295 @@ func (wm *WindowManager) GetEvents() {
- wm.Adapter.DispatchEvent(element.Event{
+ a.DispatchEvent(element.Event{
package raylib
import (
adapter "gui/adapters"
"gui/element"
"gui/fps"
"slices"
"sort"
rl "github.com/gen2brain/raylib-go/raylib"
)
func Init() *adapter.Adapter {
a := adapter.Adapter{}
wm := NewWindowManager(&a)
a.Init = func(width, height int) {
wm.OpenWindow(int32(width), int32(height))
a.Library.UnloadCallback = func(key string) {
t, exists := wm.Textures[key]
if exists {
rl.UnloadTexture(*t)
delete(wm.Textures, key)
}
}
}
a.Load = wm.LoadTextures
a.Render = func(state []element.State) {
if rl.WindowShouldClose() {
a.DispatchEvent(element.Event{Name: "close"})
}
wm.Draw(state)
}
return &a
}
// WindowManager manages the window and rectangles
type WindowManager struct {
FPSCounterOn bool
FPS int32
FPSCounter fps.FPSCounter
Textures map[string]*rl.Texture2D
Width int32
Height int32
CurrentEvents map[int]bool
MousePosition []int
MouseState bool
ContextState bool
Adapter *adapter.Adapter
}
// NewWindowManager creates a new WindowManager instance
func NewWindowManager(a *adapter.Adapter) *WindowManager {
fpsCounter := fps.NewFPSCounter()
mp := rl.GetMousePosition()
return &WindowManager{
FPSCounter: *fpsCounter,
CurrentEvents: make(map[int]bool, 256),
MousePosition: []int{int(mp.X), int(mp.Y)},
Adapter: a,
}
}
// OpenWindow opens the window
func (wm *WindowManager) OpenWindow(width, height int32) {
rl.InitWindow(width, height, "")
rl.SetTargetFPS(30)
wm.Width = width
wm.Height = height
// Enable window resizing
rl.SetWindowState(rl.FlagWindowResizable)
}
func (wm *WindowManager) SetFPS(fps int) {
wm.FPS = int32(fps)
rl.SetTargetFPS(int32(fps))
}
func (wm *WindowManager) LoadTextures(nodes []element.State) {
if wm.Textures == nil {
wm.Textures = make(map[string]*rl.Texture2D)
}
for _, node := range nodes {
if len(node.Textures) > 0 {
for _, key := range node.Textures {
_, exists := wm.Textures[key]
texture, inLibrary := wm.Adapter.Library.Get(key)
if !exists && inLibrary {
textureLoaded := rl.LoadTextureFromImage(rl.NewImageFromImage(texture))
wm.Textures[key] = &textureLoaded
}
}
}
}
}
// Draw draws all nodes on the window
func (wm *WindowManager) Draw(nodes []element.State) {
indexes := []float32{0}
rl.BeginDrawing()
wm.GetEvents()
for a := 0; a < len(indexes); a++ {
for _, node := range nodes {
if node.Z == indexes[a] {
DrawRoundedRect(node.X,
node.Y,
node.Width+node.Border.Left.Width+node.Border.Right.Width,
node.Height+node.Border.Top.Width+node.Border.Bottom.Width,
node.Border.Radius.TopLeft, node.Border.Radius.TopRight, node.Border.Radius.BottomLeft, node.Border.Radius.BottomRight, node.Background)
// Draw the border based on the style for each side
if node.Textures != nil {
for _, v := range node.Textures {
texture, exists := wm.Textures[v]
if exists {
rl.DrawTexture(*texture, int32(node.X), int32(node.Y), rl.White)
}
}
}
} else {
if !slices.Contains(indexes, node.Z) {
indexes = append(indexes, node.Z)
sort.Slice(indexes, func(i, j int) bool {
return indexes[i] < indexes[j]
})
}
}
}
}
if wm.FPSCounterOn {
wm.FPSCounter.Update()
wm.FPSCounter.Draw(10, 10, 10, rl.DarkGray)
}
rl.EndDrawing()
}
func DrawRoundedRect(x, y, width, height float32, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius float32, color rl.Color) {
// Draw the main rectangle excluding corners
rl.DrawRectangle(int32(x+topLeftRadius), int32(y), int32(width-topLeftRadius-topRightRadius), int32(height), color)
rl.DrawRectangle(int32(x), int32(y+topLeftRadius), int32(topLeftRadius), int32(height-topLeftRadius-bottomLeftRadius), color)
rl.DrawRectangle(int32(x+width-topRightRadius), int32(y+topRightRadius), int32(topRightRadius), int32(height-topRightRadius-bottomRightRadius), color)
rl.DrawRectangle(int32(x+bottomLeftRadius), int32(y+height-bottomLeftRadius), int32(width-bottomLeftRadius-bottomRightRadius), int32(bottomLeftRadius), color)
// Draw the corner circles
rl.DrawCircleSector(rl.Vector2{X: x + topLeftRadius, Y: y + topLeftRadius}, topLeftRadius, 180, 270, 16, color)
rl.DrawCircleSector(rl.Vector2{X: x + width - topRightRadius, Y: y + topRightRadius}, topRightRadius, 270, 360, 16, color)
rl.DrawCircleSector(rl.Vector2{X: x + width - bottomRightRadius, Y: y + height - bottomRightRadius}, bottomRightRadius, 0, 90, 16, color)
rl.DrawCircleSector(rl.Vector2{X: x + bottomLeftRadius, Y: y + height - bottomLeftRadius}, bottomLeftRadius, 90, 180, 16, color)
// Draw rectangle parts to fill the gaps
rl.DrawRectangle(int32(x+topLeftRadius), int32(y), int32(width-topLeftRadius-topRightRadius), int32(topLeftRadius), color) // Top
rl.DrawRectangle(int32(x), int32(y+topLeftRadius), int32(topLeftRadius), int32(height-topLeftRadius-bottomLeftRadius), color) // Left
rl.DrawRectangle(int32(x+width-topRightRadius), int32(y+topRightRadius), int32(topRightRadius), int32(height-topRightRadius-bottomRightRadius), color) // Right
rl.DrawRectangle(int32(x+bottomLeftRadius), int32(y+height-bottomLeftRadius), int32(width-bottomLeftRadius-bottomRightRadius), int32(bottomLeftRadius), color) // Bottom
}
func (wm *WindowManager) GetEvents() {
cw := rl.GetScreenWidth()
ch := rl.GetScreenHeight()
if cw != int(wm.Width) || ch != int(wm.Height) {
e := element.Event{
Name: "windowresize",
Data: map[string]int{"width": cw, "height": ch},
}
wm.Width = int32(cw)
wm.Height = int32(ch)
wm.Adapter.DispatchEvent(e)
}
// Other keys
for i := 8; i <= 255; i++ {
// for i := 32; i < 126; i++ {
isDown := rl.IsKeyDown(int32(i))
if wm.CurrentEvents[i] != isDown {
if isDown {
keydown := element.Event{
Name: "keydown",
Data: i,
}
wm.CurrentEvents[i] = true
wm.Adapter.DispatchEvent(keydown)
} else {
keyup := element.Event{
Name: "keyup",
Data: i,
}
wm.CurrentEvents[i] = false
wm.Adapter.DispatchEvent(keyup)
}
}
}
// mouse move, ctrl, shift etc
mp := rl.GetMousePosition()
if wm.MousePosition[0] != int(mp.X) || wm.MousePosition[1] != int(mp.Y) {
wm.Adapter.DispatchEvent(element.Event{
Name: "mousemove",
Data: []int{int(mp.X), int(mp.Y)},
})
wm.MousePosition[0] = int(mp.X)
wm.MousePosition[1] = int(mp.Y)
}
md := rl.IsMouseButtonDown(rl.MouseLeftButton)
if md != wm.MouseState {
if md {
wm.Adapter.DispatchEvent(element.Event{
Name: "mousedown",
})
wm.MouseState = true
} else {
wm.Adapter.DispatchEvent(element.Event{
Name: "mouseup",
})
wm.MouseState = false
}
}
cs := rl.IsMouseButtonPressed(rl.MouseRightButton)
if cs != wm.ContextState {
if cs {
wm.Adapter.DispatchEvent(element.Event{
Name: "contextmenudown",
})
wm.ContextState = true
} else {
wm.Adapter.DispatchEvent(element.Event{
Name: "contextmenuup",
})
wm.ContextState = false
}
}
wd := rl.GetMouseWheelMove()
if wd != 0 {
wm.Adapter.DispatchEvent(element.Event{
Name: "scroll",
Data: int(wd),
})
}
}