From 40d4d6576eaba0c86ff4075a77678c5a231a2eb5 Mon Sep 17 00:00:00 2001 From: vsariola <5684185+vsariola@users.noreply.github.com> Date: Mon, 19 Apr 2021 21:24:29 +0300 Subject: [PATCH] feat(sointu, tracker, gioui): add a comment field to the instrument --- instrument.go | 3 +- tracker/gioui/files.go | 3 + tracker/gioui/instruments.go | 70 ++++++++--- tracker/gioui/keyevent.go | 1 + tracker/gioui/tracker.go | 227 ++++++++++++++++++----------------- tracker/model.go | 8 ++ 6 files changed, 183 insertions(+), 129 deletions(-) diff --git a/instrument.go b/instrument.go index b6f1133..2e867ed 100644 --- a/instrument.go +++ b/instrument.go @@ -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} } diff --git a/tracker/gioui/files.go b/tracker/gioui/files.go index 8ce9428..8fc79a3 100644 --- a/tracker/gioui/files.go +++ b/tracker/gioui/files.go @@ -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 } diff --git a/tracker/gioui/instruments.go b/tracker/gioui/instruments.go index afeb157..0230954 100644 --- a/tracker/gioui/instruments.go +++ b/tracker/gioui/instruments.go @@ -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()) diff --git a/tracker/gioui/keyevent.go b/tracker/gioui/keyevent.go index d81fbfb..f16b913 100644 --- a/tracker/gioui/keyevent.go +++ b/tracker/gioui/keyevent.go @@ -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 || diff --git a/tracker/gioui/tracker.go b/tracker/gioui/tracker.go index 629293e..5692f11 100644 --- a/tracker/gioui/tracker.go +++ b/tracker/gioui/tracker.go @@ -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), diff --git a/tracker/model.go b/tracker/model.go index 7d899f5..a4d3aa0 100644 --- a/tracker/model.go +++ b/tracker/model.go @@ -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