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/artist"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
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 {
|
type ScrollBar struct {
|
||||||
*core.Core
|
*core.Core
|
||||||
core core.CoreControl
|
core core.CoreControl
|
||||||
@ -14,7 +22,7 @@ type ScrollBar struct {
|
|||||||
vertical bool
|
vertical bool
|
||||||
enabled bool
|
enabled bool
|
||||||
dragging bool
|
dragging bool
|
||||||
dragOffset int
|
dragOffset image.Point
|
||||||
track image.Rectangle
|
track image.Rectangle
|
||||||
bar image.Rectangle
|
bar image.Rectangle
|
||||||
|
|
||||||
@ -24,10 +32,11 @@ type ScrollBar struct {
|
|||||||
config config.Wrapped
|
config config.Wrapped
|
||||||
theme theme.Wrapped
|
theme theme.Wrapped
|
||||||
|
|
||||||
onSlide func ()
|
onScroll func (viewport image.Point)
|
||||||
onRelease func ()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func NewScrollBar (vertical bool) (element *ScrollBar) {
|
||||||
element = &ScrollBar {
|
element = &ScrollBar {
|
||||||
vertical: vertical,
|
vertical: vertical,
|
||||||
@ -44,15 +53,61 @@ func NewScrollBar (vertical bool) (element *ScrollBar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *ScrollBar) HandleMouseDown (x, y int, button input.Button) {
|
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) {
|
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) {
|
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) {
|
func (element *ScrollBar) HandleMouseScroll (x, y int, deltaX, deltaY float64) {
|
||||||
@ -78,6 +133,15 @@ func (element *ScrollBar) SetBounds (content, viewport image.Rectangle) {
|
|||||||
element.redo()
|
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.
|
// SetTheme sets the element's theme.
|
||||||
func (element *ScrollBar) SetTheme (new theme.Theme) {
|
func (element *ScrollBar) SetTheme (new theme.Theme) {
|
||||||
if new == element.theme.Theme { return }
|
if new == element.theme.Theme { return }
|
||||||
@ -93,6 +157,57 @@ func (element *ScrollBar) SetConfig (new config.Config) {
|
|||||||
element.redo()
|
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 () {
|
func (element *ScrollBar) recalculate () {
|
||||||
if element.vertical {
|
if element.vertical {
|
||||||
element.recalculateVertical()
|
element.recalculateVertical()
|
||||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module git.tebibyte.media/sashakoshka/tomo
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
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/faiface/beep v1.1.0
|
||||||
github.com/jezek/xgbutil v0.0.0-20210302171758-530099784e66
|
github.com/jezek/xgbutil v0.0.0-20210302171758-530099784e66
|
||||||
golang.org/x/image v0.3.0
|
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-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 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-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 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA=
|
||||||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
|
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=
|
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