feat(tracker): add adjustable vertical split between top and bottom

This commit is contained in:
vsariola 2021-01-15 12:42:06 +02:00
parent c90f8efbdc
commit f665a529e5
3 changed files with 121 additions and 88 deletions

View File

@ -77,11 +77,9 @@ func trackButton(t *material.Theme, w *widget.Clickable, text string, enabled bo
func (t *Tracker) Layout(gtx layout.Context) { func (t *Tracker) Layout(gtx layout.Context) {
paint.FillShape(gtx.Ops, backgroundColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op()) paint.FillShape(gtx.Ops, backgroundColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx2 layout.Context) layout.Dimensions { t.VerticalSplit.Layout(gtx,
return layout.Flex{Axis: layout.Vertical}.Layout(gtx2, t.layoutControls,
layout.Rigid(t.layoutControls), t.layoutTracksAndPatterns)
layout.Flexed(1, t.layoutTracksAndPatterns))
})
t.updateInstrumentScroll() t.updateInstrumentScroll()
} }
@ -90,7 +88,7 @@ func (t *Tracker) layoutTracksAndPatterns(gtx layout.Context) layout.Dimensions
if !t.Playing { if !t.Playing {
playPat = -1 playPat = -1
} }
return t.BottomSplit.Layout(gtx, return t.BottomHorizontalSplit.Layout(gtx,
t.layoutPatterns( t.layoutPatterns(
t.song.Tracks, t.song.Tracks,
t.ActiveTrack, t.ActiveTrack,
@ -208,9 +206,6 @@ func (t *Tracker) layoutTracks(gtx layout.Context) layout.Dimensions {
} }
func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions { func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min.Y = 250
gtx.Constraints.Max.Y = 250
go func() { go func() {
for t.BPMUpBtn.Clicked() { for t.BPMUpBtn.Clicked() {
t.ChangeBPM(1) t.ChangeBPM(1)
@ -231,7 +226,7 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
t.DecreaseSongLength() t.DecreaseSongLength()
} }
return t.TopSplit.Layout(gtx, return t.TopHorizontalSplit.Layout(gtx,
t.layoutSongPanel, t.layoutSongPanel,
t.layoutInstruments(), t.layoutInstruments(),
) )

View File

@ -16,25 +16,35 @@ type Split struct {
Ratio float32 Ratio float32
// Bar is the width for resizing the layout // Bar is the width for resizing the layout
Bar unit.Value Bar unit.Value
// 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 drag bool
dragID pointer.ID dragID pointer.ID
dragX float32 dragCoord float32
} }
var defaultBarWidth = unit.Dp(10) var defaultBarWidth = unit.Dp(10)
func (s *Split) Layout(gtx layout.Context, left, right layout.Widget) layout.Dimensions { func (s *Split) Layout(gtx layout.Context, first, second layout.Widget) layout.Dimensions {
bar := gtx.Px(s.Bar) bar := gtx.Px(s.Bar)
if bar <= 1 { if bar <= 1 {
bar = gtx.Px(defaultBarWidth) bar = gtx.Px(defaultBarWidth)
} }
proportion := (s.Ratio + 1) / 2 var coord int
leftsize := int(proportion*float32(gtx.Constraints.Max.X) - float32(bar)) if s.Axis == layout.Horizontal {
coord = gtx.Constraints.Max.X
} else {
coord = gtx.Constraints.Max.Y
}
rightoffset := leftsize + bar proportion := (s.Ratio + 1) / 2
rightsize := gtx.Constraints.Max.X - rightoffset firstSize := int(proportion*float32(coord) - float32(bar))
secondOffset := firstSize + bar
secondSize := coord - secondOffset
{ // handle input { // handle input
// Avoid affecting the input tree with pointer events. // Avoid affecting the input tree with pointer events.
@ -53,17 +63,28 @@ func (s *Split) Layout(gtx layout.Context, left, right layout.Widget) layout.Dim
} }
s.dragID = e.PointerID s.dragID = e.PointerID
s.dragX = e.Position.X if s.Axis == layout.Horizontal {
s.dragCoord = e.Position.X
} else {
s.dragCoord = e.Position.Y
}
case pointer.Drag: case pointer.Drag:
if s.dragID != e.PointerID { if s.dragID != e.PointerID {
break break
} }
deltaX := e.Position.X - s.dragX var deltaCoord, deltaRatio float32
s.dragX = e.Position.X 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)
}
deltaRatio := deltaX * 2 / float32(gtx.Constraints.Max.X)
s.Ratio += deltaRatio s.Ratio += deltaRatio
case pointer.Release: case pointer.Release:
@ -74,7 +95,12 @@ func (s *Split) Layout(gtx layout.Context, left, right layout.Widget) layout.Dim
} }
// register for input // register for input
barRect := image.Rect(leftsize, 0, rightoffset, gtx.Constraints.Max.X) var barRect image.Rectangle
if s.Axis == layout.Horizontal {
barRect = image.Rect(firstSize, 0, secondOffset, gtx.Constraints.Max.X)
} else {
barRect = image.Rect(0, firstSize, gtx.Constraints.Max.Y, secondOffset)
}
pointer.Rect(barRect).Add(gtx.Ops) pointer.Rect(barRect).Add(gtx.Ops)
pointer.InputOp{Tag: s, pointer.InputOp{Tag: s,
Types: pointer.Press | pointer.Drag | pointer.Release, Types: pointer.Press | pointer.Drag | pointer.Release,
@ -85,22 +111,31 @@ func (s *Split) Layout(gtx layout.Context, left, right layout.Widget) layout.Dim
} }
{ {
gtx := gtx
stack := op.Push(gtx.Ops) stack := op.Push(gtx.Ops)
gtx := gtx if s.Axis == layout.Horizontal {
gtx.Constraints = layout.Exact(image.Pt(leftsize, gtx.Constraints.Max.Y)) gtx.Constraints = layout.Exact(image.Pt(firstSize, gtx.Constraints.Max.Y))
left(gtx) } else {
gtx.Constraints = layout.Exact(image.Pt(gtx.Constraints.Max.X, firstSize))
}
first(gtx)
stack.Pop() stack.Pop()
} }
{ {
stack := op.Push(gtx.Ops)
op.Offset(f32.Pt(float32(rightoffset), 0)).Add(gtx.Ops)
gtx := gtx gtx := gtx
gtx.Constraints = layout.Exact(image.Pt(rightsize, gtx.Constraints.Max.Y)) stack := op.Push(gtx.Ops)
right(gtx) if s.Axis == layout.Horizontal {
op.Offset(f32.Pt(float32(secondOffset), 0)).Add(gtx.Ops)
gtx.Constraints = layout.Exact(image.Pt(secondSize, gtx.Constraints.Max.Y))
} else {
op.Offset(f32.Pt(0, float32(secondOffset))).Add(gtx.Ops)
gtx.Constraints = layout.Exact(image.Pt(gtx.Constraints.Max.X, secondSize))
}
second(gtx)
stack.Pop() stack.Pop()
} }

View File

@ -18,37 +18,38 @@ type Tracker struct {
song sointu.Song song sointu.Song
Playing bool Playing bool
// protects PlayPattern and PlayRow // protects PlayPattern and PlayRow
playRowPatMutex sync.RWMutex // protects song and playing playRowPatMutex sync.RWMutex // protects song and playing
PlayPattern int PlayPattern int
PlayRow int PlayRow int
CursorRow int CursorRow int
CursorColumn int CursorColumn int
DisplayPattern int DisplayPattern int
ActiveTrack int ActiveTrack int
CurrentInstrument int CurrentInstrument int
CurrentUnit int CurrentUnit int
CurrentOctave byte CurrentOctave byte
NoteTracking bool NoteTracking bool
Theme *material.Theme Theme *material.Theme
OctaveUpBtn *widget.Clickable OctaveUpBtn *widget.Clickable
OctaveDownBtn *widget.Clickable OctaveDownBtn *widget.Clickable
BPMUpBtn *widget.Clickable BPMUpBtn *widget.Clickable
BPMDownBtn *widget.Clickable BPMDownBtn *widget.Clickable
NewTrackBtn *widget.Clickable NewTrackBtn *widget.Clickable
NewInstrumentBtn *widget.Clickable NewInstrumentBtn *widget.Clickable
LoadSongFileBtn *widget.Clickable LoadSongFileBtn *widget.Clickable
NewSongFileBtn *widget.Clickable NewSongFileBtn *widget.Clickable
SongLengthUpBtn *widget.Clickable SongLengthUpBtn *widget.Clickable
SongLengthDownBtn *widget.Clickable SongLengthDownBtn *widget.Clickable
SaveSongFileBtn *widget.Clickable SaveSongFileBtn *widget.Clickable
ParameterSliders []*widget.Float ParameterSliders []*widget.Float
UnitBtns []*widget.Clickable UnitBtns []*widget.Clickable
InstrumentBtns []*widget.Clickable InstrumentBtns []*widget.Clickable
InstrumentList *layout.List InstrumentList *layout.List
TrackHexCheckBoxes []*widget.Bool TrackHexCheckBoxes []*widget.Bool
TrackShowHex []bool TrackShowHex []bool
TopSplit *Split TopHorizontalSplit *Split
BottomSplit *Split BottomHorizontalSplit *Split
VerticalSplit *Split
sequencer *Sequencer sequencer *Sequencer
ticked chan struct{} ticked chan struct{}
@ -279,33 +280,35 @@ func (t *Tracker) DecreaseSongLength() {
func New(audioContext sointu.AudioContext) *Tracker { func New(audioContext sointu.AudioContext) *Tracker {
t := &Tracker{ t := &Tracker{
Theme: material.NewTheme(gofont.Collection()), Theme: material.NewTheme(gofont.Collection()),
QuitButton: new(widget.Clickable), QuitButton: new(widget.Clickable),
CurrentOctave: 4, CurrentOctave: 4,
audioContext: audioContext, audioContext: audioContext,
OctaveUpBtn: new(widget.Clickable), OctaveUpBtn: new(widget.Clickable),
OctaveDownBtn: new(widget.Clickable), OctaveDownBtn: new(widget.Clickable),
BPMUpBtn: new(widget.Clickable), BPMUpBtn: new(widget.Clickable),
BPMDownBtn: new(widget.Clickable), BPMDownBtn: new(widget.Clickable),
NewTrackBtn: new(widget.Clickable), NewTrackBtn: new(widget.Clickable),
NewInstrumentBtn: new(widget.Clickable), NewInstrumentBtn: new(widget.Clickable),
NewSongFileBtn: new(widget.Clickable), NewSongFileBtn: new(widget.Clickable),
LoadSongFileBtn: new(widget.Clickable), LoadSongFileBtn: new(widget.Clickable),
SaveSongFileBtn: new(widget.Clickable), SaveSongFileBtn: new(widget.Clickable),
SongLengthUpBtn: new(widget.Clickable), SongLengthUpBtn: new(widget.Clickable),
SongLengthDownBtn: new(widget.Clickable), SongLengthDownBtn: new(widget.Clickable),
setPlaying: make(chan bool), setPlaying: make(chan bool),
rowJump: make(chan int), rowJump: make(chan int),
patternJump: make(chan int), patternJump: make(chan int),
ticked: make(chan struct{}), ticked: make(chan struct{}),
closer: make(chan struct{}), closer: make(chan struct{}),
undoStack: []sointu.Song{}, undoStack: []sointu.Song{},
redoStack: []sointu.Song{}, redoStack: []sointu.Song{},
InstrumentList: &layout.List{Axis: layout.Horizontal}, InstrumentList: &layout.List{Axis: layout.Horizontal},
TopSplit: new(Split), TopHorizontalSplit: new(Split),
BottomSplit: new(Split), BottomHorizontalSplit: new(Split),
VerticalSplit: new(Split),
} }
t.BottomSplit.Ratio = -.5 t.VerticalSplit.Axis = layout.Vertical
t.BottomHorizontalSplit.Ratio = -.5
t.Theme.Color.Primary = primaryColor t.Theme.Color.Primary = primaryColor
t.Theme.Color.InvText = black t.Theme.Color.InvText = black
go t.sequencerLoop(t.closer) go t.sequencerLoop(t.closer)