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 // Instrument includes a list of units consisting of the instrument, and the number of polyphonic voices for this instrument
type Instrument struct { type Instrument struct {
Name string `yaml:",omitempty"` Name string `yaml:",omitempty"`
Comment string `yaml:",omitempty"`
NumVoices int NumVoices int
Units []Unit Units []Unit
} }
@ -12,5 +13,5 @@ func (instr *Instrument) Copy() Instrument {
for i, u := range instr.Units { for i, u := range instr.Units {
units[i] = u.Copy() 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 return false
} }
t.SetInstrument(instrument) t.SetInstrument(instrument)
if t.Instrument().Comment != "" {
t.InstrumentExpanded = true
}
return true return true
} }

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"gioui.org/io/clipboard" "gioui.org/io/clipboard"
"gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
@ -72,6 +73,16 @@ func (t *Tracker) layoutInstruments(gtx C) D {
func (t *Tracker) layoutInstrumentHeader(gtx C) D { func (t *Tracker) layoutInstrumentHeader(gtx C) D {
header := func(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 := material.IconButton(t.Theme, t.CopyInstrumentBtn, widgetForIcon(icons.ContentContentCopy))
copyInstrumentBtnStyle.Background = transparent copyInstrumentBtnStyle.Background = transparent
copyInstrumentBtnStyle.Inset = layout.UniformInset(unit.Dp(6)) copyInstrumentBtnStyle.Inset = layout.UniformInset(unit.Dp(6))
@ -96,23 +107,48 @@ func (t *Tracker) layoutInstrumentHeader(gtx C) D {
deleteInstrumentBtnStyle.Color = disabledTextColor deleteInstrumentBtnStyle.Color = disabledTextColor
} }
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, header := func(gtx C) D {
layout.Rigid(Label("Voices: ", white)), return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(Label("Voices: ", white)),
maxRemain := t.MaxInstrumentVoices() layout.Rigid(func(gtx layout.Context) layout.Dimensions {
t.InstrumentVoices.Value = t.Instrument().NumVoices maxRemain := t.MaxInstrumentVoices()
numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain) t.InstrumentVoices.Value = t.Instrument().NumVoices
gtx.Constraints.Min.Y = gtx.Px(unit.Dp(20)) numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain)
gtx.Constraints.Min.X = gtx.Px(unit.Dp(70)) gtx.Constraints.Min.Y = gtx.Px(unit.Dp(20))
dims := numStyle.Layout(gtx) gtx.Constraints.Min.X = gtx.Px(unit.Dp(70))
t.SetInstrumentVoices(t.InstrumentVoices.Value) dims := numStyle.Layout(gtx)
return dims 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.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
layout.Rigid(loadInstrumentBtnStyle.Layout), layout.Rigid(instrumentExpandBtnStyle.Layout),
layout.Rigid(copyInstrumentBtnStyle.Layout), layout.Rigid(saveInstrumentBtnStyle.Layout),
layout.Rigid(deleteInstrumentBtnStyle.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() { for t.CopyInstrumentBtn.Clicked() {
contents, err := yaml.Marshal(t.Instrument()) 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 { func (t *Tracker) KeyEvent(w *app.Window, e key.Event) bool {
if e.State == key.Press { if e.State == key.Press {
if t.InstrumentNameEditor.Focused() || if t.InstrumentNameEditor.Focused() ||
t.InstrumentCommentEditor.Focused() ||
t.OpenSongDialog.Visible || t.OpenSongDialog.Visible ||
t.SaveSongDialog.Visible || t.SaveSongDialog.Visible ||
t.SaveInstrumentDialog.Visible || t.SaveInstrumentDialog.Visible ||

View File

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

View File

@ -171,6 +171,14 @@ func (m *Model) SetInstrumentName(name string) {
m.song.Patch[m.instrIndex].Name = name 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) { func (m *Model) SetBPM(value int) {
if value < 1 { if value < 1 {
value = 1 value = 1