sointu/tracker/gioui/split.go
5684185+vsariola@users.noreply.github.com 1c020fffa3 refactor(gioui): update gioui to v0.5.0
2024-03-01 22:11:44 +02:00

171 lines
3.8 KiB
Go

package gioui
import (
"image"
"gioui.org/io/event"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/unit"
)
type Split struct {
// Ratio keeps the current layout.
// 0 is center, -1 completely to the left, 1 completely to the right.
Ratio float32
// Bar is the width for resizing the layout
Bar unit.Dp
// Axis is the split direction: layout.Horizontal splits the view in left
// and right, layout.Vertical splits the view in top and bottom
Axis layout.Axis
drag bool
dragID pointer.ID
dragCoord float32
}
var defaultBarWidth = unit.Dp(10)
func (s *Split) Layout(gtx layout.Context, first, second layout.Widget) layout.Dimensions {
bar := gtx.Dp(s.Bar)
if bar <= 1 {
bar = gtx.Dp(defaultBarWidth)
}
var coord int
if s.Axis == layout.Horizontal {
coord = gtx.Constraints.Max.X
} else {
coord = gtx.Constraints.Max.Y
}
proportion := (s.Ratio + 1) / 2
firstSize := int(proportion*float32(coord) - float32(bar))
secondOffset := firstSize + bar
secondSize := coord - secondOffset
{ // handle input
// Avoid affecting the input tree with pointer events.
for {
ev, ok := gtx.Event(pointer.Filter{
Target: s,
Kinds: pointer.Press | pointer.Drag | pointer.Release,
// TODO: there should be a grab; there was Grab: s.drag,
})
if !ok {
break
}
e, ok := ev.(pointer.Event)
if !ok {
continue
}
switch e.Kind {
case pointer.Press:
if s.drag {
break
}
s.dragID = e.PointerID
if s.Axis == layout.Horizontal {
s.dragCoord = e.Position.X
} else {
s.dragCoord = e.Position.Y
}
s.drag = true
case pointer.Drag:
if s.dragID != e.PointerID {
break
}
var deltaCoord, deltaRatio float32
if s.Axis == layout.Horizontal {
deltaCoord = e.Position.X - s.dragCoord
s.dragCoord = e.Position.X
deltaRatio = deltaCoord * 2 / float32(gtx.Constraints.Max.X)
} else {
deltaCoord = e.Position.Y - s.dragCoord
s.dragCoord = e.Position.Y
deltaRatio = deltaCoord * 2 / float32(gtx.Constraints.Max.Y)
}
s.Ratio += deltaRatio
case pointer.Release:
fallthrough
case pointer.Cancel:
s.drag = false
}
}
low := -1 + float32(bar)/float32(coord)*2
const snapMargin = 0.1
if s.Ratio < low {
s.Ratio = low
}
if s.Ratio > 1 {
s.Ratio = 1
}
if s.Ratio < low+snapMargin {
firstSize = 0
secondOffset = bar
secondSize = coord - bar
} else if s.Ratio > 1-snapMargin {
firstSize = coord - bar
secondOffset = coord
secondSize = 0
}
// register for input
var barRect image.Rectangle
if s.Axis == layout.Horizontal {
barRect = image.Rect(firstSize, 0, secondOffset, gtx.Constraints.Max.Y)
} else {
barRect = image.Rect(0, firstSize, gtx.Constraints.Max.X, secondOffset)
}
area := clip.Rect(barRect).Push(gtx.Ops)
event.Op(gtx.Ops, s)
area.Pop()
}
{
gtx := gtx
if s.Axis == layout.Horizontal {
gtx.Constraints = layout.Exact(image.Pt(firstSize, gtx.Constraints.Max.Y))
} else {
gtx.Constraints = layout.Exact(image.Pt(gtx.Constraints.Max.X, firstSize))
}
area := clip.Rect(image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)).Push(gtx.Ops)
first(gtx)
area.Pop()
}
{
gtx := gtx
var transform op.TransformStack
if s.Axis == layout.Horizontal {
transform = op.Offset(image.Pt(secondOffset, 0)).Push(gtx.Ops)
gtx.Constraints = layout.Exact(image.Pt(secondSize, gtx.Constraints.Max.Y))
} else {
transform = op.Offset(image.Pt(0, secondOffset)).Push(gtx.Ops)
gtx.Constraints = layout.Exact(image.Pt(gtx.Constraints.Max.X, secondSize))
}
area := clip.Rect(image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)).Push(gtx.Ops)
second(gtx)
area.Pop()
transform.Pop()
}
return layout.Dimensions{Size: gtx.Constraints.Max}
}