mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-14 02:54:37 -04:00
feat(sointu, tracker, gioui): add a comment field to the instrument
This commit is contained in:
@ -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}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
@ -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 ||
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user