We now have an untested lone scrollbar element
This commit is contained in:
parent
cf672824a6
commit
aff9aca835
@ -7,6 +7,14 @@ import "git.tebibyte.media/sashakoshka/tomo/config"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||
|
||||
// ScrollBar is an element similar to Slider, but it has special behavior that
|
||||
// makes it well suited for controlling the viewport position on one axis of a
|
||||
// scrollable element. Instead of having a value from zero to one, it stores
|
||||
// viewport and content boundaries. When the user drags the scroll bar handle,
|
||||
// the scroll bar calls the OnScroll callback assigned to it with the position
|
||||
// the user is trying to move the handle to. A program can check to see if this
|
||||
// value is valid, move the viewport, and give the scroll bar the new viewport
|
||||
// bounds (which will then cause it to move the handle).
|
||||
type ScrollBar struct {
|
||||
*core.Core
|
||||
core core.CoreControl
|
||||
@ -14,7 +22,7 @@ type ScrollBar struct {
|
||||
vertical bool
|
||||
enabled bool
|
||||
dragging bool
|
||||
dragOffset int
|
||||
dragOffset image.Point
|
||||
track image.Rectangle
|
||||
bar image.Rectangle
|
||||
|
||||
@ -24,10 +32,11 @@ type ScrollBar struct {
|
||||
config config.Wrapped
|
||||
theme theme.Wrapped
|
||||
|
||||
onSlide func ()
|
||||
onRelease func ()
|
||||
onScroll func (viewport image.Point)
|
||||
}
|
||||
|
||||
// NewScrollBar creates a new scroll bar. If vertical is set to true, the scroll
|
||||
// bar will be vertical instead of horizontal.
|
||||
func NewScrollBar (vertical bool) (element *ScrollBar) {
|
||||
element = &ScrollBar {
|
||||
vertical: vertical,
|
||||
@ -44,15 +53,61 @@ func NewScrollBar (vertical bool) (element *ScrollBar) {
|
||||
}
|
||||
|
||||
func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
|
||||
velocity := element.config.ScrollVelocity()
|
||||
point := image.Pt(x, y)
|
||||
|
||||
if point.In(element.bar) {
|
||||
// the mouse is pressed down within the bar's handle
|
||||
element.dragging = true
|
||||
element.redo()
|
||||
element.dragOffset = point
|
||||
element.dragTo(point)
|
||||
} else {
|
||||
// the mouse is pressed down within the bar's gutter
|
||||
switch button {
|
||||
case input.ButtonLeft:
|
||||
// start scrolling at this point, but set the offset to
|
||||
// the middle of the handle
|
||||
element.dragging = true
|
||||
element.dragOffset = element.fallbackDragOffset()
|
||||
element.dragTo(point)
|
||||
|
||||
case input.ButtonMiddle:
|
||||
// page up/down on middle click
|
||||
viewport := 0
|
||||
if element.vertical {
|
||||
viewport = element.viewportBounds.Dy()
|
||||
} else {
|
||||
viewport = element.viewportBounds.Dx()
|
||||
}
|
||||
if element.isAfterHandle(point) {
|
||||
element.scrollBy(viewport)
|
||||
} else {
|
||||
element.scrollBy(-viewport)
|
||||
}
|
||||
|
||||
case input.ButtonRight:
|
||||
// inch up/down on right click
|
||||
if element.isAfterHandle(point) {
|
||||
element.scrollBy(velocity)
|
||||
} else {
|
||||
element.scrollBy(-velocity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) HandleMouseUp (x, y int, button input.Button) {
|
||||
|
||||
if element.dragging {
|
||||
element.dragging = false
|
||||
element.redo()
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) HandleMouseMove (x, y int) {
|
||||
|
||||
if element.dragging {
|
||||
element.dragTo(image.Pt(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) HandleMouseScroll (x, y int, deltaX, deltaY float64) {
|
||||
@ -78,6 +133,15 @@ func (element *ScrollBar) SetBounds (content, viewport image.Rectangle) {
|
||||
element.redo()
|
||||
}
|
||||
|
||||
// OnScroll sets a function to be called when the user tries to move the scroll
|
||||
// bar's handle. The callback is passed a point representing the new viewport
|
||||
// position. For the scroll bar's position to visually update, the callback must
|
||||
// check if the position is valid and call ScrollBar.SetBounds with the new
|
||||
// viewport bounds.
|
||||
func (element *ScrollBar) OnScroll (callback func (viewport image.Point)) {
|
||||
element.onScroll = callback
|
||||
}
|
||||
|
||||
// SetTheme sets the element's theme.
|
||||
func (element *ScrollBar) SetTheme (new theme.Theme) {
|
||||
if new == element.theme.Theme { return }
|
||||
@ -93,6 +157,57 @@ func (element *ScrollBar) SetConfig (new config.Config) {
|
||||
element.redo()
|
||||
}
|
||||
|
||||
func (element *ScrollBar) isAfterHandle (point image.Point) bool {
|
||||
if element.vertical {
|
||||
return point.Y > element.bar.Min.Y
|
||||
} else {
|
||||
return point.X > element.bar.Min.X
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) fallbackDragOffset () image.Point {
|
||||
if element.vertical {
|
||||
return element.bar.Min.Add(image.Pt(0, element.bar.Dy() / 2))
|
||||
} else {
|
||||
return element.bar.Min.Add(image.Pt(element.bar.Dx() / 2, 0))
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) scrollBy (delta int) {
|
||||
deltaPoint := image.Point { }
|
||||
if element.vertical {
|
||||
deltaPoint.Y = delta
|
||||
} else {
|
||||
deltaPoint.X = delta
|
||||
}
|
||||
if element.onScroll != nil {
|
||||
element.onScroll(element.viewportBounds.Min.Add(deltaPoint))
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) dragTo (point image.Point) {
|
||||
point = point.Sub(element.dragOffset)
|
||||
var scrollX, scrollY float64
|
||||
|
||||
if element.vertical {
|
||||
ratio :=
|
||||
float64(element.contentBounds.Dy()) /
|
||||
float64(element.track.Dy())
|
||||
scrollX = float64(element.viewportBounds.Min.X)
|
||||
scrollY = float64(point.Y) * ratio
|
||||
} else {
|
||||
ratio :=
|
||||
float64(element.contentBounds.Dx()) /
|
||||
float64(element.track.Dx())
|
||||
scrollX = float64(point.X) * ratio
|
||||
scrollY = float64(element.viewportBounds.Min.Y)
|
||||
}
|
||||
|
||||
if element.onScroll != nil {
|
||||
element.onScroll(image.Pt(int(scrollX), int(scrollY)))
|
||||
}
|
||||
}
|
||||
|
||||
func (element *ScrollBar) recalculate () {
|
||||
if element.vertical {
|
||||
element.recalculateVertical()
|
||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module git.tebibyte.media/sashakoshka/tomo
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309013201-fc0de8121523
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309044548-401cba83602b
|
||||
github.com/faiface/beep v1.1.0
|
||||
github.com/jezek/xgbutil v0.0.0-20210302171758-530099784e66
|
||||
golang.org/x/image v0.3.0
|
||||
|
2
go.sum
2
go.sum
@ -4,6 +4,8 @@ git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309013013-f7ee80c8f908 h1:kFdc
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309013013-f7ee80c8f908/go.mod h1:cpXX8SAUDEvZX5m7scoyruavUhEqQ1SByfWzPFHkTbg=
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309013201-fc0de8121523 h1:1KaoiGetWYIDQKts6yas1hW+4ObkuTm6+TkFpl6jZxg=
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309013201-fc0de8121523/go.mod h1:cpXX8SAUDEvZX5m7scoyruavUhEqQ1SByfWzPFHkTbg=
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309044548-401cba83602b h1:vPFKR7vjN1VrMdMtpATMrKQobz/cqbPiRrA1EbtG6PM=
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309044548-401cba83602b/go.mod h1:cpXX8SAUDEvZX5m7scoyruavUhEqQ1SByfWzPFHkTbg=
|
||||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA=
|
||||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
|
||||
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g=
|
||||
|
BIN
xcf/large.xcf
Normal file
BIN
xcf/large.xcf
Normal file
Binary file not shown.
BIN
xcf/small.xcf
Normal file
BIN
xcf/small.xcf
Normal file
Binary file not shown.
Reference in New Issue
Block a user