feat(sointu, tracker, gioui): add a comment field to the instrument

This commit is contained in:
vsariola 2021-04-19 21:24:29 +03:00
parent 147e8a2513
commit 40d4d6576e
6 changed files with 183 additions and 129 deletions

View File

@ -3,6 +3,7 @@ package sointu
// Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument
type Instrument struct {
Name string `yaml:",omitempty"`
Comment string `yaml:",omitempty"`
NumVoices int
Units []Unit
}
@ -12,5 +13,5 @@ func (instr *Instrument) Copy() Instrument {
for i, u := range instr.Units {
units[i] = u.Copy()
}
return Instrument{Name: instr.Name, NumVoices: instr.NumVoices, Units: units}
return Instrument{Name: instr.Name, Comment: instr.Comment, NumVoices: instr.NumVoices, Units: units}
}

View File

@ -174,5 +174,8 @@ func (t *Tracker) loadInstrument(filename string) bool {
return false
}
t.SetInstrument(instrument)
if t.Instrument().Comment != "" {
t.InstrumentExpanded = true
}
return true
}

View File

@ -9,6 +9,7 @@ import (
"time"
"gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/layout"
"gioui.org/op"
@ -72,6 +73,16 @@ func (t *Tracker) layoutInstruments(gtx C) D {
func (t *Tracker) layoutInstrumentHeader(gtx C) D {
header := func(gtx C) D {
collapseIcon := icons.NavigationExpandLess
if t.InstrumentExpanded {
collapseIcon = icons.NavigationExpandMore
}
instrumentExpandBtnStyle := material.IconButton(t.Theme, t.InstrumentExpandBtn, widgetForIcon(collapseIcon))
instrumentExpandBtnStyle.Background = transparent
instrumentExpandBtnStyle.Inset = layout.UniformInset(unit.Dp(6))
instrumentExpandBtnStyle.Color = primaryColor
copyInstrumentBtnStyle := material.IconButton(t.Theme, t.CopyInstrumentBtn, widgetForIcon(icons.ContentContentCopy))
copyInstrumentBtnStyle.Background = transparent
copyInstrumentBtnStyle.Inset = layout.UniformInset(unit.Dp(6))
@ -96,23 +107,48 @@ func (t *Tracker) layoutInstrumentHeader(gtx C) D {
deleteInstrumentBtnStyle.Color = disabledTextColor
}
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(Label("Voices: ", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
maxRemain := t.MaxInstrumentVoices()
t.InstrumentVoices.Value = t.Instrument().NumVoices
numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain)
gtx.Constraints.Min.Y = gtx.Px(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Px(unit.Dp(70))
dims := numStyle.Layout(gtx)
t.SetInstrumentVoices(t.InstrumentVoices.Value)
return dims
}),
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
layout.Rigid(saveInstrumentBtnStyle.Layout),
layout.Rigid(loadInstrumentBtnStyle.Layout),
layout.Rigid(copyInstrumentBtnStyle.Layout),
layout.Rigid(deleteInstrumentBtnStyle.Layout))
header := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(Label("Voices: ", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
maxRemain := t.MaxInstrumentVoices()
t.InstrumentVoices.Value = t.Instrument().NumVoices
numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain)
gtx.Constraints.Min.Y = gtx.Px(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Px(unit.Dp(70))
dims := numStyle.Layout(gtx)
t.SetInstrumentVoices(t.InstrumentVoices.Value)
return dims
}),
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
layout.Rigid(instrumentExpandBtnStyle.Layout),
layout.Rigid(saveInstrumentBtnStyle.Layout),
layout.Rigid(loadInstrumentBtnStyle.Layout),
layout.Rigid(copyInstrumentBtnStyle.Layout),
layout.Rigid(deleteInstrumentBtnStyle.Layout))
}
for t.InstrumentExpandBtn.Clicked() {
t.InstrumentExpanded = !t.InstrumentExpanded
if !t.InstrumentExpanded {
key.FocusOp{Tag: nil}.Add(gtx.Ops) // clear focus
}
}
if t.InstrumentExpanded || t.InstrumentCommentEditor.Focused() { // we draw once the widget after it manages to lose focus
if t.InstrumentCommentEditor.Text() != t.Instrument().Comment {
t.InstrumentCommentEditor.SetText(t.Instrument().Comment)
}
editorStyle := material.Editor(t.Theme, t.InstrumentCommentEditor, "Comment")
editorStyle.Color = highEmphasisTextColor
ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(header),
layout.Rigid(func(gtx C) D {
return layout.UniformInset(unit.Dp(6)).Layout(gtx, editorStyle.Layout)
}),
)
t.SetInstrumentComment(t.InstrumentCommentEditor.Text())
return ret
}
return header(gtx)
}
for t.CopyInstrumentBtn.Clicked() {
contents, err := yaml.Marshal(t.Instrument())

View File

@ -82,6 +82,7 @@ var unitKeyMap = map[string]string{
func (t *Tracker) KeyEvent(w *app.Window, e key.Event) bool {
if e.State == key.Press {
if t.InstrumentNameEditor.Focused() ||
t.InstrumentCommentEditor.Focused() ||
t.OpenSongDialog.Visible ||
t.SaveSongDialog.Visible ||
t.SaveInstrumentDialog.Visible ||

View File

@ -23,63 +23,66 @@ const (
)
type Tracker struct {
Theme *material.Theme
MenuBar []widget.Clickable
Menus []Menu
OctaveNumberInput *NumberInput
BPM *NumberInput
RowsPerPattern *NumberInput
RowsPerBeat *NumberInput
Step *NumberInput
InstrumentVoices *NumberInput
TrackVoices *NumberInput
InstrumentNameEditor *widget.Editor
NewTrackBtn *widget.Clickable
DeleteTrackBtn *widget.Clickable
NewInstrumentBtn *widget.Clickable
DeleteInstrumentBtn *widget.Clickable
AddSemitoneBtn *widget.Clickable
SubtractSemitoneBtn *widget.Clickable
AddOctaveBtn *widget.Clickable
SubtractOctaveBtn *widget.Clickable
NoteOffBtn *widget.Clickable
SongLength *NumberInput
PanicBtn *widget.Clickable
CopyInstrumentBtn *widget.Clickable
SaveInstrumentBtn *widget.Clickable
LoadInstrumentBtn *widget.Clickable
ParameterList *layout.List
ParameterScrollBar *ScrollBar
Parameters []*ParameterWidget
UnitDragList *DragList
UnitScrollBar *ScrollBar
DeleteUnitBtn *widget.Clickable
ClearUnitBtn *widget.Clickable
ChooseUnitTypeList *layout.List
ChooseUnitScrollBar *ScrollBar
ChooseUnitTypeBtns []*widget.Clickable
AddUnitBtn *widget.Clickable
InstrumentDragList *DragList
InstrumentScrollBar *ScrollBar
TrackHexCheckBox *widget.Bool
TopHorizontalSplit *Split
BottomHorizontalSplit *Split
VerticalSplit *Split
StackUse []int
KeyPlaying map[string]uint32
Alert Alert
PatternOrderList *layout.List
PatternOrderScrollBar *ScrollBar
ConfirmInstrDelete *Dialog
ConfirmSongDialog *Dialog
WaveTypeDialog *Dialog
OpenSongDialog *FileDialog
SaveSongDialog *FileDialog
OpenInstrumentDialog *FileDialog
SaveInstrumentDialog *FileDialog
ExportWavDialog *FileDialog
ConfirmSongActionType int
window *app.Window
Theme *material.Theme
MenuBar []widget.Clickable
Menus []Menu
OctaveNumberInput *NumberInput
BPM *NumberInput
RowsPerPattern *NumberInput
RowsPerBeat *NumberInput
Step *NumberInput
InstrumentVoices *NumberInput
TrackVoices *NumberInput
InstrumentNameEditor *widget.Editor
NewTrackBtn *widget.Clickable
DeleteTrackBtn *widget.Clickable
NewInstrumentBtn *widget.Clickable
DeleteInstrumentBtn *widget.Clickable
AddSemitoneBtn *widget.Clickable
SubtractSemitoneBtn *widget.Clickable
AddOctaveBtn *widget.Clickable
SubtractOctaveBtn *widget.Clickable
NoteOffBtn *widget.Clickable
SongLength *NumberInput
PanicBtn *widget.Clickable
CopyInstrumentBtn *widget.Clickable
SaveInstrumentBtn *widget.Clickable
LoadInstrumentBtn *widget.Clickable
ParameterList *layout.List
ParameterScrollBar *ScrollBar
Parameters []*ParameterWidget
UnitDragList *DragList
UnitScrollBar *ScrollBar
DeleteUnitBtn *widget.Clickable
ClearUnitBtn *widget.Clickable
ChooseUnitTypeList *layout.List
ChooseUnitScrollBar *ScrollBar
ChooseUnitTypeBtns []*widget.Clickable
AddUnitBtn *widget.Clickable
InstrumentDragList *DragList
InstrumentScrollBar *ScrollBar
TrackHexCheckBox *widget.Bool
TopHorizontalSplit *Split
BottomHorizontalSplit *Split
VerticalSplit *Split
StackUse []int
KeyPlaying map[string]uint32
Alert Alert
PatternOrderList *layout.List
PatternOrderScrollBar *ScrollBar
ConfirmInstrDelete *Dialog
ConfirmSongDialog *Dialog
WaveTypeDialog *Dialog
OpenSongDialog *FileDialog
SaveSongDialog *FileDialog
OpenInstrumentDialog *FileDialog
SaveInstrumentDialog *FileDialog
ExportWavDialog *FileDialog
InstrumentCommentEditor *widget.Editor
InstrumentExpandBtn *widget.Clickable
InstrumentExpanded bool
ConfirmSongActionType int
window *app.Window
lastVolume tracker.Volume
volumeChan chan tracker.Volume
@ -128,60 +131,62 @@ func (t *Tracker) Close() {
func New(audioContext sointu.AudioContext, synthService sointu.SynthService, syncChannel chan<- []float32, window *app.Window) *Tracker {
t := &Tracker{
Theme: material.NewTheme(gofont.Collection()),
audioContext: audioContext,
BPM: new(NumberInput),
OctaveNumberInput: &NumberInput{Value: 4},
SongLength: new(NumberInput),
RowsPerPattern: new(NumberInput),
RowsPerBeat: new(NumberInput),
Step: &NumberInput{Value: 1},
InstrumentVoices: new(NumberInput),
TrackVoices: new(NumberInput),
InstrumentNameEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Middle},
NewTrackBtn: new(widget.Clickable),
DeleteTrackBtn: new(widget.Clickable),
NewInstrumentBtn: new(widget.Clickable),
DeleteInstrumentBtn: new(widget.Clickable),
AddSemitoneBtn: new(widget.Clickable),
SubtractSemitoneBtn: new(widget.Clickable),
AddOctaveBtn: new(widget.Clickable),
SubtractOctaveBtn: new(widget.Clickable),
NoteOffBtn: new(widget.Clickable),
AddUnitBtn: new(widget.Clickable),
DeleteUnitBtn: new(widget.Clickable),
ClearUnitBtn: new(widget.Clickable),
PanicBtn: new(widget.Clickable),
CopyInstrumentBtn: new(widget.Clickable),
SaveInstrumentBtn: new(widget.Clickable),
LoadInstrumentBtn: new(widget.Clickable),
TrackHexCheckBox: new(widget.Bool),
Menus: make([]Menu, 2),
MenuBar: make([]widget.Clickable, 2),
UnitDragList: &DragList{List: &layout.List{Axis: layout.Vertical}, HoverItem: -1},
UnitScrollBar: &ScrollBar{Axis: layout.Vertical},
refresh: make(chan struct{}, 1), // use non-blocking sends; no need to queue extra ticks if one is queued already
InstrumentDragList: &DragList{List: &layout.List{Axis: layout.Horizontal}, HoverItem: -1},
InstrumentScrollBar: &ScrollBar{Axis: layout.Horizontal},
ParameterList: &layout.List{Axis: layout.Vertical},
ParameterScrollBar: &ScrollBar{Axis: layout.Vertical},
TopHorizontalSplit: &Split{Ratio: -.6},
BottomHorizontalSplit: &Split{Ratio: -.6},
VerticalSplit: &Split{Axis: layout.Vertical},
ChooseUnitTypeList: &layout.List{Axis: layout.Vertical},
ChooseUnitScrollBar: &ScrollBar{Axis: layout.Vertical},
KeyPlaying: make(map[string]uint32),
volumeChan: make(chan tracker.Volume, 1),
playerCloser: make(chan struct{}),
PatternOrderList: &layout.List{Axis: layout.Vertical},
PatternOrderScrollBar: &ScrollBar{Axis: layout.Vertical},
ConfirmInstrDelete: new(Dialog),
ConfirmSongDialog: new(Dialog),
WaveTypeDialog: new(Dialog),
OpenSongDialog: NewFileDialog(),
SaveSongDialog: NewFileDialog(),
OpenInstrumentDialog: NewFileDialog(),
SaveInstrumentDialog: NewFileDialog(),
Theme: material.NewTheme(gofont.Collection()),
audioContext: audioContext,
BPM: new(NumberInput),
OctaveNumberInput: &NumberInput{Value: 4},
SongLength: new(NumberInput),
RowsPerPattern: new(NumberInput),
RowsPerBeat: new(NumberInput),
Step: &NumberInput{Value: 1},
InstrumentVoices: new(NumberInput),
TrackVoices: new(NumberInput),
InstrumentNameEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Middle},
NewTrackBtn: new(widget.Clickable),
DeleteTrackBtn: new(widget.Clickable),
NewInstrumentBtn: new(widget.Clickable),
DeleteInstrumentBtn: new(widget.Clickable),
AddSemitoneBtn: new(widget.Clickable),
SubtractSemitoneBtn: new(widget.Clickable),
AddOctaveBtn: new(widget.Clickable),
SubtractOctaveBtn: new(widget.Clickable),
NoteOffBtn: new(widget.Clickable),
AddUnitBtn: new(widget.Clickable),
DeleteUnitBtn: new(widget.Clickable),
ClearUnitBtn: new(widget.Clickable),
PanicBtn: new(widget.Clickable),
CopyInstrumentBtn: new(widget.Clickable),
SaveInstrumentBtn: new(widget.Clickable),
LoadInstrumentBtn: new(widget.Clickable),
TrackHexCheckBox: new(widget.Bool),
Menus: make([]Menu, 2),
MenuBar: make([]widget.Clickable, 2),
UnitDragList: &DragList{List: &layout.List{Axis: layout.Vertical}, HoverItem: -1},
UnitScrollBar: &ScrollBar{Axis: layout.Vertical},
refresh: make(chan struct{}, 1), // use non-blocking sends; no need to queue extra ticks if one is queued already
InstrumentDragList: &DragList{List: &layout.List{Axis: layout.Horizontal}, HoverItem: -1},
InstrumentScrollBar: &ScrollBar{Axis: layout.Horizontal},
ParameterList: &layout.List{Axis: layout.Vertical},
ParameterScrollBar: &ScrollBar{Axis: layout.Vertical},
TopHorizontalSplit: &Split{Ratio: -.6},
BottomHorizontalSplit: &Split{Ratio: -.6},
VerticalSplit: &Split{Axis: layout.Vertical},
ChooseUnitTypeList: &layout.List{Axis: layout.Vertical},
ChooseUnitScrollBar: &ScrollBar{Axis: layout.Vertical},
KeyPlaying: make(map[string]uint32),
volumeChan: make(chan tracker.Volume, 1),
playerCloser: make(chan struct{}),
PatternOrderList: &layout.List{Axis: layout.Vertical},
PatternOrderScrollBar: &ScrollBar{Axis: layout.Vertical},
ConfirmInstrDelete: new(Dialog),
ConfirmSongDialog: new(Dialog),
WaveTypeDialog: new(Dialog),
OpenSongDialog: NewFileDialog(),
SaveSongDialog: NewFileDialog(),
OpenInstrumentDialog: NewFileDialog(),
SaveInstrumentDialog: NewFileDialog(),
InstrumentCommentEditor: new(widget.Editor),
InstrumentExpandBtn: new(widget.Clickable),
ExportWavDialog: NewFileDialog(),
errorChannel: make(chan error, 32),

View File

@ -171,6 +171,14 @@ func (m *Model) SetInstrumentName(name string) {
m.song.Patch[m.instrIndex].Name = name
}
func (m *Model) SetInstrumentComment(comment string) {
if m.Instrument().Comment == comment {
return
}
m.saveUndo("SetInstrumentComment", 10)
m.song.Patch[m.instrIndex].Comment = comment
}
func (m *Model) SetBPM(value int) {
if value < 1 {
value = 1