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