initial
This commit is contained in:
commit
08b843d80f
23 changed files with 639 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/ouichat
|
||||
|
57
core/buftree.go
Normal file
57
core/buftree.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package core
|
||||
|
||||
import "container/list"
|
||||
|
||||
type BufferNode struct {
|
||||
name string
|
||||
children []*BufferNode
|
||||
messages []*Message
|
||||
}
|
||||
|
||||
func NewBufferNode(name string) *BufferNode {
|
||||
return &BufferNode{
|
||||
name: name,
|
||||
children: make([]*BufferNode, 0),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
WALK_DEPTH_FIRST = 0
|
||||
WALK_BREADTH_FIRST = 1
|
||||
)
|
||||
|
||||
type WalkEntry struct {
|
||||
Node *BufferNode
|
||||
Depth int
|
||||
}
|
||||
|
||||
func (n*BufferNode) AddChild(child*BufferNode) {
|
||||
n.children = append(n.children, child)
|
||||
}
|
||||
|
||||
func (n *BufferNode) Walk(cb func(*WalkEntry), mode int) {
|
||||
queue := list.New()
|
||||
queue.PushBack(&WalkEntry{
|
||||
Node: n,
|
||||
Depth: 0,
|
||||
})
|
||||
|
||||
for queue.Len() > 0 {
|
||||
next := queue.Front()
|
||||
queue.Remove(next)
|
||||
entry := next.Value.(*WalkEntry)
|
||||
cb(entry)
|
||||
|
||||
for _, child := range entry.Node.children {
|
||||
childEntry := &WalkEntry{
|
||||
Node: child,
|
||||
Depth: entry.Depth + 1,
|
||||
}
|
||||
if mode == WALK_DEPTH_FIRST {
|
||||
queue.PushFront(childEntry)
|
||||
} else if mode == WALK_BREADTH_FIRST {
|
||||
queue.PushBack(childEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
core/message.go
Normal file
11
core/message.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package core
|
||||
|
||||
import "time"
|
||||
|
||||
type Message struct {
|
||||
Time time.Time `json:"time"`
|
||||
MessageID string
|
||||
Author string
|
||||
AuthorID string
|
||||
Contents string
|
||||
}
|
BIN
fonts/roboto-mono.ttf
Normal file
BIN
fonts/roboto-mono.ttf
Normal file
Binary file not shown.
BIN
fonts/roboto.ttf
Normal file
BIN
fonts/roboto.ttf
Normal file
Binary file not shown.
9
go.mod
Normal file
9
go.mod
Normal file
|
@ -0,0 +1,9 @@
|
|||
module ouichat
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/veandco/go-sdl2 v0.4.4
|
||||
gopkg.in/irc.v3 v3.1.3
|
||||
)
|
17
go.sum
Normal file
17
go.sum
Normal file
|
@ -0,0 +1,17 @@
|
|||
github.com/MONKEY-WORKS/cassowary v0.0.0-20180307084501-6ee9ddb566f5 h1:KE9sasTMSjdsNIxvLyKMArAkzDyvwUo3O6ZqpOJNGWc=
|
||||
github.com/MONKEY-WORKS/cassowary v0.0.0-20180307084501-6ee9ddb566f5/go.mod h1:kx7TtzC7VssZelylEr4cXP8Y/RVcBWT7ngGQg4MVijE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/monkey-works/cassowary v0.0.0-20180307084501-6ee9ddb566f5 h1:PqoJpAn2FCPtiQ0/PcuRDZ14Bl6vg6zukOQmoeYLdsU=
|
||||
github.com/monkey-works/cassowary v0.0.0-20180307084501-6ee9ddb566f5/go.mod h1:M/BEaa1gf6g5yzH31758qbbAMp0WjZh2KGP6LnCxiJE=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/veandco/go-sdl2 v0.4.4 h1:coOJGftOdvNvGoUIZmm4XD+ZRQF4mg9ZVHmH3/42zFQ=
|
||||
github.com/veandco/go-sdl2 v0.4.4/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2AB78DPg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/irc.v3 v3.1.3 h1:yeTiJ365882L8h4AnBKYfesD92y5R5ZhGiylu9DfcPY=
|
||||
gopkg.in/irc.v3 v3.1.3/go.mod h1:shO2gz8+PVeS+4E6GAny88Z0YVVQSxQghdrMVGQsR9s=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
74
main.go
Normal file
74
main.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"github.com/veandco/go-sdl2/ttf"
|
||||
|
||||
"ouichat/core"
|
||||
"ouichat/plugins"
|
||||
"ouichat/ui"
|
||||
)
|
||||
|
||||
func run() int {
|
||||
var err error
|
||||
|
||||
// Initialize libraries
|
||||
if err = sdl.Init(sdl.INIT_EVERYTHING); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer sdl.Quit()
|
||||
if err = ttf.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer ttf.Quit()
|
||||
|
||||
// create window
|
||||
window, err := sdl.CreateWindow(
|
||||
"ouichat",
|
||||
sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
|
||||
1024, 768,
|
||||
sdl.WINDOW_SHOWN,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer window.Destroy()
|
||||
|
||||
// create buffer tree
|
||||
tree := core.NewBufferNode("ouichat")
|
||||
|
||||
// start plugins
|
||||
man := plugins.NewPluginManager(tree)
|
||||
man.Spawn(plugins.NewIrcPlugin("acm"))
|
||||
|
||||
// initialize ui
|
||||
ui, err := ui.NewUI(window, tree)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return 1
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
// main loop
|
||||
for {
|
||||
exit := ui.RenderLoop()
|
||||
window.UpdateSurface()
|
||||
|
||||
if exit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
var exitcode int
|
||||
sdl.Main(func() {
|
||||
exitcode = run()
|
||||
})
|
||||
os.Exit(exitcode)
|
||||
}
|
54
plugins/irc.go
Normal file
54
plugins/irc.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package plugins
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"ouichat/core"
|
||||
|
||||
"gopkg.in/irc.v3"
|
||||
)
|
||||
|
||||
type IrcPlugin struct {
|
||||
name string
|
||||
client *irc.Client
|
||||
messages chan *irc.Message
|
||||
man *PluginManager
|
||||
root *core.BufferNode
|
||||
}
|
||||
|
||||
func NewIrcPlugin(name string) *IrcPlugin {
|
||||
conn, err := tls.Dial("tcp", "acm.umn.edu:6669", &tls.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// messages := make(chan *irc.Message)
|
||||
config := irc.ClientConfig{
|
||||
Nick: "ouichat",
|
||||
User: "ouichat",
|
||||
Name: "ouichat",
|
||||
Handler: irc.HandlerFunc(func(c *irc.Client, m *irc.Message) {
|
||||
fmt.Printf("%+v\n", m)
|
||||
// messages <- m
|
||||
}),
|
||||
}
|
||||
client := irc.NewClient(conn, config)
|
||||
return &IrcPlugin{
|
||||
name: name,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func(p*IrcPlugin) Name() string {
|
||||
return p.name
|
||||
}
|
||||
|
||||
func (p *IrcPlugin) Init(man *PluginManager, root *core.BufferNode) {
|
||||
p.man = man
|
||||
p.root = root
|
||||
go p.client.Run()
|
||||
}
|
||||
|
||||
func (p *IrcPlugin) Buffers() *core.BufferNode {
|
||||
return p.root
|
||||
}
|
23
plugins/manager.go
Normal file
23
plugins/manager.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package plugins
|
||||
|
||||
import "ouichat/core"
|
||||
|
||||
// PluginManager is a convenience type that handles everything related to plugins
|
||||
type PluginManager struct {
|
||||
root *core.BufferNode
|
||||
plugins []Plugin
|
||||
}
|
||||
|
||||
func NewPluginManager(root *core.BufferNode) *PluginManager {
|
||||
return &PluginManager{
|
||||
root: root,
|
||||
plugins: make([]Plugin, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (man *PluginManager) Spawn(plugin Plugin) {
|
||||
node := core.NewBufferNode(plugin.Name())
|
||||
man.root.AddChild(node)
|
||||
plugin.Init(man, node)
|
||||
man.plugins = append(man.plugins, plugin)
|
||||
}
|
17
plugins/plugin.go
Normal file
17
plugins/plugin.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package plugins
|
||||
|
||||
import (
|
||||
"ouichat/core"
|
||||
)
|
||||
|
||||
type Plugin interface {
|
||||
// Name is a unique name
|
||||
Name() string
|
||||
|
||||
// Start is executed before anything
|
||||
Init(man *PluginManager, root *core.BufferNode)
|
||||
|
||||
// Buffers gets a list of the buffers underneath this one
|
||||
// the root one given to the plugin must be returned
|
||||
Buffers() *core.BufferNode
|
||||
}
|
1
plugins/rocketchat.go
Normal file
1
plugins/rocketchat.go
Normal file
|
@ -0,0 +1 @@
|
|||
package plugins
|
1
plugins/weechat.go
Normal file
1
plugins/weechat.go
Normal file
|
@ -0,0 +1 @@
|
|||
package plugins
|
11
ui/colors.go
Normal file
11
ui/colors.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package ui
|
||||
|
||||
import "github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
var (
|
||||
BACKGROUND = sdl.Color{R: 46, G: 46, B: 50, A: 255}
|
||||
BACKGROUND2 = sdl.Color{R: 37, G: 37, B: 41, A: 255}
|
||||
DIVIDER = sdl.Color{R: 32, G: 32, B: 36, A: 255}
|
||||
TEXT = sdl.Color{R: 151, G: 151, B: 156, A: 255}
|
||||
CARET = sdl.Color{R: 147, G: 221, B: 250, A: 255}
|
||||
)
|
4
ui/ctr_scroller.go
Normal file
4
ui/ctr_scroller.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
package ui
|
||||
|
||||
type Scroller struct {
|
||||
}
|
81
ui/ui.go
Normal file
81
ui/ui.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"github.com/veandco/go-sdl2/ttf"
|
||||
|
||||
"ouichat/core"
|
||||
)
|
||||
|
||||
const (
|
||||
FrameRate = 60
|
||||
)
|
||||
|
||||
var (
|
||||
sansFont *ttf.Font
|
||||
monoFont *ttf.Font
|
||||
)
|
||||
|
||||
type UI struct {
|
||||
win *sdl.Window
|
||||
rdr *sdl.Renderer // SDL renderer
|
||||
root View
|
||||
focused *View
|
||||
}
|
||||
|
||||
func NewUI(win *sdl.Window, tree *core.BufferNode) (*UI, error) {
|
||||
rdr, err := sdl.CreateRenderer(win, 0, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sansFont == nil {
|
||||
if sansFont, err = ttf.OpenFont("fonts/roboto.ttf", 16); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if monoFont == nil {
|
||||
if monoFont, err = ttf.OpenFont("fonts/roboto-mono.ttf", 16); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
root := NewRootView(tree)
|
||||
|
||||
return &UI{
|
||||
win: win,
|
||||
rdr: rdr,
|
||||
root: root,
|
||||
focused: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ui *UI) RenderLoop() bool {
|
||||
// handle events first
|
||||
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
||||
switch event.(type) {
|
||||
case *sdl.KeyboardEvent:
|
||||
// fmt.Printf("event: %+v\n", event)
|
||||
case *sdl.QuitEvent:
|
||||
println("Quit")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// render
|
||||
ui.rdr.SetDrawColor(BACKGROUND.R, BACKGROUND.G, BACKGROUND.B, BACKGROUND.A)
|
||||
ui.rdr.Clear()
|
||||
w, h := ui.win.GetSize()
|
||||
ui.root.Draw(ui.rdr, 0, 0, int(w), int(h))
|
||||
|
||||
ui.rdr.Present()
|
||||
sdl.Delay(1000 / FrameRate)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ui *UI) Close() {
|
||||
ui.rdr.Destroy()
|
||||
sansFont.Close()
|
||||
monoFont.Close()
|
||||
}
|
25
ui/util.go
Normal file
25
ui/util.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"github.com/veandco/go-sdl2/ttf"
|
||||
)
|
||||
|
||||
func CreateText(r *sdl.Renderer, font *ttf.Font, text string, color sdl.Color) (*sdl.Texture, int32, int32, error) {
|
||||
var (
|
||||
err error
|
||||
surf *sdl.Surface
|
||||
txt *sdl.Texture
|
||||
)
|
||||
|
||||
if surf, err = font.RenderUTF8Blended(text, color); err != nil {
|
||||
return nil, -1, -1, err
|
||||
}
|
||||
defer surf.Free()
|
||||
|
||||
if txt, err = r.CreateTextureFromSurface(surf); err != nil {
|
||||
return nil, -1, -1, err
|
||||
}
|
||||
|
||||
return txt, surf.W, surf.H, nil
|
||||
}
|
35
ui/view.go
Normal file
35
ui/view.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
var (
|
||||
idLock = &sync.Mutex{}
|
||||
id = 0
|
||||
)
|
||||
|
||||
// View is a rectangular widget
|
||||
type View interface {
|
||||
// Name returns a unique way to identify this view
|
||||
// Use ViewID to create a unique ID
|
||||
Name() string
|
||||
|
||||
// Draw the view using the given dimensions and offset
|
||||
Draw(r *sdl.Renderer, x, y int32, w, h int) error
|
||||
|
||||
GetWidth() int
|
||||
GetMinWidth() int
|
||||
}
|
||||
|
||||
func ViewID(name string) string {
|
||||
idLock.Lock()
|
||||
defer idLock.Unlock()
|
||||
|
||||
next := id
|
||||
id += 1
|
||||
return fmt.Sprintf("%s_%d", name, next)
|
||||
}
|
32
ui/view_buffer.go
Normal file
32
ui/view_buffer.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package ui
|
||||
|
||||
import "github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
type BufferView struct {
|
||||
name string
|
||||
textbox *TextBox
|
||||
}
|
||||
|
||||
func NewBufferView(name string) *BufferView {
|
||||
return &BufferView{
|
||||
name: ViewID(name),
|
||||
textbox: NewTextBox(),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *BufferView) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
func (v *BufferView) GetWidth() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (v *BufferView) GetMinWidth() int {
|
||||
return 400
|
||||
}
|
||||
|
||||
func (v *BufferView) Draw(r *sdl.Renderer, x, y int32, w, h int) error {
|
||||
v.textbox.Draw(r, x, y+int32(h)-40, w, 40)
|
||||
return nil
|
||||
}
|
42
ui/view_root.go
Normal file
42
ui/view_root.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
"ouichat/core"
|
||||
)
|
||||
|
||||
type RootView struct {
|
||||
name string
|
||||
split *VSplitView
|
||||
}
|
||||
|
||||
func NewRootView(tree *core.BufferNode) *RootView {
|
||||
name := ViewID("root")
|
||||
|
||||
split := NewVSplitView()
|
||||
split.AddChild(NewTreeView(tree))
|
||||
split.AddChild(NewBufferView("empty"))
|
||||
|
||||
return &RootView{
|
||||
name,
|
||||
split,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *RootView) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
func (v *RootView) GetWidth() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (v *RootView) GetMinWidth() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (v *RootView) Draw(r *sdl.Renderer, x, y int32, w, h int) error {
|
||||
// don't do anything but forward it to the split
|
||||
return v.split.Draw(r, x, y, w, h)
|
||||
}
|
55
ui/view_tree.go
Normal file
55
ui/view_tree.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
"ouichat/core"
|
||||
)
|
||||
|
||||
type TreeView struct {
|
||||
name string
|
||||
tree *core.BufferNode
|
||||
width uint
|
||||
}
|
||||
|
||||
func NewTreeView(tree *core.BufferNode) *TreeView {
|
||||
return &TreeView{
|
||||
name: ViewID("tree"),
|
||||
tree: tree,
|
||||
width: 200,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *TreeView) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
func (v *TreeView) GetWidth() int {
|
||||
return int(v.width)
|
||||
}
|
||||
|
||||
func (v *TreeView) GetMinWidth() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (v *TreeView) Draw(r *sdl.Renderer, x, y int32, w, h int) error {
|
||||
var (
|
||||
txt *sdl.Texture
|
||||
tw, th int32
|
||||
err error
|
||||
)
|
||||
if txt, tw, th, err = CreateText(r, sansFont, "BUFFERS", TEXT); err != nil {
|
||||
return err
|
||||
}
|
||||
defer txt.Destroy()
|
||||
r.Copy(txt, nil, &sdl.Rect{X: 5, Y: 5, W: tw, H: th})
|
||||
|
||||
v.tree.Walk(func(entry *core.WalkEntry) {
|
||||
fmt.Printf("%s%+v\n", strings.Repeat(" ", entry.Depth), entry.Node)
|
||||
}, core.WALK_DEPTH_FIRST)
|
||||
|
||||
return nil
|
||||
}
|
56
ui/view_vsplit.go
Normal file
56
ui/view_vsplit.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
type VSplitView struct {
|
||||
children []View
|
||||
}
|
||||
|
||||
func NewVSplitView() *VSplitView {
|
||||
children := make([]View, 0)
|
||||
return &VSplitView{
|
||||
children,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VSplitView) AddChild(child View) {
|
||||
v.children = append(v.children, child)
|
||||
}
|
||||
|
||||
func (v *VSplitView) Draw(r *sdl.Renderer, x, y int32, w, h int) error {
|
||||
totalWidth := w - int(len(v.children)) + 1
|
||||
widths := make([]int, len(v.children))
|
||||
remainingChildren := 0
|
||||
for i, child := range v.children {
|
||||
cw := child.GetWidth()
|
||||
if cw >= 0 {
|
||||
totalWidth -= cw
|
||||
widths[i] = cw
|
||||
} else {
|
||||
remainingChildren += 1
|
||||
widths[i] = -1
|
||||
}
|
||||
}
|
||||
eachChild := totalWidth / remainingChildren
|
||||
for i := range v.children {
|
||||
if widths[i] == -1 {
|
||||
widths[i] = eachChild
|
||||
}
|
||||
}
|
||||
|
||||
runningX := int32(0)
|
||||
for i, child := range v.children {
|
||||
child.Draw(r, runningX, y, widths[i], h)
|
||||
runningX += int32(widths[i] + 1)
|
||||
|
||||
if err := r.SetDrawColor(DIVIDER.R, DIVIDER.G, DIVIDER.B, DIVIDER.A); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.DrawLine(runningX-1, y, int32(runningX)-1, y+int32(h)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
32
ui/widget_textbox.go
Normal file
32
ui/widget_textbox.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/veandco/go-sdl2/gfx"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
var (
|
||||
blinkTimer = time.Now()
|
||||
)
|
||||
|
||||
type TextBox struct {
|
||||
value string
|
||||
hasFocus bool
|
||||
}
|
||||
|
||||
func NewTextBox() *TextBox {
|
||||
return &TextBox{}
|
||||
}
|
||||
|
||||
func (b *TextBox) Draw(r *sdl.Renderer, x, y int32, w, h int) error {
|
||||
r.SetDrawColor(BACKGROUND2.R, BACKGROUND2.G, BACKGROUND2.B, BACKGROUND2.A)
|
||||
r.FillRect(&sdl.Rect{X: x, Y: y, W: int32(w), H: int32(h)})
|
||||
|
||||
if int64(time.Now().Sub(blinkTimer).Seconds())%2 == 0 {
|
||||
cx := x + 5
|
||||
gfx.ThickLineColor(r, cx, y+5, cx, y+int32(h)-5, 3, CARET)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue