Files
wehub-resource-sync c4536f7e05
CI / test (push) Failing after 1s
CI / macOS amd64 (push) Has been cancelled
CI / macOS arm64 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:31 +08:00

69 lines
2.2 KiB
Go

package gui
import (
"image"
"gioui.org/gesture"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/widget"
"gioui.org/widget/material"
)
// ScrollRegion is the vertical scroll state for a manually drawn
// column: wheel/gesture scrolling, a scrollbar, and animated jumps.
// Offset is the pixel offset of the content top relative to the
// viewport top, so it goes negative as the content scrolls up.
type ScrollRegion struct {
Offset float32
Gesture gesture.Scroll
Bar widget.Scrollbar
Anim ScrollAnimation
}
// LayoutBar draws a vertical scrollbar at barX with the given width,
// applies scrollbar drags and scroll gestures, and clamps the offset
// so the viewport stays within [contentTop, contentBot].
func (region *ScrollRegion) LayoutBar(gtx layout.Context, th *material.Theme, barX, barWidth int, contentTop, contentBot float32) {
viewTop := -region.Offset
viewBot := -region.Offset + float32(gtx.Constraints.Max.Y)
stack := op.Offset(image.Pt(barX, 0)).Push(gtx.Ops)
bgtx := gtx
bgtx.Constraints = layout.Exact(image.Pt(barWidth, gtx.Constraints.Max.Y))
material.Scrollbar(th, &region.Bar).Layout(bgtx, layout.Vertical,
(viewTop-contentTop)/(contentBot-contentTop),
(viewBot-contentTop)/(contentBot-contentTop),
)
stack.Pop()
if distance := region.Bar.ScrollDistance(); distance != 0 {
region.Offset -= distance * (contentBot - contentTop)
}
if distance := region.Gesture.Update(gtx.Metric, gtx.Source, gtx.Now, gesture.Vertical,
pointer.ScrollRange{},
pointer.ScrollRange{Min: -1000, Max: 1000},
); distance != 0 {
region.Offset -= float32(distance)
}
region.Clamp(gtx, contentTop, contentBot)
}
// Clamp keeps the viewport within the content, stopping any running
// jump animation when it hits an edge.
func (region *ScrollRegion) Clamp(gtx layout.Context, contentTop, contentBot float32) {
if -region.Offset < contentTop {
region.Offset = -contentTop
region.Anim.Stop()
}
if -region.Offset+float32(gtx.Constraints.Max.Y) > contentBot {
if contentBot < float32(gtx.Constraints.Max.Y) {
region.Offset = -contentTop
} else {
region.Offset = float32(gtx.Constraints.Max.Y) - contentBot
}
region.Anim.Stop()
}
}