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 {
|
layout.UniformInset(unit.Dp(2)).Layout(gtx, func(gtx2 layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx2,
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx2,
|
||||||
layout.Rigid(t.layoutControls),
|
layout.Rigid(t.layoutControls),
|
||||||
layout.Rigid(t.line(true, separatorLineColor)),
|
|
||||||
layout.Flexed(1, t.layoutTracksAndPatterns))
|
layout.Flexed(1, t.layoutTracksAndPatterns))
|
||||||
})
|
})
|
||||||
t.updateInstrumentScroll()
|
t.updateInstrumentScroll()
|
||||||
@ -86,14 +85,16 @@ func (t *Tracker) layoutTracksAndPatterns(gtx layout.Context) layout.Dimensions
|
|||||||
if !t.Playing {
|
if !t.Playing {
|
||||||
playPat = -1
|
playPat = -1
|
||||||
}
|
}
|
||||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
return t.BottomSplit.Layout(gtx,
|
||||||
layout.Rigid(t.layoutPatterns(
|
t.layoutPatterns(
|
||||||
t.song.Tracks,
|
t.song.Tracks,
|
||||||
t.ActiveTrack,
|
t.ActiveTrack,
|
||||||
t.DisplayPattern,
|
t.DisplayPattern,
|
||||||
t.CursorColumn,
|
t.CursorColumn,
|
||||||
playPat,
|
playPat,
|
||||||
)), layout.Flexed(1, t.layoutTracks))
|
),
|
||||||
|
t.layoutTracks,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) layoutTracks(gtx layout.Context) layout.Dimensions {
|
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() {
|
for t.SongLengthUpBtn.Clicked() {
|
||||||
t.IncreaseSongLength()
|
t.IncreaseSongLength()
|
||||||
}
|
}
|
||||||
@ -235,34 +228,19 @@ func (t *Tracker) layoutControls(gtx layout.Context) layout.Dimensions {
|
|||||||
t.DecreaseSongLength()
|
t.DecreaseSongLength()
|
||||||
}
|
}
|
||||||
|
|
||||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
return t.TopSplit.Layout(gtx,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
t.layoutSongPanel,
|
||||||
return in.Layout(gtx, smallButton(material.IconButton(t.Theme, t.SongLengthUpBtn, upIcon)).Layout)
|
func(gtx C) D {
|
||||||
}),
|
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Flexed(1, t.layoutInstruments()),
|
||||||
return in.Layout(gtx, enableButton(smallButton(material.IconButton(t.Theme, t.SongLengthDownBtn, downIcon)), t.song.SequenceLength() > 1).Layout)
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
}),
|
iconBtn := enableButton(material.IconButton(t.Theme, t.NewInstrumentBtn, addIcon), t.song.Patch.TotalVoices() < 32)
|
||||||
layout.Rigid(Label(fmt.Sprintf("BPM: %3v", t.song.BPM), white)),
|
return in.Layout(gtx, iconBtn.Layout)
|
||||||
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)
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) line(horizontal bool, color color.RGBA) layout.Widget {
|
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 inactiveBtnColor = color.RGBA{R: 61, G: 55, B: 55, A: 255}
|
||||||
|
|
||||||
var instrumentSurfaceColor = color.RGBA{R: 31, G: 31, B: 31, A: 31}
|
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
|
NewTrackBtn *widget.Clickable
|
||||||
NewInstrumentBtn *widget.Clickable
|
NewInstrumentBtn *widget.Clickable
|
||||||
LoadSongFileBtn *widget.Clickable
|
LoadSongFileBtn *widget.Clickable
|
||||||
|
NewSongFileBtn *widget.Clickable
|
||||||
SongLengthUpBtn *widget.Clickable
|
SongLengthUpBtn *widget.Clickable
|
||||||
SongLengthDownBtn *widget.Clickable
|
SongLengthDownBtn *widget.Clickable
|
||||||
SaveSongFileBtn *widget.Clickable
|
SaveSongFileBtn *widget.Clickable
|
||||||
@ -46,6 +47,8 @@ type Tracker struct {
|
|||||||
InstrumentList *layout.List
|
InstrumentList *layout.List
|
||||||
TrackHexCheckBoxes []*widget.Bool
|
TrackHexCheckBoxes []*widget.Bool
|
||||||
TrackShowHex []bool
|
TrackShowHex []bool
|
||||||
|
TopSplit *Split
|
||||||
|
BottomSplit *Split
|
||||||
|
|
||||||
sequencer *Sequencer
|
sequencer *Sequencer
|
||||||
ticked chan struct{}
|
ticked chan struct{}
|
||||||
@ -286,6 +289,7 @@ func New(audioContext sointu.AudioContext) *Tracker {
|
|||||||
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),
|
||||||
LoadSongFileBtn: new(widget.Clickable),
|
LoadSongFileBtn: new(widget.Clickable),
|
||||||
SaveSongFileBtn: new(widget.Clickable),
|
SaveSongFileBtn: new(widget.Clickable),
|
||||||
SongLengthUpBtn: new(widget.Clickable),
|
SongLengthUpBtn: new(widget.Clickable),
|
||||||
@ -298,7 +302,10 @@ func New(audioContext sointu.AudioContext) *Tracker {
|
|||||||
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),
|
||||||
|
BottomSplit: new(Split),
|
||||||
}
|
}
|
||||||
|
t.BottomSplit.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)
|
||||||
|
Loading…
Reference in New Issue
Block a user