Skip to content

igadmg/goclay

Repository files navigation

GoClay

Source code line by line port of Nick Barkers Clay library. For more indepth documentation look at Clay project page.

Quick Start

Do go get github.com/igadmg/goclay and import it in your code.

package main

import (
	"log"
	"os"
	"runtime/pprof"

	clay "github.com/igadmg/goclay"
)

var COLOR_LIGHT clay.Color = clay.Color{R: 224, G: 215, B: 210, A: 255}
var COLOR_RED clay.Color = clay.Color{R: 168, G: 66, B: 28, A: 255}
var COLOR_ORANGE clay.Color = clay.Color{R: 225, G: 138, B: 50, A: 255}

// Layout config is just a struct that can be declared statically, or inline
var sidebarItemConfig clay.ElementDeclaration = clay.ElementDeclaration{
	Layout: clay.LayoutConfig{
		Sizing: clay.Sizing{
			Width:  clay.GROW(),
			Height: clay.FIXED(50),
		},
	},
	BackgroundColor: COLOR_ORANGE,
}

func gx_Must[T any](v T, e error) T {
	if e != nil {
		panic(e)
	}
	return v
}

func pprofex_WriteCPUProfile(fileName string) (func(), error) {
	f, err := os.Create(fileName + ".prof")
	if err != nil {
		log.Fatal("could not create CPU profile: ", err)
		return func() {}, err
	}
	if err := pprof.StartCPUProfile(f); err != nil {
		log.Fatal("could not start CPU profile: ", err)
		return func() {}, err
	}

	return func() {
		pprof.StopCPUProfile()
		f.Close()
	}, nil
}

// Re-useable components are just normal functions
func SidebarItemComponent(ctx *clay.Context) {
	ctx.CLAY(sidebarItemConfig, func() {
		// children go here...
	})
}

func main() {
	defer gx_Must(pprofex_WriteCPUProfile("goclay"))()

	screenSize := clay.MakeDimensions(640, 480)
	mousePosition := clay.MakeVector2(160, 100)
	mouseWheel := clay.MakeVector2(0, 0)
	isMouseDown := false
	var profilePicture any = &struct{ ImageData []byte }{ImageData: nil}
	var deltaTime float32 = 0.1

	// Note: screenWidth and screenHeight will need to come from your environment, Clay doesn't handle window related tasks
	ui := clay.Initialize(screenSize, clay.ErrorHandler{})
	ui.SetMeasureTextFunction(func(text string, config *clay.TextElementConfig, userData any) clay.Dimensions {
		width := config.FontSize * 3 / 4
		return clay.MakeDimensions(
			width*uint16(len(text)),
			config.FontSize,
		)
	}, nil)

	for range 100000 {
		// Optional: Update internal layout dimensions to support resizing
		ui.SetLayoutDimensions(screenSize)
		// Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling & debug tools
		ui.SetPointerState(mousePosition, isMouseDown)
		// Optional: Update internal pointer position for handling mouseover / click / touch events - needed for scrolling and debug tools
		ui.UpdateScrollContainers(true, mouseWheel, deltaTime)

		// All clay layouts are declared between clay.BeginLayout and clay.EndLayout
		ui.BeginLayout()

		// An example of laying out a UI with a fixed width sidebar and flexible width main content
		ui.CLAY_ID(clay.ID("OuterContainer"),
			clay.ElementDeclaration{
				Layout: clay.LayoutConfig{
					Sizing:   clay.Sizing{Width: clay.GROW(), Height: clay.GROW()},
					Padding:  clay.PADDING_ALL(16),
					ChildGap: 16,
				},
				BackgroundColor: clay.Color{R: 250, G: 250, B: 255, A: 255},
			}, func() {
				ui.CLAY_ID(clay.ID("SideBar"),
					clay.ElementDeclaration{
						Layout: clay.LayoutConfig{
							LayoutDirection: clay.TOP_TO_BOTTOM,
							Sizing:          clay.Sizing{Width: clay.FIXED(300), Height: clay.GROW()},
							Padding:         clay.PADDING_ALL(16),
							ChildGap:        16,
						},
						BackgroundColor: COLOR_LIGHT,
					}, func() {
						ui.CLAY_ID(clay.ID("ProfilePictureOuter"),
							clay.ElementDeclaration{
								Layout: clay.LayoutConfig{
									Sizing:         clay.Sizing{Width: clay.GROW()},
									Padding:        clay.PADDING_ALL(16),
									ChildGap:       16,
									ChildAlignment: clay.ChildAlignment{Y: clay.ALIGN_Y_CENTER},
								},
								BackgroundColor: COLOR_RED,
							}, func() {
								ui.CLAY_ID(clay.ID("ProfilePicture"),
									clay.ElementDeclaration{
										Layout: clay.LayoutConfig{
											Sizing: clay.Sizing{Width: clay.FIXED(60), Height: clay.FIXED(60)},
										},
										Image: clay.ImageElementConfig{
											ImageData: profilePicture,
										},
									})
								ui.CLAY_ID(clay.ID("TextContent"),
									clay.ElementDeclaration{
										Layout: clay.LayoutConfig{
											Sizing: clay.Sizing{Width: clay.GROW(), Height: clay.GROW()},
										},
										BackgroundColor: COLOR_LIGHT,
									})

								ui.CLAY_TEXT("Clay - UI Library", &clay.TextElementConfig{
									FontSize:  24,
									TextColor: clay.Color{R: 255, G: 255, B: 255, A: 255},
								})
							})

						// Standard C code like loops etc work inside components
						for range 5 {
							SidebarItemComponent(ui)
						}

						ui.CLAY_ID(clay.ID("MainContent"),
							clay.ElementDeclaration{
								Layout: clay.LayoutConfig{
									Sizing: clay.Sizing{Width: clay.GROW(), Height: clay.GROW()},
								},
								BackgroundColor: COLOR_LIGHT,
							})
					})
			})

		// All clay layouts are declared between clay.BeginLayout and clay.EndLayout
		renderCommands := ui.EndLayout()

		// More comprehensive rendering examples can be found in the renderers/ directory
		for _, renderCommand := range renderCommands {
			_ = renderCommand
			//fmt.Printf("%d: %s\t\t%s\n", renderCommand.Id, renderCommand.BoundingBox, reflect.TypeOf(renderCommand.RenderData))
		}
	}
}

Using custom Math types

All dependent math types are declared in math.go, one can change them to their own implementations.

Project is still WIP

Some parts where not ported yet, Error reporting, mouse processing and scroll areas. But will be soon.

About

Rewrite of Nic Barkers CLay in go language.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages