commit 429a17cbf5484144aa9ebc3f09bd11b35d4728e3 Author: illyum Date: Sun Oct 20 15:48:38 2024 -0600 Initial Commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8e200d9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/ui.iml b/.idea/ui.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/ui.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/arial.ttf b/arial.ttf new file mode 100644 index 0000000..8682d94 Binary files /dev/null and b/arial.ttf differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..317a6aa --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module main + +go 1.23.0 + +require ( + github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect + github.com/go-gl/gltext v0.0.0-20170328174336-01a355945a70 // indirect + github.com/go-gl/mathgl v1.1.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + golang.org/x/image v0.21.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..448239e --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA= +github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/gltext v0.0.0-20170328174336-01a355945a70 h1:jLHSn7wgzCXTsZoldNrhgFS5fasJxb8Htn+iaYsZt88= +github.com/go-gl/gltext v0.0.0-20170328174336-01a355945a70/go.mod h1:KpCmHMLAPxpCBuDN9Tp7mjJA7lwFZNc2bZ0rCi/X6yc= +github.com/go-gl/mathgl v1.1.0 h1:0lzZ+rntPX3/oGrDzYGdowSLC2ky8Osirvf5uAwfIEA= +github.com/go-gl/mathgl v1.1.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= +golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.exe b/main.exe new file mode 100644 index 0000000..f92312e Binary files /dev/null and b/main.exe differ diff --git a/stuff.go b/stuff.go new file mode 100644 index 0000000..19d5356 --- /dev/null +++ b/stuff.go @@ -0,0 +1,483 @@ +package main + +import ( + "fmt" + "os" + "runtime" + "strings" + + "github.com/go-gl/gl/v4.1-core/gl" + "github.com/go-gl/glfw/v3.3/glfw" + "github.com/go-gl/gltext" + "github.com/go-gl/mathgl/mgl32" +) + +func init() { + // GLFW event handling must run on the main OS thread + runtime.LockOSThread() +} + +// Position represents a 2D position +type Position struct { + X, Y float32 +} + +// Size represents the width and height of a UI element +type Size struct { + Width, Height float32 +} + +// EventType represents the type of an event +type EventType int + +const ( + EventTypeClick EventType = iota + EventTypeMouseMove + EventTypeHover +) + +// Event represents an input event +type Event struct { + Type EventType + Position Position +} + +// UIElement defines the interface for UI elements +type UIElement interface { + Draw() + Update() + HandleEvent(event Event) +} + +// Container defines the interface for UI containers +type Container interface { + UIElement + AddChild(UIElement) + RemoveChild(UIElement) +} + +// Context manages the OpenGL context and resources +type Context struct { + shaderProgram uint32 + font *gltext.Font + windowWidth int + windowHeight int +} + +func NewContext(windowWidth, windowHeight int) *Context { + return &Context{ + windowWidth: windowWidth, + windowHeight: windowHeight, + } +} + +func (c *Context) InitOpenGL() { + if err := gl.Init(); err != nil { + panic(err) + } + version := gl.GoStr(gl.GetString(gl.VERSION)) + fmt.Println("OpenGL version", version) + + // Initialize shader program + c.shaderProgram = NewShaderProgram(vertexShaderSource, fragmentShaderSource) + + // Load font using gltext + fontData, err := os.Open("arial.ttf") + if err != nil { + panic(err) + } + defer fontData.Close() + + // Load the truetype font: specify scale, rune range (ASCII), and direction + font, err := gltext.LoadTruetype(fontData, 24, 32, 127, gltext.LeftToRight) + if err != nil { + panic(err) + } + c.font = font +} + +func (c *Context) Clear() { + gl.ClearColor(0.0, 0.0, 0.0, 1.0) + gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) +} + +func (c *Context) SwapBuffers(window *glfw.Window) { + window.SwapBuffers() +} + +// Window represents the main application window +type Window struct { + width int + height int + title string + transparent bool + panels []*Panel + glContext *Context + glfwWindow *glfw.Window +} + +func NewWindow(width, height int, title string) *Window { + if err := glfw.Init(); err != nil { + panic(err) + } + glfw.WindowHint(glfw.ContextVersionMajor, 4) + glfw.WindowHint(glfw.ContextVersionMinor, 1) + glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) + glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) + + glfwWindow, err := glfw.CreateWindow(width, height, title, nil, nil) + if err != nil { + panic(err) + } + glfwWindow.MakeContextCurrent() + + return &Window{ + width: width, + height: height, + title: title, + panels: []*Panel{}, + glfwWindow: glfwWindow, + } +} + +func (w *Window) SetTransparent(t bool) { + w.transparent = t + // Apply transparency settings if needed +} + +func (w *Window) DefineContext() *Context { + w.glContext = NewContext(w.width, w.height) + w.glContext.InitOpenGL() + return w.glContext +} + +func (w *Window) CreatePanel() *Panel { + panel := NewPanel() + w.panels = append(w.panels, panel) + return panel +} + +func (w *Window) registerCallbacks() { + w.glfwWindow.SetCursorPosCallback(w.cursorPosCallback) + w.glfwWindow.SetMouseButtonCallback(w.mouseButtonCallback) +} + +func (w *Window) cursorPosCallback(window *glfw.Window, xpos float64, ypos float64) { + event := Event{ + Type: EventTypeMouseMove, + Position: Position{X: float32(xpos), Y: float32(ypos)}, + } + w.handleEvent(event) +} + +func (w *Window) mouseButtonCallback(window *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) { + if button == glfw.MouseButtonLeft && action == glfw.Press { + xpos, ypos := window.GetCursorPos() + event := Event{ + Type: EventTypeClick, + Position: Position{X: float32(xpos), Y: float32(ypos)}, + } + w.handleEvent(event) + } +} + +func (w *Window) handleEvent(event Event) { + for _, panel := range w.panels { + panel.HandleEvent(event) + } +} + +func (w *Window) update() { + for _, panel := range w.panels { + panel.Update() + } +} + +func (w *Window) draw() { + for _, panel := range w.panels { + panel.Draw() + } +} + +func (w *Window) Start() { + w.registerCallbacks() + for !w.glfwWindow.ShouldClose() { + glfw.PollEvents() + w.glContext.Clear() + + w.update() + w.draw() + + w.glContext.SwapBuffers(w.glfwWindow) + } + glfw.Terminate() +} + +// Panel is a container for UI elements +type Panel struct { + children []UIElement +} + +func NewPanel() *Panel { + return &Panel{ + children: []UIElement{}, + } +} + +func (p *Panel) AddChild(e UIElement) { + p.children = append(p.children, e) +} + +func (p *Panel) RemoveChild(e UIElement) { + // Implement child removal logic if needed +} + +func (p *Panel) Draw() { + for _, child := range p.children { + child.Draw() + } +} + +func (p *Panel) Update() { + for _, child := range p.children { + child.Update() + } +} + +func (p *Panel) HandleEvent(event Event) { + for _, child := range p.children { + child.HandleEvent(event) + } +} + +// Button is a clickable UI element +type Button struct { + label string + onClick func() + Position + Size + isHovered bool + context *Context +} + +func NewButton(label string, onClick func(), context *Context) *Button { + return &Button{ + label: label, + onClick: onClick, + context: context, + Position: Position{X: 0, Y: 0}, + Size: Size{Width: 100, Height: 50}, + isHovered: false, + } +} + +func (b *Button) contains(pos Position) bool { + return pos.X >= b.Position.X && pos.X <= b.Position.X+b.Size.Width && + pos.Y >= b.Position.Y && pos.Y <= b.Position.Y+b.Size.Height +} + +func (b *Button) Draw() { + var color [4]float32 + if b.isHovered { + color = [4]float32{0.7, 0.7, 0.7, 1.0} // Lighter when hovered + } else { + color = [4]float32{0.5, 0.5, 0.5, 1.0} + } + + rect := NewRectangle(b.Position.X, b.Position.Y, b.Size.Width, b.Size.Height, color, b.context) + rect.Draw() + rect.Destroy() + + // Draw the label + x := b.Position.X + 10 + y := b.Position.Y + b.Size.Height/2 + 8 // Approximate vertical centering + b.context.font.Printf(x, y, b.label) +} + +func (b *Button) Update() { + // Update the button if needed +} + +func (b *Button) HandleEvent(event Event) { + switch event.Type { + case EventTypeClick: + if b.contains(event.Position) { + b.onClick() + } + case EventTypeMouseMove: + if b.contains(event.Position) { + if !b.isHovered { + b.isHovered = true + } + } else { + if b.isHovered { + b.isHovered = false + } + } + } +} + +// Rectangle is used to draw a rectangle using OpenGL +type Rectangle struct { + Position + Size + Color [4]float32 + vao uint32 + vbo uint32 + shaderProgram uint32 + context *Context +} + +func NewRectangle(x, y, width, height float32, color [4]float32, context *Context) *Rectangle { + rect := &Rectangle{ + Position: Position{X: x, Y: y}, + Size: Size{Width: width, Height: height}, + Color: color, + shaderProgram: context.shaderProgram, + context: context, + } + rect.initOpenGL() + return rect +} + +func (r *Rectangle) initOpenGL() { + vertices := []float32{ + r.Position.X, r.Position.Y, 0.0, + r.Position.X + r.Size.Width, r.Position.Y, 0.0, + r.Position.X + r.Size.Width, r.Position.Y + r.Size.Height, 0.0, + r.Position.X, r.Position.Y + r.Size.Height, 0.0, + } + indices := []uint32{ + 0, 1, 2, + 2, 3, 0, + } + gl.GenVertexArrays(1, &r.vao) + gl.BindVertexArray(r.vao) + + gl.GenBuffers(1, &r.vbo) + gl.BindBuffer(gl.ARRAY_BUFFER, r.vbo) + gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.STATIC_DRAW) + + var ebo uint32 + gl.GenBuffers(1, &ebo) + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices)*4, gl.Ptr(indices), gl.STATIC_DRAW) + + gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 3*4, gl.PtrOffset(0)) + gl.EnableVertexAttribArray(0) + + gl.BindVertexArray(0) +} + +func (r *Rectangle) Draw() { + gl.UseProgram(r.shaderProgram) + + // Set uniforms + model := mgl32.Ident4() + projection := mgl32.Ortho2D(0, float32(r.context.windowWidth), float32(r.context.windowHeight), 0) + + modelLoc := gl.GetUniformLocation(r.shaderProgram, gl.Str("model\x00")) + projectionLoc := gl.GetUniformLocation(r.shaderProgram, gl.Str("projection\x00")) + colorLoc := gl.GetUniformLocation(r.shaderProgram, gl.Str("color\x00")) + + gl.UniformMatrix4fv(modelLoc, 1, false, &model[0]) + gl.UniformMatrix4fv(projectionLoc, 1, false, &projection[0]) + gl.Uniform4fv(colorLoc, 1, &r.Color[0]) + + gl.BindVertexArray(r.vao) + gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, gl.PtrOffset(0)) + gl.BindVertexArray(0) +} + +func (r *Rectangle) Destroy() { + gl.DeleteVertexArrays(1, &r.vao) + gl.DeleteBuffers(1, &r.vbo) +} + +// Shader sources +var vertexShaderSource = ` +#version 410 core +layout(location = 0) in vec3 position; +uniform mat4 model; +uniform mat4 projection; +void main() { + gl_Position = projection * model * vec4(position, 1.0); +} +` + "\x00" + +var fragmentShaderSource = ` +#version 410 core +out vec4 fragColor; +uniform vec4 color; +void main() { + fragColor = color; +} +` + "\x00" + +// NewShaderProgram compiles and links the vertex and fragment shaders +func NewShaderProgram(vertexShaderSource, fragmentShaderSource string) uint32 { + vertexShader := gl.CreateShader(gl.VERTEX_SHADER) + cvertexShaderSource, freeVertex := gl.Strs(vertexShaderSource) + gl.ShaderSource(vertexShader, 1, cvertexShaderSource, nil) + freeVertex() + gl.CompileShader(vertexShader) + + var status int32 + gl.GetShaderiv(vertexShader, gl.COMPILE_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetShaderiv(vertexShader, gl.INFO_LOG_LENGTH, &logLength) + log := strings.Repeat("\x00", int(logLength+1)) + gl.GetShaderInfoLog(vertexShader, logLength, nil, gl.Str(log)) + panic(fmt.Sprintf("failed to compile vertex shader: %v", log)) + } + + fragmentShader := gl.CreateShader(gl.FRAGMENT_SHADER) + cfragmentShaderSource, freeFragment := gl.Strs(fragmentShaderSource) + gl.ShaderSource(fragmentShader, 1, cfragmentShaderSource, nil) + freeFragment() + gl.CompileShader(fragmentShader) + + gl.GetShaderiv(fragmentShader, gl.COMPILE_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetShaderiv(fragmentShader, gl.INFO_LOG_LENGTH, &logLength) + log := strings.Repeat("\x00", int(logLength+1)) + gl.GetShaderInfoLog(fragmentShader, logLength, nil, gl.Str(log)) + panic(fmt.Sprintf("failed to compile fragment shader: %v", log)) + } + + program := gl.CreateProgram() + gl.AttachShader(program, vertexShader) + gl.AttachShader(program, fragmentShader) + gl.LinkProgram(program) + + gl.GetProgramiv(program, gl.LINK_STATUS, &status) + if status == gl.FALSE { + var logLength int32 + gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength) + log := strings.Repeat("\x00", int(logLength+1)) + gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log)) + panic(fmt.Sprintf("failed to link program: %v", log)) + } + + gl.DeleteShader(vertexShader) + gl.DeleteShader(fragmentShader) + + return program +} + +func main() { + window := NewWindow(800, 600, "Demo Window") + window.SetTransparent(true) + context := window.DefineContext() + + panel := window.CreatePanel() + + button := NewButton("Click me!", func() { fmt.Println("Button pressed!") }, context) + button.Position = Position{X: 100, Y: 100} + button.Size = Size{Width: 200, Height: 50} + panel.AddChild(button) + + window.Start() +}