MouseEvent handling refactored

This commit is contained in:
Timon Ringwald 2022-05-04 14:16:08 +02:00
parent b1ad5ab081
commit b6b6668d0e
8 changed files with 118 additions and 43 deletions

View File

@ -6,7 +6,29 @@ type Events interface {
// If OnKeyPressed returns true, the event will not be passed onto child views
OnKeyPressed(event *KeyEvent) (consumed bool)
// OnMouseClicked is called every time a mouse button was pressed on the view.
// OnMouseEvent is called every time the mouse was used in any way.
// That includes mouse movement, button presses and scroll wheel usage
// If OnMouseClicked returns true, the event will not be passed onto child views
OnMouseClicked(event *MouseEvent) (consumed bool)
OnMouseEvent(event *MouseEvent) (consumed bool)
}
type EventTmpl struct {
KeyPressed func(event *KeyEvent) (consumed bool)
MouseEvent func(event *MouseEvent) (consumed bool)
}
var _ Events = &EventTmpl{}
func (e *EventTmpl) OnKeyPressed(event *KeyEvent) (consumed bool) {
if e.KeyPressed == nil {
return false
}
return e.KeyPressed(event)
}
func (e *EventTmpl) OnMouseEvent(event *MouseEvent) (consumed bool) {
if e.MouseEvent == nil {
return false
}
return e.MouseEvent(event)
}

View File

