feat(tracker): move song related buttons to top and make the panel sizes adjustable

This commit is contained in:
vsariola 2021-01-13 18:37:20 +02:00
parent 62ea87c709
commit 5124e0bf74
5 changed files with 223 additions and 39 deletions

View File

@ -75,7 +75,6 @@ func (t *Tracker) Layout(gtx layout.Context) {
layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx2 layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx2,
layout.Rigid(t.layoutControls),
layout.Rigid(t.line(true, separatorLineColor)),
layout.Flexed(1, t.layoutTracksAndPatterns))
})
t.updateInstrumentScroll()
@ -86,14 +85,16 @@ func (t *Tracker) layoutTracksAndPatterns(gtx layout.Context) layout.Dimensions
if !t.Playing {
playPat = -1
}
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(t.layoutPatterns(
return t.BottomSplit.Layout(gtx,
t.layoutPatterns(
t.song.Tracks,
t.ActiveTrack,
t.DisplayPattern,
t.CursorColumn,
playPat,
)), layout.Flexed(1, t.layoutTracks))
),
t.layoutTracks,
)
}
func (t *Tracker) layoutTracks(gtx layout.Context) layout.Dimensions {
@ -219,14 +220,6 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
}
}()
for t.LoadSongFileBtn.Clicked() {
t.LoadSongFile()
}
for t.SaveSongFileBtn.Clicked() {
t.SaveSongFile()
}
for t.SongLengthUpBtn.Clicked() {
t.IncreaseSongLength()
}
@ -235,34 +228,19 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
t.DecreaseSongLength()
}
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, smallButton(material.IconButton(t.Theme, t.SongLengthUpBtn, upIcon)).Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.SongLengthDownBtn, downIcon)), t.song.SequenceLength() > 1).Layout)
}),
layout.Rigid(Label(fmt.Sprintf("BPM: %3v", t.song.BPM), white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.BPMUpBtn, upIcon)), t.song.BPM < 999).Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.BPMDownBtn, downIcon)), t.song.BPM > 1).Layout)
}),
layout.Flexed(1, t.layoutInstruments()),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
iconBtn := enableButton(material.IconButton(t.Theme, t.NewInstrumentBtn, addIcon), t.song.Patch.TotalVoices() < 32)
return in.Layout(gtx, iconBtn.Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
iconBtn := material.IconButton(t.Theme, t.LoadSongFileBtn, loadIcon)
return in.Layout(gtx, iconBtn.Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
iconBtn := material.IconButton(t.Theme, t.SaveSongFileBtn, saveIcon)
return in.Layout(gtx, iconBtn.Layout)
}),
return t.TopSplit.Layout(gtx,
t.layoutSongPanel,
func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Flexed(1, t.layoutInstruments()),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
iconBtn := enableButton(material.IconButton(t.Theme, t.NewInstrumentBtn, addIcon), t.song.Patch.TotalVoices() < 32)
return in.Layout(gtx, iconBtn.Layout)
}),
)
},
)
}
func (t *Tracker) line(horizontal bool, color color.RGBA) layout.Widget {

88
tracker/songpanel.go Normal file
View File

@ -0,0 +1,88 @@
package tracker
import (
"fmt"
"image"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget/material"
)
func (t *Tracker) layoutSongPanel(gtx C) D {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(t.layoutSongButtons),
layout.Flexed(1, t.layoutSongOptions),
)
}
func (t *Tracker) layoutSongButtons(gtx C) D {
gtx.Constraints.Max.Y = gtx.Px(unit.Dp(36))
gtx.Constraints.Min.Y = gtx.Px(unit.Dp(36))
//paint.FillShape(gtx.Ops, primaryColorDark, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
for t.LoadSongFileBtn.Clicked() {
t.LoadSongFile()
}
for t.SaveSongFileBtn.Clicked() {
t.SaveSongFile()
}
newBtnStyle := material.IconButton(t.Theme, t.NewSongFileBtn, addIcon)
newBtnStyle.Background = transparent
newBtnStyle.Inset = layout.UniformInset(unit.Dp(6))
newBtnStyle.Color = primaryColor
loadBtnStyle := material.IconButton(t.Theme, t.LoadSongFileBtn, loadIcon)
loadBtnStyle.Background = transparent
loadBtnStyle.Inset = layout.UniformInset(unit.Dp(6))
loadBtnStyle.Color = primaryColor
saveBtnStyle := material.IconButton(t.Theme, t.SaveSongFileBtn, saveIcon)
saveBtnStyle.Background = transparent
saveBtnStyle.Inset = layout.UniformInset(unit.Dp(6))
saveBtnStyle.Color = primaryColor
layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(newBtnStyle.Layout),
layout.Rigid(loadBtnStyle.Layout),
layout.Rigid(saveBtnStyle.Layout),
)
return layout.Dimensions{Size: gtx.Constraints.Max}
}
func (t *Tracker) layoutSongOptions(gtx C) D {
paint.FillShape(gtx.Ops, songSurfaceColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
in := layout.UniformInset(unit.Dp(1))
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(Label(fmt.Sprintf("LEN: %3v", t.song.SequenceLength()), white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, smallButton(material.IconButton(t.Theme, t.SongLengthUpBtn, upIcon)).Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.SongLengthDownBtn, downIcon)), t.song.SequenceLength() > 1).Layout)
}),
)
}),
layout.Rigid(func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(Label(fmt.Sprintf("BPM: %3v", t.song.BPM), white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.BPMUpBtn, upIcon)), t.song.BPM < 999).Layout)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.BPMDownBtn, downIcon)), t.song.BPM > 1).Layout)
}),
)
}),
)
}

