diff --git a/common/elements/v1/blocks/block.go b/common/elements/v1/blocks/block.go index ade548e..4ff9380 100644 --- a/common/elements/v1/blocks/block.go +++ b/common/elements/v1/blocks/block.go @@ -9,7 +9,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" ) -func New(ops ...Option) elements.ElementFunc { +func New(ops ...Option) elements.InitFunc { return func(d elements.Bounds) elements.Element { b := Block{ ContainerBounds: d, @@ -41,8 +41,8 @@ func (b *Block) Size() (w, h float64) { return } -func (b *Block) Draw(image *ebiten.Image) (w, h float64) { - w, h = b.Size() +func (b *Block) Draw(image *ebiten.Image) { + w, h := b.Size() if b.backgroundColor != nil { vector.DrawFilledRect( image, diff --git a/common/elements/v1/button.go b/common/elements/v1/button.go deleted file mode 100644 index e59a809..0000000 --- a/common/elements/v1/button.go +++ /dev/null @@ -1,70 +0,0 @@ -package elements - -// -//import ( -// "image/color" -//) -// -//type xAlign int -//type yAlign int -// -//const ( -// AlignCente xAlign = iota -// AlignLeft -// AlignRight -//) -// -//const ( -// AlignCenter yAlign = iota -// AlignTop -// AlignBottom -//) -// -//type Button struct { -// mouseHandler -// block -// Label string -// OnClick func() error -// OnRightClick func() error -// Style struct { -// MouseDownColor *color.Color -// } -//} -// -//func (b *Button) HandleClick(_ MouseState) error { -// if b.OnClick == nil { -// return nil -// } -// return b.OnClick() -//} -// -// -//func (b *Button) getFont() font.Face { -// if b.block.Font == nil { -// return ux.FontFace -// } -// -// return *b.block.block.Font -//} -// -//func (b *Button) backgroundColor() color.Color { -// var c *color.Color -// if b.block.block.BackgroundColor != nil { -// c = b.block.block.BackgroundColor -// } else { -// c = &ux.BackgroundColor -// } -// -// if (b.mouseState.RightDown || b.mouseState.LeftDown) && b.Style.MouseDownColor != nil { -// c = b.Style.MouseDownColor -// } -// -// return *c -//} -// -//func (b *Button) Draw(ctx context.Context, image *ebiten.Image) error { -// -// vector.StrokeRect(image, xz, yz, w, h, 1, b.backgroundColor(), true) -// -// return nil -//} diff --git a/common/elements/v1/buttons/button.go b/common/elements/v1/buttons/button.go new file mode 100644 index 0000000..7023d49 --- /dev/null +++ b/common/elements/v1/buttons/button.go @@ -0,0 +1,50 @@ +package buttons + +import ( + "git.vezzani.net/ben/games/common/elements/v1" + "git.vezzani.net/ben/games/common/elements/v1/blocks" + "git.vezzani.net/ben/games/common/elements/v1/mouse" +) + +func New(ops ...Option) elements.InitFunc { + return func(bounds elements.Bounds) elements.Element { + b := Button{ + Block: blocks.Block{ContainerBounds: bounds}, + } + for op := range ops { + ops[op](&b) + } + + return &b + } +} + +type Button struct { + blocks.Block + + onClick func(ms mouse.State) + onMouseDown func(ms mouse.State) + onMouseUp func(ms mouse.State) +} + +func (b *Button) HandleMouseEvent(ms mouse.State) bool { + if !b.Block.ContainerBounds.Contains(elements.Point{ + X: float64(ms.X), + Y: float64(ms.Y), + }) { + return false + } + switch { + case b.onClick != nil && (ms.RightClicked || ms.LeftClicked): + b.onClick(ms) + return true + case b.onMouseUp != nil && (ms.RightChanged && !ms.RightDown || ms.LeftChanged && !ms.LeftDown): + b.onMouseUp(ms) + return true + case b.onMouseDown != nil && (ms.RightChanged && ms.RightDown || ms.LeftChanged && ms.LeftDown): + b.onMouseDown(ms) + return true + } + + return false +} diff --git a/common/elements/v1/buttons/options.go b/common/elements/v1/buttons/options.go new file mode 100644 index 0000000..7d4a76b --- /dev/null +++ b/common/elements/v1/buttons/options.go @@ -0,0 +1,32 @@ +package buttons + +import ( + "git.vezzani.net/ben/games/common/elements/v1/blocks" + "git.vezzani.net/ben/games/common/elements/v1/mouse" +) + +type Option func(button *Button) + +func OnClick(f func(ms mouse.State)) Option { + return func(button *Button) { + button.onClick = f + } +} + +func OnMouseDown(f func(ms mouse.State)) Option { + return func(button *Button) { + button.onMouseDown = f + } +} + +func OnMouseUp(f func(ms mouse.State)) Option { + return func(button *Button) { + button.onMouseUp = f + } +} + +func BlockOpt(o blocks.Option) Option { + return func(s *Button) { + o(&s.Block) + } +} diff --git a/common/elements/v1/element.go b/common/elements/v1/element.go index 278fd90..6ef79a7 100644 --- a/common/elements/v1/element.go +++ b/common/elements/v1/element.go @@ -1,22 +1,15 @@ package elements import ( - "git.vezzani.net/ben/games/common/elements/v1/mouse" "github.com/hajimehoshi/ebiten/v2" ) -type ElementFunc func(Bounds) Element - -type Clickable interface { -} - -type Mouseable interface { -} +type InitFunc func(Bounds) Element type Element interface { - HandleMouseEvent(s mouse.State) bool + Size() (w, h float64) - Draw(image *ebiten.Image) (w, h float64) + Draw(image *ebiten.Image) } type Point struct { @@ -27,3 +20,7 @@ type Bounds struct { Min Point Width, Height float64 } + +func (b *Bounds) Contains(p Point) bool { + return p.X >= b.Min.X && p.X <= b.Min.X+b.Width && p.Y >= b.Min.Y && p.Y <= b.Min.Y+b.Height +} diff --git a/common/elements/v1/mouse/mouse.go b/common/elements/v1/mouse/mouse.go index 4a61d1d..f64a4d9 100644 --- a/common/elements/v1/mouse/mouse.go +++ b/common/elements/v1/mouse/mouse.go @@ -1,6 +1,7 @@ package mouse import ( + "git.vezzani.net/ben/games/common/elements/v1" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" ) @@ -11,6 +12,14 @@ func (n NopHandler) HandleMouseEvent(s State) bool { return false } +type ChildrenProvider interface { + GetChildren() []elements.Element +} + +type EventHandler interface { + HandleMouseEvent(s State) bool +} + type State struct { X, Y int LeftDown, RightDown bool @@ -19,13 +28,30 @@ type State struct { Clicked bool } -//func ClickHandler(e elements.Element) func() bool { -// newState := StateBuilder() -// return func() bool { -// _ = newState() -// if -// } -//} +func Handler(e elements.Element) func() bool { + newState := StateBuilder() + ms := newState() + return func() bool { + ms = newState() + return propagateMouse(e, ms) + } +} + +func propagateMouse(e elements.Element, ms State) bool { + if p, ok := e.(ChildrenProvider); ok { + for _, c := range p.GetChildren() { + if propagateMouse(c, ms) { + return true + } + } + } + + if e, ok := e.(EventHandler); ok { + return e.HandleMouseEvent(ms) + } + + return false +} func StateBuilder() func() State { prevState := State{} @@ -57,6 +83,8 @@ func StateBuilder() func() State { state.LeftChanged = state.LeftDown != prevState.LeftDown state.RightChanged = state.RightDown != prevState.RightDown + state.LeftClicked = state.LeftChanged && state.LeftDown + state.RightClicked = state.RightChanged && state.RightDown prevState = state return state diff --git a/common/elements/v1/stacks/options.go b/common/elements/v1/stacks/options.go index 8ed006b..2432aa8 100644 --- a/common/elements/v1/stacks/options.go +++ b/common/elements/v1/stacks/options.go @@ -13,9 +13,9 @@ func BlockOpt(o blocks.Option) Option { } } -func Children(children ...elements.ElementFunc) Option { +func Children(children ...elements.InitFunc) Option { return func(s *Stack) { - s.children = children + s.childrenInit = children } } diff --git a/common/elements/v1/stacks/stack.go b/common/elements/v1/stacks/stack.go index 012ffe4..8e67eae 100644 --- a/common/elements/v1/stacks/stack.go +++ b/common/elements/v1/stacks/stack.go @@ -8,7 +8,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" ) -func New(ops ...Option) elements.ElementFunc { +func New(ops ...Option) elements.InitFunc { return func(d elements.Bounds) elements.Element { s := Stack{ Block: blocks.Block{ContainerBounds: d}, @@ -16,6 +16,26 @@ func New(ops ...Option) elements.ElementFunc { for op := range ops { ops[op](&s) } + + d.Width, d.Height = s.Block.Size() + if s.horizontal { + d.Width /= float64(len(s.childrenInit)) + } else { + d.Height /= float64(len(s.childrenInit)) + } + + s.Children = make([]elements.Element, len(s.childrenInit)) + var offsetX, offsetY float64 + for i := range s.childrenInit { + s.Children[i] = s.childrenInit[i](d) + offsetX, offsetY = s.Children[i].Size() + if s.horizontal { + d.Min.X += offsetX + } else { + d.Min.Y += offsetY + } + } + return &s } } @@ -23,29 +43,19 @@ func New(ops ...Option) elements.ElementFunc { type Stack struct { blocks.Block mouse.NopHandler - horizontal bool - children []elements.ElementFunc + horizontal bool + childrenInit []elements.InitFunc + Children []elements.Element } -func (s *Stack) Draw(image *ebiten.Image) (w, h float64) { +func (s *Stack) Draw(image *ebiten.Image) { s.Block.Draw(image) - d := s.ContainerBounds - d.Width, d.Height = s.Block.Size() - if s.horizontal { - d.Width /= float64(len(s.children)) - } else { - d.Height /= float64(len(s.children)) + for i := range s.Children { + s.Children[i].Draw(image) } - - var offsetX, offsetY float64 - for i := range s.children { - offsetX, offsetY = s.children[i](d).Draw(image) - if s.horizontal { - d.Min.X += offsetX - } else { - d.Min.Y += offsetY - } - } - return s.Block.Size() +} + +func (s *Stack) GetChildren() []elements.Element { + return s.Children } diff --git a/tools/component_test/editor.go b/tools/component_test/editor.go index eea5bb5..d009a65 100644 --- a/tools/component_test/editor.go +++ b/tools/component_test/editor.go @@ -5,6 +5,8 @@ import ( "git.vezzani.net/ben/games/common/elements/v1" "git.vezzani.net/ben/games/common/elements/v1/blocks" + "git.vezzani.net/ben/games/common/elements/v1/buttons" + "git.vezzani.net/ben/games/common/elements/v1/mouse" "git.vezzani.net/ben/games/common/elements/v1/stacks" "git.vezzani.net/ben/games/common/sprites/v1" "github.com/hajimehoshi/ebiten/v2" @@ -20,7 +22,12 @@ var root = stacks.New( stacks.New( stacks.BlockOpt(blocks.Name("left")), stacks.Children( - blocks.New(blocks.BackgroundColor(colornames.Green)), + buttons.New( + buttons.BlockOpt(blocks.BackgroundColor(colornames.Green)), + buttons.OnClick(func(ms mouse.State) { + println("green") + }), + ), blocks.New(blocks.BackgroundColor(colornames.Yellow)), )), stacks.New( @@ -38,15 +45,14 @@ func newEditor() *editor { } type editor struct { - root elements.Element - bounds elements.Bounds + root elements.Element + bounds elements.Bounds + handleMouse func() bool } -//var handleClick = mouse.ClickHandler(root) - func (e *editor) Update() error { sprites.Update() - //handleClick() + e.handleMouse() return nil } @@ -62,6 +68,7 @@ func (e *editor) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHei Height: float64(outsideHeight), } e.root = root(e.bounds) + e.handleMouse = mouse.Handler(e.root) } return outsideWidth, outsideHeight