@ -9,6 +9,8 @@ import (
)
type Screen struct {
EventTmpl
scr tcell.Screen
buf *ViewBuffer
@ -17,12 +19,6 @@ type Screen struct {
// Root is the root view which is currently shown on screen
Root View
// KeyPressed is called every time a key or key-combination is pressed.
KeyPressed func(event *KeyEvent) (consumed bool)
// MouseClicked is called every time a mouse button was pressed.
MouseClicked func(event *MouseEvent) (consumed bool)
}
func NewScreen(root View) (*Screen, error) {
@ -72,9 +68,10 @@ func (s *Screen) onKeyPressed(event *KeyEvent) {
s.Redraw()
}
func (s *Screen) onMouseClicked(event *MouseEvent) {
if s.MouseClicked == nil || !s.MouseClicked(event) {
go s.Root.OnMouseClicked(event)
func (s *Screen) onMouseEvent(event *MouseEvent) {
if s.MouseEvent == nil || !s.MouseEvent(event) {
go s.Root.OnMouseEvent(event)
// TODO redraw in same thread
}
if event.Button != MouseButtonNone {
s.Redraw()
@ -104,7 +101,7 @@ func (s *Screen) eventloop() {
case *tcell.EventKey:
s.onKeyPressed(event)
case *tcell.EventMouse:
s.onMouseClicked(convertMouseEvent(event))
s.onMouseEvent(convertMouseEvent(event))
default:
s.StopWithError(fmt.Errorf("%#v", event))
}

View File

@ -7,6 +7,7 @@ import (
"math/rand"
"os"
"strconv"
"strings"
"testing"
"git.tordarus.net/Tordarus/tui"
@ -61,7 +62,7 @@ func TestScrollView(t *testing.T) {
return true
}
screen.MouseClicked = func(event *tui.MouseEvent) (consumed bool) {
screen.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
//textViews[0].(*views.TextView).Text = fmt.Sprintf("mouse position: %d | %d", event.X, event.Y)
//textViews[1].(*views.TextView).Text = fmt.Sprintf("mouse button: %d", event.Button)
@ -123,7 +124,7 @@ func TestMousePosition(t *testing.T) {
return
}
screen.MouseClicked = func(event *tui.MouseEvent) (consumed bool) {
screen.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
textView.Text = fmt.Sprintf("position: %s", event.Position.String())
return true
}
@ -217,20 +218,58 @@ func TestSeparatorLayout(t *testing.T) {
}
func TestBorderLayout(t *testing.T) {
topView := views.NewConstrainView(nil, 10, 10)
textView := views.NewTextView("")
textView.SetStyle(tui.StyleDefault.Background(tcell.ColorBlue))
textView.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
if event.Button == tui.MouseButtonLeft {
textView.SetStyle(tui.StyleDefault.Background(tcell.ColorRed))
return true
}
return false
}
topView := views.NewConstrainView(textView, -1, -1)
topView.SetStyle(tui.StyleDefault.Background(tcell.ColorBlue))
bottomView := views.NewConstrainView(nil, 10, 10)
bottomView.SetStyle(tui.StyleDefault.Background(tcell.ColorRed))
bottomView.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
if event.Button == tui.MouseButtonLeft {
bottomView.SetStyle(tui.StyleDefault.Background(tcell.ColorBlue))
return true
}
return false
}
leftView := views.NewConstrainView(nil, 10, 10)
leftView.SetStyle(tui.StyleDefault.Background(tcell.ColorYellow))
leftView.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
if event.Button == tui.MouseButtonLeft {
leftView.SetStyle(tui.StyleDefault.Background(tcell.ColorGreen))
return true
}
return false
}
rightView := views.NewConstrainView(nil, 10, 10)
rightView.SetStyle(tui.StyleDefault.Background(tcell.ColorGreen))
rightView.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
if event.Button == tui.MouseButtonLeft {
rightView.SetStyle(tui.StyleDefault.Background(tcell.ColorYellow))
return true
}
return false
}
centerView := views.NewConstrainView(nil, 10, 10)
centerView.SetStyle(tui.StyleDefault.Background(tcell.ColorPurple))
centerView.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
if event.Button == tui.MouseButtonLeft {
centerView.SetStyle(tui.StyleDefault.Background(tcell.ColorOrange))
return true
}
return false
}
borderLayout := views.NewBorderLayout()
borderLayout.SetStyle(tui.StyleDefault.Background(tcell.ColorPurple))
@ -253,6 +292,23 @@ func TestBorderLayout(t *testing.T) {
return true
}
screen.MouseEvent = func(event *tui.MouseEvent) (consumed bool) {
if event.Button == tui.MouseButtonLeft {
b := new(strings.Builder)
b.WriteString(textView.Text)
for slot, dim := range borderLayout.ViewDimensions {
if event.Position.In(dim) {
b.WriteString(fmt.Sprintf("%s: %s\n", slot, dim))
}
}
textView.Text = b.String()
}
return false
}
err = screen.Start()
fmt.Println(err)
}

View File

@ -23,7 +23,7 @@ func P(x, y int) Point {
}
func (p Point) In(d Dimension) bool {
return p.X >= d.X && p.X < d.X+d.Width && p.Y >= d.Y && p.Y < d.Y+d.Height
return p.X > d.X && p.X < d.X+d.Width && p.Y > d.Y && p.Y < d.Y+d.Height
}
func (p Point) String() string {

View File

@ -11,7 +11,7 @@ type BorderLayout struct {
horizontalLayout *LayoutResult
verticalLayout *LayoutResult
viewDimensions map[Slot]tui.Dimension
ViewDimensions map[Slot]tui.Dimension
}
var _ tui.Layout = &BorderLayout{}
@ -19,7 +19,7 @@ var _ tui.Layout = &BorderLayout{}
func NewBorderLayout() *BorderLayout {
return &BorderLayout{
views: map[Slot]tui.View{},
viewDimensions: map[Slot]tui.Dimension{},
ViewDimensions: map[Slot]tui.Dimension{},
}
}
@ -79,7 +79,7 @@ func (g *BorderLayout) Draw(buf *tui.ViewBuffer) {
topHeight = int(float64(buf.Height()) * float64(topHeight) / float64(verticalLayout.Sum.Height))
}
g.viewDimensions[Top] = tui.D(0, 0, buf.Width(), topHeight)
g.ViewDimensions[Top] = tui.D(0, 0, buf.Width(), topHeight)
view.Draw(buf.Sub(0, 0, buf.Width(), topHeight))
}
@ -92,7 +92,7 @@ func (g *BorderLayout) Draw(buf *tui.ViewBuffer) {
bottomHeight = int(float64(buf.Height()) * float64(bottomHeight) / float64(verticalLayout.Sum.Height))
}
g.viewDimensions[Bottom] = tui.D(0, buf.Height()-bottomHeight, buf.Width(), bottomHeight)
g.ViewDimensions[Bottom] = tui.D(0, buf.Height()-bottomHeight, buf.Width(), bottomHeight)
view.Draw(buf.Sub(0, buf.Height()-bottomHeight, buf.Width(), bottomHeight))
}
@ -105,7 +105,7 @@ func (g *BorderLayout) Draw(buf *tui.ViewBuffer) {
leftWidth = int(float64(buf.Width()) * float64(leftWidth) / float64(horizontalLayout.Sum.Width))
}
g.viewDimensions[Left] = tui.D(0, topHeight, leftWidth, buf.Height()-topHeight-bottomHeight)
g.ViewDimensions[Left] = tui.D(0, topHeight, leftWidth, buf.Height()-topHeight-bottomHeight)
view.Draw(buf.Sub(0, topHeight, leftWidth, buf.Height()-topHeight-bottomHeight))
}
@ -118,12 +118,12 @@ func (g *BorderLayout) Draw(buf *tui.ViewBuffer) {
rightWidth = int(float64(buf.Width()) * float64(rightWidth) / float64(horizontalLayout.Sum.Width))
}
g.viewDimensions[Right] = tui.D(buf.Width()-rightWidth, topHeight, rightWidth, buf.Height()-topHeight-bottomHeight)
g.ViewDimensions[Right] = tui.D(buf.Width()-rightWidth, topHeight, rightWidth, buf.Height()-topHeight-bottomHeight)
view.Draw(buf.Sub(buf.Width()-rightWidth, topHeight, rightWidth, buf.Height()-topHeight-bottomHeight))
}
if view, ok := g.views[Center]; ok {
g.viewDimensions[Center] = tui.D(leftWidth, topHeight, buf.Width()-leftWidth-rightWidth, buf.Height()-topHeight-bottomHeight)
g.ViewDimensions[Center] = tui.D(leftWidth, topHeight, buf.Width()-leftWidth-rightWidth, buf.Height()-topHeight-bottomHeight)
view.Draw(buf.Sub(leftWidth, topHeight, buf.Width()-leftWidth-rightWidth, buf.Height()-topHeight-bottomHeight))
}
@ -146,22 +146,22 @@ func (g *BorderLayout) OnKeyPressed(event *tui.KeyEvent) (consumed bool) {
return false
}
func (g *BorderLayout) OnMouseClicked(event *tui.MouseEvent) (consumed bool) {
for slot, dim := range g.viewDimensions {
func (g *BorderLayout) OnMouseEvent(event *tui.MouseEvent) (consumed bool) {
for slot, dim := range g.ViewDimensions {
if event.Position.In(dim) {
g.views[slot].OnMouseClicked(event)
g.views[slot].OnMouseEvent(event)
return true
}
}
return false
}
type Slot uint8
type Slot string
const (
Top Slot = iota
Bottom
Left
Right
Center
Top Slot = "top"
Bottom Slot = "bottom"
Left Slot = "left"
Right Slot = "right"
Center Slot = "center"
)

View File

@ -4,7 +4,7 @@ import (
"git.tordarus.net/Tordarus/tui"
)
// SeperatorLayout ia a tui.Layout which separates
// SeperatorLayout ia a tui.Layout which separates its view into gravity-based portions
type SeperatorLayout struct {
tui.ViewTmpl
views []tui.View
@ -68,8 +68,8 @@ func (g *SeperatorLayout) RemoveView(v tui.View) {
}
}
func (g *SeperatorLayout) View(slot Slot) tui.View {
return g.views[slot]
func (g *SeperatorLayout) View(index int) tui.View {
return g.views[index]
}
func (g *SeperatorLayout) Draw(buf *tui.ViewBuffer) {

View File

@ -1,6 +1,7 @@
package tui
type ViewTmpl struct {
EventTmpl
style *Style
}
@ -14,14 +15,6 @@ func (v *ViewTmpl) Layout() (prefWidth, prefHeight int) {
return -1, -1
}
func (v *ViewTmpl) OnKeyPressed(event *KeyEvent) (consumed bool) {
return false
}
func (v *ViewTmpl) OnMouseClicked(event *MouseEvent) (consumed bool) {
return false
}
func (v *ViewTmpl) SetStyle(s Style) {
v.style = &s
}

View File

@ -29,6 +29,13 @@ func (v *WrapperTmpl) OnKeyPressed(event *KeyEvent) (consumed bool) {
return v.ViewTmpl.OnKeyPressed(event)
}
func (v *WrapperTmpl) OnMouseEvent(event *MouseEvent) (consumed bool) {
if v.view != nil {
return v.view.OnMouseEvent(event)
}
return v.ViewTmpl.OnMouseEvent(event)
}
func (v *WrapperTmpl) SetStyle(s Style) {
v.ViewTmpl.SetStyle(s)
}