109
tracker/split.go Normal file
View File

@ -0,0 +1,109 @@
package tracker
import (
"image"
"gioui.org/f32"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op"
"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.Value
drag bool
dragID pointer.ID
dragX float32
}
var defaultBarWidth = unit.Dp(10)
func (s *Split) Layout(gtx layout.Context, left, right layout.Widget) layout.Dimensions {
bar := gtx.Px(s.Bar)
if bar <= 1 {
bar = gtx.Px(defaultBarWidth)
}
proportion := (s.Ratio + 1) / 2
leftsize := int(proportion*float32(gtx.Constraints.Max.X) - float32(bar))
rightoffset := leftsize + bar
rightsize := gtx.Constraints.Max.X - rightoffset
{ // handle input
// Avoid affecting the input tree with pointer events.
stack := op.Push(gtx.Ops)
for _, ev := range gtx.Events(s) {
e, ok := ev.(pointer.Event)
if !ok {
continue
}
switch e.Type {
case pointer.Press:
if s.drag {
break
}
s.dragID = e.PointerID
s.dragX = e.Position.X
case pointer.Drag:
if s.dragID != e.PointerID {
break
}
deltaX := e.Position.X - s.dragX
s.dragX = e.Position.X
deltaRatio := deltaX * 2 / float32(gtx.Constraints.Max.X)
s.Ratio += deltaRatio
case pointer.Release:
fallthrough
case pointer.Cancel:
s.drag = false
}
}
// register for input
barRect := image.Rect(leftsize, 0, rightoffset, gtx.Constraints.Max.X)
pointer.Rect(barRect).Add(gtx.Ops)
pointer.InputOp{Tag: s,
Types: pointer.Press | pointer.Drag | pointer.Release,
Grab: s.drag,
}.Add(gtx.Ops)
stack.Pop()
}
{
stack := op.Push(gtx.Ops)
gtx := gtx
gtx.Constraints = layout.Exact(image.Pt(leftsize, gtx.Constraints.Max.Y))
left(gtx)
stack.Pop()
}
{
stack := op.Push(gtx.Ops)
op.Offset(f32.Pt(float32(rightoffset), 0)).Add(gtx.Ops)
gtx := gtx
gtx.Constraints = layout.Exact(image.Pt(rightsize, gtx.Constraints.Max.Y))
right(gtx)
stack.Pop()
}
return layout.Dimensions{Size: gtx.Constraints.Max}
}

View File

@ -82,3 +82,5 @@ var patternCursorColor = color.RGBA{R: 38, G: 79, B: 120, A: 64}
var inactiveBtnColor = color.RGBA{R: 61, G: 55, B: 55, A: 255}
var instrumentSurfaceColor = color.RGBA{R: 31, G: 31, B: 31, A: 31}
var songSurfaceColor = color.RGBA{R: 31, G: 31, B: 31, A: 31}

View File

@ -37,6 +37,7 @@ type Tracker struct {
NewTrackBtn *widget.Clickable
NewInstrumentBtn *widget.Clickable
LoadSongFileBtn *widget.Clickable
NewSongFileBtn *widget.Clickable
SongLengthUpBtn *widget.Clickable
SongLengthDownBtn *widget.Clickable
SaveSongFileBtn *widget.Clickable
@ -46,6 +47,8 @@ type Tracker struct {
InstrumentList *layout.List
TrackHexCheckBoxes []*widget.Bool
TrackShowHex []bool
TopSplit *Split
BottomSplit *Split
sequencer *Sequencer
ticked chan struct{}
@ -286,6 +289,7 @@ func New(audioContext sointu.AudioContext) *Tracker {
BPMDownBtn: new(widget.Clickable),
NewTrackBtn: new(widget.Clickable),
NewInstrumentBtn: new(widget.Clickable),
NewSongFileBtn: new(widget.Clickable),
LoadSongFileBtn: new(widget.Clickable),
SaveSongFileBtn: new(widget.Clickable),
SongLengthUpBtn: new(widget.Clickable),
@ -298,7 +302,10 @@ func New(audioContext sointu.AudioContext) *Tracker {
undoStack: []sointu.Song{},
redoStack: []sointu.Song{},
InstrumentList: &layout.List{Axis: layout.Horizontal},
TopSplit: new(Split),
BottomSplit: new(Split),
}
t.BottomSplit.Ratio = -.5
t.Theme.Color.Primary = primaryColor
t.Theme.Color.InvText = black
go t.sequencerLoop(t.closer)