Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions components/textarea.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,26 @@ func (layout *TextareaLayout) ScrollBy(dy float32) {
layout.scroll = layout.clampScroll(layout.scroll + dy)
}

// Wheel scrolls the box by a wheel event over the text it last painted, as ScrollBy does, and
// reports whether the box moved. A wheel the box cannot move with (it is at the end the wheel
// points to, or it shows all its rows) reports false, so the page scrolls instead.
func (layout *TextareaLayout) Wheel(event gfx.ScrollEvent) bool {
content := layout.content
if !layout.painted || event.X < content.X || event.X >= content.X+content.Width ||
event.Y < content.Y || event.Y >= content.Y+content.Height {
return false
}
dy := event.DeltaY
if !event.Precise && dy > 0 && dy < 24 {
dy = 24
} else if !event.Precise && dy < 0 && dy > -24 {
dy = -24
}
before := layout.scroll
layout.ScrollBy(dy)
return layout.scroll != before
}

// RowCount is how many rows text takes in a box of outer width width: one for
// each line that fits and as many as a longer one wraps to, and an empty one
// after a final newline. The rows it wraps are the ones the next paint at that
Expand Down
45 changes: 45 additions & 0 deletions components/textarea_wheel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package components

import (
"strings"
"testing"

"github.com/vivaui/viva/gfx"
)

// A wheel over a TextareaLayout scrolls it while it can move in that direction, and reports
// nothing once it is at the end, so the page can scroll instead.
func TestTextareaWheelScrollsUntilItsEnd(t *testing.T) {
var lines []string
for index := range 40 {
lines = append(lines, "row "+string(rune('a'+index%26)))
}
text := strings.Join(lines, "\n")
box := newWrapBox(t, 100)
box.paint(TextareaSnapshot{Value: text, Caret: 0})
center := gfx.Point{X: box.layout.content.X + 4, Y: box.layout.content.Y + 4}

outside := gfx.ScrollEvent{X: box.layout.content.X - 10, Y: center.Y, DeltaY: 30}
if box.layout.Wheel(outside) {
t.Fatal("a wheel outside the text scrolled the box")
}
up := gfx.ScrollEvent{X: center.X, Y: center.Y, DeltaY: -30}
if box.layout.Wheel(up) {
t.Fatal("a wheel up at the top scrolled the box")
}

down := gfx.ScrollEvent{X: center.X, Y: center.Y, DeltaY: 30}
if !box.layout.Wheel(down) {
t.Fatal("a wheel down over the text did not scroll the box")
}
for range 200 {
box.paint(TextareaSnapshot{Value: text, Caret: 0})
if !box.layout.Wheel(down) {
break
}
}
box.paint(TextareaSnapshot{Value: text, Caret: 0})
if box.layout.Wheel(down) {
t.Fatal("a wheel down at the end of the text still scrolled the box")
}
}
7 changes: 7 additions & 0 deletions ui/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,13 @@ func (p *Page) Wheel(event gfx.ScrollEvent) gfx.EventResult {
return state.Wheel(event)
}
}
// A TextArea under the pointer scrolls its own rows first; at its end the wheel goes on
// to the page, as in a browser.
for _, entry := range p.fields {
if entry.field.rows && entry.field.area != nil && entry.field.area.Wheel(event) {
return gfx.EventResult{Handled: true, Repaint: true}
}
}
if p.wheelRoute != nil {
if result, ok := p.wheelRoute(p, event); ok {
return result
Expand Down
Loading