From b6b6668d0ed68ece099459cf1e2a2848cbad5553 Mon Sep 17 00:00:00 2001 From: Timon Ringwald Date: Wed, 4 May 2022 14:16:08 +0200 Subject: [PATCH] MouseEvent handling refactored --- events.go | 26 ++++++++++++++-- screen.go | 17 +++++------ tests/screen_test.go | 62 +++++++++++++++++++++++++++++++++++++-- types.go | 2 +- views/layout_border.go | 32 ++++++++++---------- views/layout_separator.go | 6 ++-- viewtmpl.go | 9 +----- wrappertmpl.go | 7 +++++ 8 files changed, 118 insertions(+), 43 deletions(-) diff --git a/events.go b/events.go index 74767ea..a462c84 100644 --- a/events.go +++ b/events.go @@ -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) } diff --git a/screen.go b/screen.go index 7ee22ba..d60266b 100644 --- a/screen.go +++ b/screen.go @@ -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)) } diff --git a/tests/screen_test.go b/tests/screen_test.go index 964d20e..309f4ff 100644 --- a/tests/screen_test.go +++ b/tests/screen_test.go @@ -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) } diff --git a/types.go b/types.go index 254af93..10c7fd7 100644 --- a/types.go +++ b/types.go @@ -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 { diff --git a/views/layout_border.go b/views/layout_border.go index 045697e..e2934d5 100644 --- a/views/layout_border.go +++ b/views/layout_border.go @@ -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" ) diff --git a/views/layout_separator.go b/views/layout_separator.go index 77ba4f5..a644cb6 100644 --- a/views/layout_separator.go +++ b/views/layout_separator.go @@ -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) { diff --git a/viewtmpl.go b/viewtmpl.go index 0ec5181..77e635a 100644 --- a/viewtmpl.go +++ b/viewtmpl.go @@ -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 } diff --git a/wrappertmpl.go b/wrappertmpl.go index 63e54ff..07e9be8 100644 --- a/wrappertmpl.go +++ b/wrappertmpl.go @@ -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) }