mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
feat(tracker): move song related buttons to top and make the panel sizes adjustable
This commit is contained in:
parent
62ea87c709
commit
5124e0bf74
@ -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
88
tracker/songpanel.go
Normal 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
109
tracker/split.go
Normal 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}
|
||||
}
|
@ -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}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user