mirror of
https://github.com/vsariola/sointu.git
synced 2026-02-23 16:43:25 -05:00
refactor(tracker): make Model methods return List, avoiding .List()
This commit is contained in:
parent
74beb6760c
commit
1693d7ed5e
@ -145,7 +145,7 @@ func (m *AddTrack) Do() {
|
||||
|
||||
func (m *Model) DeleteTrack() Action { return MakeAction((*DeleteTrack)(m)) }
|
||||
func (m *DeleteTrack) Enabled() bool { return len(m.d.Song.Score.Tracks) > 0 }
|
||||
func (m *DeleteTrack) Do() { (*Model)(m).Tracks().List().DeleteElements(false) }
|
||||
func (m *DeleteTrack) Do() { (*Model)(m).Tracks().DeleteElements(false) }
|
||||
|
||||
// AddInstrument
|
||||
|
||||
@ -164,7 +164,7 @@ func (m *AddInstrument) Do() {
|
||||
|
||||
func (m *Model) DeleteInstrument() Action { return MakeAction((*DeleteInstrument)(m)) }
|
||||
func (m *DeleteInstrument) Enabled() bool { return len((*Model)(m).d.Song.Patch) > 0 }
|
||||
func (m *DeleteInstrument) Do() { (*Model)(m).Instruments().List().DeleteElements(false) }
|
||||
func (m *DeleteInstrument) Do() { (*Model)(m).Instruments().DeleteElements(false) }
|
||||
|
||||
// SplitTrack
|
||||
|
||||
@ -259,7 +259,7 @@ func (m *DeleteUnit) Enabled() bool {
|
||||
}
|
||||
func (m *DeleteUnit) Do() {
|
||||
defer (*Model)(m).change("DeleteUnitAction", PatchChange, MajorChange)()
|
||||
(*Model)(m).Units().List().DeleteElements(true)
|
||||
(*Model)(m).Units().DeleteElements(true)
|
||||
}
|
||||
|
||||
// ClearUnit
|
||||
@ -271,7 +271,7 @@ func (m *ClearUnit) Enabled() bool {
|
||||
}
|
||||
func (m *ClearUnit) Do() {
|
||||
defer (*Model)(m).change("DeleteUnitAction", PatchChange, MajorChange)()
|
||||
l := ((*Model)(m)).Units().List()
|
||||
l := ((*Model)(m)).Units()
|
||||
r := l.listRange()
|
||||
for i := r.Start; i < r.End; i++ {
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units[i] = sointu.Unit{}
|
||||
@ -426,7 +426,7 @@ func (m *PlaySelected) Enabled() bool { return !m.instrEnlarged }
|
||||
func (m *PlaySelected) Do() {
|
||||
(*Model)(m).setPanic(false)
|
||||
m.playing = true
|
||||
l := (*Model)(m).OrderRows().List()
|
||||
l := (*Model)(m).OrderRows()
|
||||
r := l.listRange()
|
||||
newLoop := Loop{r.Start, r.End - r.Start}
|
||||
(*Model)(m).setLoop(newLoop)
|
||||
|
||||
@ -272,6 +272,7 @@ func (m *UnitSearching) SetValue(val bool) {
|
||||
return
|
||||
}
|
||||
m.d.UnitSearchString = m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Type
|
||||
(*Model)(m).updateDerivedUnitSearch()
|
||||
}
|
||||
|
||||
// UnitDisabled methods
|
||||
@ -290,7 +291,7 @@ func (m *UnitDisabled) SetValue(val bool) {
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return
|
||||
}
|
||||
l := ((*Model)(m)).Units().List()
|
||||
l := ((*Model)(m)).Units()
|
||||
r := l.listRange()
|
||||
defer (*Model)(m).change("UnitDisabledSet", PatchChange, MajorChange)()
|
||||
for i := r.Start; i < r.End; i++ {
|
||||
@ -315,7 +316,7 @@ func (t *LoopToggle) SetValue(val bool) {
|
||||
m := (*Model)(t)
|
||||
newLoop := Loop{}
|
||||
if val {
|
||||
l := m.OrderRows().List()
|
||||
l := m.OrderRows()
|
||||
r := l.listRange()
|
||||
newLoop = Loop{r.Start, r.End - r.Start}
|
||||
}
|
||||
|
||||
@ -33,10 +33,11 @@ type (
|
||||
// corresponding part of the model changes.
|
||||
derivedModelData struct {
|
||||
// map Unit by ID, other entities by their respective index
|
||||
patch []derivedInstrument
|
||||
tracks []derivedTrack
|
||||
railError RailError
|
||||
presetSearch derivedPresetSearch
|
||||
patch []derivedInstrument
|
||||
tracks []derivedTrack
|
||||
railError RailError
|
||||
presetSearch derivedPresetSearch
|
||||
searchResults []string
|
||||
}
|
||||
|
||||
derivedInstrument struct {
|
||||
|
||||
@ -128,7 +128,7 @@ func (s FilledDragListStyle) Layout(gtx C, element, bg func(gtx C, i int) D) D {
|
||||
gtx.Execute(op.InvalidateCmd{})
|
||||
}
|
||||
|
||||
_, isMutable := s.dragList.TrackerList.ListData.(tracker.MutableListData)
|
||||
isMutable := s.dragList.TrackerList.Mutable()
|
||||
|
||||
listElem := func(gtx C, index int) D {
|
||||
for len(s.dragList.tags) <= index {
|
||||
|
||||
@ -53,7 +53,7 @@ type (
|
||||
|
||||
func NewInstrumentEditor(m *tracker.Model) *InstrumentEditor {
|
||||
ret := &InstrumentEditor{
|
||||
dragList: NewDragList(m.Units().List(), layout.Vertical),
|
||||
dragList: NewDragList(m.Units(), layout.Vertical),
|
||||
addUnitBtn: new(Clickable),
|
||||
searchEditor: NewEditor(true, true, text.Start),
|
||||
DeleteUnitBtn: new(Clickable),
|
||||
@ -62,8 +62,8 @@ func NewInstrumentEditor(m *tracker.Model) *InstrumentEditor {
|
||||
CopyUnitBtn: new(Clickable),
|
||||
SelectTypeBtn: new(Clickable),
|
||||
commentEditor: NewEditor(true, true, text.Start),
|
||||
paramTable: NewScrollTable(m.Params().Table(), m.ParamVertList().List(), m.Units().List()),
|
||||
searchList: NewDragList(m.SearchResults().List(), layout.Vertical),
|
||||
paramTable: NewScrollTable(m.Params().Table(), m.ParamVertList().List(), m.Units()),
|
||||
searchList: NewDragList(m.SearchResults(), layout.Vertical),
|
||||
searching: m.UnitSearching(),
|
||||
}
|
||||
ret.caser = cases.Title(language.English)
|
||||
@ -95,7 +95,7 @@ func (ul *InstrumentEditor) layoutList(gtx C) D {
|
||||
element := func(gtx C, i int) D {
|
||||
gtx.Constraints.Max.Y = gtx.Dp(20)
|
||||
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
|
||||
u := t.Units().Item(i)
|
||||
u := t.Unit(i)
|
||||
editorStyle := t.Theme.InstrumentEditor.UnitList.Name
|
||||
signalError := t.RailError()
|
||||
switch {
|
||||
@ -169,7 +169,7 @@ func (ul *InstrumentEditor) update(gtx C) {
|
||||
case key.NameRightArrow:
|
||||
t.PatchPanel.instrEditor.paramTable.RowTitleList.Focus()
|
||||
case key.NameDeleteBackward:
|
||||
t.Units().SetSelectedType("")
|
||||
t.SetSelectedUnitType("")
|
||||
t.UnitSearching().SetValue(true)
|
||||
ul.searchEditor.Focus()
|
||||
case key.NameEnter, key.NameReturn:
|
||||
@ -185,12 +185,12 @@ func (ul *InstrumentEditor) update(gtx C) {
|
||||
if str.Value() != "" {
|
||||
for _, n := range sointu.UnitNames {
|
||||
if strings.HasPrefix(n, str.Value()) {
|
||||
t.Units().SetSelectedType(n)
|
||||
t.SetSelectedUnitType(n)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.Units().SetSelectedType("")
|
||||
t.SetSelectedUnitType("")
|
||||
}
|
||||
}
|
||||
ul.dragList.Focus()
|
||||
@ -202,7 +202,7 @@ func (ul *InstrumentEditor) update(gtx C) {
|
||||
ul.searchEditor.Focus()
|
||||
}
|
||||
for ul.CopyUnitBtn.Clicked(gtx) {
|
||||
if contents, ok := t.Units().List().CopyElements(); ok {
|
||||
if contents, ok := t.Units().CopyElements(); ok {
|
||||
gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(bytes.NewReader(contents))})
|
||||
t.Alerts().Add("Unit(s) copied to clipboard", tracker.Info)
|
||||
}
|
||||
@ -288,8 +288,8 @@ func (pe *InstrumentEditor) layoutTable(gtx C) D {
|
||||
}
|
||||
|
||||
func (pe *InstrumentEditor) ChooseUnitType(t *Tracker) {
|
||||
if ut, ok := t.SearchResults().Item(pe.searchList.TrackerList.Selected()); ok {
|
||||
t.Units().SetSelectedType(ut)
|
||||
if ut, ok := t.SearchResult(pe.searchList.TrackerList.Selected()); ok {
|
||||
t.SetSelectedUnitType(ut)
|
||||
pe.paramTable.RowTitleList.Focus()
|
||||
}
|
||||
}
|
||||
@ -321,7 +321,7 @@ func (pe *InstrumentEditor) layoutRack(gtx C) D {
|
||||
if y < 0 || y >= len(pe.Parameters) {
|
||||
return D{}
|
||||
}
|
||||
item := t.Units().Item(y)
|
||||
item := t.Unit(y)
|
||||
sr := Rail(t.Theme, item.Signals)
|
||||
label := Label(t.Theme, &t.Theme.UnitEditor.UnitList.Name, item.Type)
|
||||
switch {
|
||||
@ -360,7 +360,7 @@ func (pe *InstrumentEditor) layoutRack(gtx C) D {
|
||||
}
|
||||
|
||||
param := t.Model.Params().Item(point)
|
||||
paramStyle := Param(param, t.Theme, pe.Parameters[y][x], pe.paramTable.Table.Cursor() == point, t.Units().Item(y).Disabled)
|
||||
paramStyle := Param(param, t.Theme, pe.Parameters[y][x], pe.paramTable.Table.Cursor() == point, t.Unit(y).Disabled)
|
||||
paramStyle.Layout(gtx)
|
||||
if x == t.Model.Params().RowWidth(y) {
|
||||
if y == cursor.Y {
|
||||
@ -373,7 +373,7 @@ func (pe *InstrumentEditor) layoutRack(gtx C) D {
|
||||
return pe.commentEditor.Layout(gtx, t.UnitComment(), t.Theme, &t.Theme.InstrumentEditor.UnitComment, "---")
|
||||
})
|
||||
} else {
|
||||
comment := t.Units().Item(y).Comment
|
||||
comment := t.Unit(y).Comment
|
||||
if comment != "" {
|
||||
style := t.Theme.InstrumentEditor.UnitComment.AsLabelStyle()
|
||||
label := Label(t.Theme, &style, comment)
|
||||
@ -530,16 +530,9 @@ func (pe *InstrumentEditor) layoutFooter(gtx C) D {
|
||||
|
||||
func (pe *InstrumentEditor) layoutUnitTypeChooser(gtx C) D {
|
||||
t := TrackerFromContext(gtx)
|
||||
var namesArray [256]string
|
||||
names := namesArray[:0]
|
||||
for _, item := range t.Model.SearchResults().Iterate {
|
||||
names = append(names, item)
|
||||
}
|
||||
element := func(gtx C, i int) D {
|
||||
if i < 0 || i >= len(names) {
|
||||
return D{}
|
||||
}
|
||||
w := Label(t.Theme, &t.Theme.UnitEditor.Chooser, names[i])
|
||||
name, _ := t.SearchResult(i)
|
||||
w := Label(t.Theme, &t.Theme.UnitEditor.Chooser, name)
|
||||
if i == pe.searchList.TrackerList.Selected() {
|
||||
return pe.SelectTypeBtn.Layout(gtx, w.Layout)
|
||||
}
|
||||
|
||||
@ -289,7 +289,7 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
instr := t.Model.Instruments().List().Selected()
|
||||
instr := t.Model.Instruments().Selected()
|
||||
n := noteAsValue(t.Model.Octave().Value(), val-12)
|
||||
t.KeyNoteMap.Press(e.Name, tracker.NoteEvent{Channel: instr, Note: n})
|
||||
}
|
||||
|
||||
@ -93,8 +93,8 @@ func NewNoteEditor(model *tracker.Model) *NoteEditor {
|
||||
TrackMidiInBtn: new(Clickable),
|
||||
scrollTable: NewScrollTable(
|
||||
model.Notes().Table(),
|
||||
model.Tracks().List(),
|
||||
model.NoteRows().List(),
|
||||
model.Tracks(),
|
||||
model.NoteRows(),
|
||||
),
|
||||
}
|
||||
for k, a := range keyBindingMap {
|
||||
|
||||
@ -42,8 +42,8 @@ func NewOrderEditor(m *tracker.Model) *OrderEditor {
|
||||
return &OrderEditor{
|
||||
scrollTable: NewScrollTable(
|
||||
m.Order().Table(),
|
||||
m.Tracks().List(),
|
||||
m.OrderRows().List(),
|
||||
m.Tracks(),
|
||||
m.OrderRows(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ func (it *InstrumentTools) Layout(gtx C) D {
|
||||
|
||||
func (it *InstrumentTools) update(gtx C, tr *Tracker) {
|
||||
for it.copyInstrumentBtn.Clicked(gtx) {
|
||||
if contents, ok := tr.Instruments().List().CopyElements(); ok {
|
||||
if contents, ok := tr.Instruments().CopyElements(); ok {
|
||||
gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(bytes.NewReader(contents))})
|
||||
tr.Alerts().Add("Instrument copied to clipboard", tracker.Info)
|
||||
}
|
||||
@ -208,7 +208,7 @@ func (it *InstrumentTools) Tags(level int, yield TagYieldFunc) bool {
|
||||
|
||||
func MakeInstrList(model *tracker.Model) InstrumentList {
|
||||
return InstrumentList{
|
||||
instrumentDragList: NewDragList(model.Instruments().List(), layout.Horizontal),
|
||||
instrumentDragList: NewDragList(model.Instruments(), layout.Horizontal),
|
||||
nameEditor: NewEditor(true, true, text.Middle),
|
||||
}
|
||||
}
|
||||
@ -221,7 +221,7 @@ func (il *InstrumentList) Layout(gtx C) D {
|
||||
element := func(gtx C, i int) D {
|
||||
grabhandle := Label(t.Theme, &t.Theme.InstrumentEditor.InstrumentList.Number, strconv.Itoa(i+1))
|
||||
label := func(gtx C) D {
|
||||
name, level, mute, ok := (*tracker.Instruments)(t.Model).Item(i)
|
||||
name, level, mute, ok := t.Instrument(i)
|
||||
if !ok {
|
||||
labelStyle := Label(t.Theme, &t.Theme.InstrumentEditor.InstrumentList.Number, "")
|
||||
return layout.Center.Layout(gtx, labelStyle.Layout)
|
||||
|
||||
428
tracker/list.go
428
tracker/list.go
@ -6,7 +6,6 @@ import (
|
||||
"iter"
|
||||
"math"
|
||||
"math/bits"
|
||||
"strings"
|
||||
|
||||
"github.com/vsariola/sointu"
|
||||
"github.com/vsariola/sointu/vm"
|
||||
@ -15,7 +14,7 @@ import (
|
||||
|
||||
type (
|
||||
List struct {
|
||||
ListData
|
||||
data ListData
|
||||
}
|
||||
|
||||
ListData interface {
|
||||
@ -27,49 +26,32 @@ type (
|
||||
}
|
||||
|
||||
MutableListData interface {
|
||||
change(kind string, severity ChangeSeverity) func()
|
||||
cancel()
|
||||
move(r Range, delta int) (ok bool)
|
||||
delete(r Range) (ok bool)
|
||||
marshal(r Range) ([]byte, error)
|
||||
unmarshal([]byte) (r Range, err error)
|
||||
}
|
||||
|
||||
UnitListItem struct {
|
||||
Type, Comment string
|
||||
Disabled bool
|
||||
Signals Rail
|
||||
Change(kind string, severity ChangeSeverity) func()
|
||||
Cancel()
|
||||
Move(r Range, delta int) (ok bool)
|
||||
Delete(r Range) (ok bool)
|
||||
Marshal(r Range) ([]byte, error)
|
||||
Unmarshal([]byte) (r Range, err error)
|
||||
}
|
||||
|
||||
// Range is used to represent a range [Start,End) of integers
|
||||
Range struct {
|
||||
Start, End int
|
||||
}
|
||||
|
||||
UnitYieldFunc func(index int, item UnitListItem) (ok bool)
|
||||
UnitSearchYieldFunc func(index int, item string) (ok bool)
|
||||
|
||||
Instruments Model // Instruments is a list of instruments, implementing ListData & MutableListData interfaces
|
||||
Units Model // Units is a list of all the units in the selected instrument, implementing ListData & MutableListData interfaces
|
||||
Tracks Model // Tracks is a list of all the tracks, implementing ListData & MutableListData interfaces
|
||||
OrderRows Model // OrderRows is a list of all the order rows, implementing ListData & MutableListData interfaces
|
||||
NoteRows Model // NoteRows is a list of all the note rows, implementing ListData & MutableListData interfaces
|
||||
SearchResults Model // SearchResults is a unmutable list of all the search results, implementing ListData interface
|
||||
)
|
||||
|
||||
// Model methods
|
||||
func MakeList(data ListData) List { return List{data} }
|
||||
|
||||
func (m *Model) Instruments() *Instruments { return (*Instruments)(m) }
|
||||
func (m *Model) Units() *Units { return (*Units)(m) }
|
||||
func (m *Model) Tracks() *Tracks { return (*Tracks)(m) }
|
||||
func (m *Model) OrderRows() *OrderRows { return (*OrderRows)(m) }
|
||||
func (m *Model) NoteRows() *NoteRows { return (*NoteRows)(m) }
|
||||
func (m *Model) SearchResults() *SearchResults { return (*SearchResults)(m) }
|
||||
func (l List) Selected() int { return max(min(l.data.Selected(), l.data.Count()-1), 0) }
|
||||
func (l List) Selected2() int { return max(min(l.data.Selected2(), l.data.Count()-1), 0) }
|
||||
func (l List) SetSelected(value int) { l.data.SetSelected(max(min(value, l.data.Count()-1), 0)) }
|
||||
func (l List) SetSelected2(value int) { l.data.SetSelected2(max(min(value, l.data.Count()-1), 0)) }
|
||||
func (l List) Count() int { return l.data.Count() }
|
||||
|
||||
// MoveElements moves the selected elements in a list by delta. The list must
|
||||
// implement the MutableListData interface.
|
||||
func (v List) MoveElements(delta int) bool {
|
||||
s, ok := v.ListData.(MutableListData)
|
||||
s, ok := v.data.(MutableListData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@ -77,9 +59,9 @@ func (v List) MoveElements(delta int) bool {
|
||||
if delta == 0 || r.Start+delta < 0 || r.End+delta > v.Count() {
|
||||
return false
|
||||
}
|
||||
defer s.change("MoveElements", MajorChange)()
|
||||
if !s.move(r, delta) {
|
||||
s.cancel()
|
||||
defer s.Change("MoveElements", MajorChange)()
|
||||
if !s.Move(r, delta) {
|
||||
s.Cancel()
|
||||
return false
|
||||
}
|
||||
v.SetSelected(v.Selected() + delta)
|
||||
@ -90,7 +72,7 @@ func (v List) MoveElements(delta int) bool {
|
||||
// DeleteElements deletes the selected elements in a list. The list must
|
||||
// implement the MutableListData interface.
|
||||
func (v List) DeleteElements(backwards bool) bool {
|
||||
d, ok := v.ListData.(MutableListData)
|
||||
d, ok := v.data.(MutableListData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@ -98,9 +80,9 @@ func (v List) DeleteElements(backwards bool) bool {
|
||||
if r.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
defer d.change("DeleteElements", MajorChange)()
|
||||
if !d.delete(r) {
|
||||
d.cancel()
|
||||
defer d.Change("DeleteElements", MajorChange)()
|
||||
if !d.Delete(r) {
|
||||
d.Cancel()
|
||||
return false
|
||||
}
|
||||
if backwards && r.Start > 0 {
|
||||
@ -115,7 +97,7 @@ func (v List) DeleteElements(backwards bool) bool {
|
||||
// the MutableListData interface. Returns the copied data, marshaled into byte
|
||||
// slice, and true if successful.
|
||||
func (v List) CopyElements() ([]byte, bool) {
|
||||
m, ok := v.ListData.(MutableListData)
|
||||
m, ok := v.data.(MutableListData)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
@ -123,7 +105,7 @@ func (v List) CopyElements() ([]byte, bool) {
|
||||
if r.Len() == 0 {
|
||||
return nil, false
|
||||
}
|
||||
ret, err := m.marshal(r)
|
||||
ret, err := m.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
@ -134,14 +116,14 @@ func (v List) CopyElements() ([]byte, bool) {
|
||||
// byte slice. The list must implement the MutableListData interface. Returns
|
||||
// true if successful.
|
||||
func (v List) PasteElements(data []byte) (ok bool) {
|
||||
m, ok := v.ListData.(MutableListData)
|
||||
m, ok := v.data.(MutableListData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
defer m.change("PasteElements", MajorChange)()
|
||||
r, err := m.unmarshal(data)
|
||||
defer m.Change("PasteElements", MajorChange)()
|
||||
r, err := m.Unmarshal(data)
|
||||
if err != nil {
|
||||
m.cancel()
|
||||
m.Cancel()
|
||||
return false
|
||||
}
|
||||
v.SetSelected(r.Start)
|
||||
@ -149,19 +131,23 @@ func (v List) PasteElements(data []byte) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v List) Mutable() bool {
|
||||
_, ok := v.data.(MutableListData)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v *List) listRange() (r Range) {
|
||||
r.Start = max(min(v.Selected(), v.Selected2()), 0)
|
||||
r.End = min(max(v.Selected(), v.Selected2())+1, v.Count())
|
||||
return
|
||||
}
|
||||
|
||||
// Instruments methods
|
||||
// instruments is a list of instruments, implementing ListData & MutableListData interfaces
|
||||
type instruments Model
|
||||
|
||||
func (v *Instruments) List() List {
|
||||
return List{v}
|
||||
}
|
||||
func (m *Model) Instruments() List { return List{(*instruments)(m)} }
|
||||
|
||||
func (v *Instruments) Item(i int) (name string, maxLevel float32, mute bool, ok bool) {
|
||||
func (v *Model) Instrument(i int) (name string, maxLevel float32, mute bool, ok bool) {
|
||||
if i < 0 || i >= len(v.d.Song.Patch) {
|
||||
return "", 0, false, false
|
||||
}
|
||||
@ -182,37 +168,20 @@ func (v *Instruments) Item(i int) (name string, maxLevel float32, mute bool, ok
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
func (v *Instruments) FirstID(i int) (id int, ok bool) {
|
||||
if i < 0 || i >= len(v.d.Song.Patch) {
|
||||
return 0, false
|
||||
}
|
||||
if len(v.d.Song.Patch[i].Units) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return v.d.Song.Patch[i].Units[0].ID, true
|
||||
}
|
||||
|
||||
func (v *Instruments) Selected() int {
|
||||
return max(min(v.d.InstrIndex, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Instruments) Selected2() int {
|
||||
return max(min(v.d.InstrIndex2, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Instruments) SetSelected(value int) {
|
||||
v.d.InstrIndex = max(min(value, v.Count()-1), 0)
|
||||
func (v *instruments) Count() int { return len(v.d.Song.Patch) }
|
||||
func (v *instruments) Selected() int { return v.d.InstrIndex }
|
||||
func (v *instruments) Selected2() int { return v.d.InstrIndex2 }
|
||||
func (v *instruments) SetSelected2(value int) { v.d.InstrIndex2 = value }
|
||||
func (v *instruments) SetSelected(value int) {
|
||||
v.d.InstrIndex = value
|
||||
v.d.UnitIndex = 0
|
||||
v.d.UnitIndex2 = 0
|
||||
v.d.UnitSearching = false
|
||||
v.d.UnitSearchString = ""
|
||||
}
|
||||
|
||||
func (v *Instruments) SetSelected2(value int) {
|
||||
v.d.InstrIndex2 = max(min(value, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Instruments) move(r Range, delta int) (ok bool) {
|
||||
func (v *instruments) Move(r Range, delta int) (ok bool) {
|
||||
voiceDelta := 0
|
||||
if delta < 0 {
|
||||
voiceDelta = -VoiceRange(v.d.Song.Patch, Range{r.Start + delta, r.Start}).Len()
|
||||
@ -226,28 +195,24 @@ func (v *Instruments) move(r Range, delta int) (ok bool) {
|
||||
return (*Model)(v).sliceInstrumentsTracks(true, v.linkInstrTrack, ranges[:]...)
|
||||
}
|
||||
|
||||
func (v *Instruments) delete(r Range) (ok bool) {
|
||||
func (v *instruments) Delete(r Range) (ok bool) {
|
||||
ranges := Complement(VoiceRange(v.d.Song.Patch, r))
|
||||
return (*Model)(v).sliceInstrumentsTracks(true, v.linkInstrTrack, ranges[:]...)
|
||||
}
|
||||
|
||||
func (v *Instruments) change(n string, severity ChangeSeverity) func() {
|
||||
func (v *instruments) Change(n string, severity ChangeSeverity) func() {
|
||||
return (*Model)(v).change("Instruments."+n, SongChange, severity)
|
||||
}
|
||||
|
||||
func (v *Instruments) cancel() {
|
||||
func (v *instruments) Cancel() {
|
||||
v.changeCancel = true
|
||||
}
|
||||
|
||||
func (v *Instruments) Count() int {
|
||||
return len(v.d.Song.Patch)
|
||||
}
|
||||
|
||||
func (v *Instruments) marshal(r Range) ([]byte, error) {
|
||||
func (v *instruments) Marshal(r Range) ([]byte, error) {
|
||||
return (*Model)(v).marshalVoices(VoiceRange(v.d.Song.Patch, r))
|
||||
}
|
||||
|
||||
func (m *Instruments) unmarshal(data []byte) (r Range, err error) {
|
||||
func (m *instruments) Unmarshal(data []byte) (r Range, err error) {
|
||||
voiceIndex := m.d.Song.Patch.FirstVoiceForInstrument(m.d.InstrIndex)
|
||||
r, _, ok := (*Model)(m).unmarshalVoices(voiceIndex, data, true, m.linkInstrTrack)
|
||||
if !ok {
|
||||
@ -256,13 +221,37 @@ func (m *Instruments) unmarshal(data []byte) (r Range, err error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Units methods
|
||||
// units is a list of all the units in the selected instrument, implementing ListData & MutableListData interfaces
|
||||
type (
|
||||
units Model
|
||||
UnitListItem struct {
|
||||
Type, Comment string
|
||||
Disabled bool
|
||||
Signals Rail
|
||||
}
|
||||
)
|
||||
|
||||
func (v *Units) List() List {
|
||||
return List{v}
|
||||
func (m *Model) Units() List { return List{(*units)(m)} }
|
||||
|
||||
func (v *Model) Unit(index int) UnitListItem {
|
||||
i := v.d.InstrIndex
|
||||
if i < 0 || i >= len(v.d.Song.Patch) || index < 0 || index >= (*units)(v).Count() {
|
||||
return UnitListItem{}
|
||||
}
|
||||
unit := v.d.Song.Patch[v.d.InstrIndex].Units[index]
|
||||
signals := Rail{}
|
||||
if i >= 0 && i < len(v.derived.patch) && index >= 0 && index < len(v.derived.patch[i].rails) {
|
||||
signals = v.derived.patch[i].rails[index]
|
||||
}
|
||||
return UnitListItem{
|
||||
Type: unit.Type,
|
||||
Comment: unit.Comment,
|
||||
Disabled: unit.Disabled,
|
||||
Signals: signals,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Units) SelectedType() string {
|
||||
func (m *Model) SelectedUnitType() string {
|
||||
if m.d.InstrIndex < 0 ||
|
||||
m.d.InstrIndex >= len(m.d.Song.Patch) ||
|
||||
m.d.UnitIndex < 0 ||
|
||||
@ -272,7 +261,7 @@ func (m *Units) SelectedType() string {
|
||||
return m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Type
|
||||
}
|
||||
|
||||
func (m *Units) SetSelectedType(t string) {
|
||||
func (m *Model) SetSelectedUnitType(t string) {
|
||||
if m.d.InstrIndex < 0 ||
|
||||
m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return
|
||||
@ -293,58 +282,28 @@ func (m *Units) SetSelectedType(t string) {
|
||||
if oldUnit.Type == unit.Type {
|
||||
return
|
||||
}
|
||||
defer m.change("SetSelectedType", MajorChange)()
|
||||
defer (*units)(m).Change("SetSelectedType", MajorChange)()
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex] = unit
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].ID = oldUnit.ID // keep the ID of the replaced unit
|
||||
}
|
||||
|
||||
func (v *Units) Item(index int) UnitListItem {
|
||||
i := v.d.InstrIndex
|
||||
if i < 0 || i >= len(v.d.Song.Patch) || index < 0 || index >= v.Count() {
|
||||
return UnitListItem{}
|
||||
}
|
||||
unit := v.d.Song.Patch[v.d.InstrIndex].Units[index]
|
||||
signals := Rail{}
|
||||
if i >= 0 && i < len(v.derived.patch) && index >= 0 && index < len(v.derived.patch[i].rails) {
|
||||
signals = v.derived.patch[i].rails[index]
|
||||
}
|
||||
return UnitListItem{
|
||||
Type: unit.Type,
|
||||
Comment: unit.Comment,
|
||||
Disabled: unit.Disabled,
|
||||
Signals: signals,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Units) Selected() int {
|
||||
return max(min(v.d.UnitIndex, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Units) Selected2() int {
|
||||
return max(min(v.d.UnitIndex2, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Units) SetSelected(value int) {
|
||||
m := (*Model)(v)
|
||||
m.d.UnitIndex = max(min(value, v.Count()-1), 0)
|
||||
func (v *units) Selected() int { return v.d.UnitIndex }
|
||||
func (v *units) Selected2() int { return v.d.UnitIndex2 }
|
||||
func (v *units) SetSelected2(value int) { v.d.UnitIndex2 = value }
|
||||
func (m *units) SetSelected(value int) {
|
||||
m.d.UnitIndex = value
|
||||
m.d.ParamIndex = 0
|
||||
m.d.UnitSearching = false
|
||||
m.d.UnitSearchString = ""
|
||||
}
|
||||
|
||||
func (v *Units) SetSelected2(value int) {
|
||||
(*Model)(v).d.UnitIndex2 = max(min(value, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Units) Count() int {
|
||||
m := (*Model)(v)
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
func (v *units) Count() int {
|
||||
if v.d.InstrIndex < 0 || v.d.InstrIndex >= len(v.d.Song.Patch) {
|
||||
return 0
|
||||
}
|
||||
return len(m.d.Song.Patch[(*Model)(v).d.InstrIndex].Units)
|
||||
return len(v.d.Song.Patch[v.d.InstrIndex].Units)
|
||||
}
|
||||
|
||||
func (v *Units) move(r Range, delta int) (ok bool) {
|
||||
func (v *units) Move(r Range, delta int) (ok bool) {
|
||||
m := (*Model)(v)
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return false
|
||||
@ -356,7 +315,7 @@ func (v *Units) move(r Range, delta int) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Units) delete(r Range) (ok bool) {
|
||||
func (v *units) Delete(r Range) (ok bool) {
|
||||
m := (*Model)(v)
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return false
|
||||
@ -366,15 +325,15 @@ func (v *Units) delete(r Range) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Units) change(n string, severity ChangeSeverity) func() {
|
||||
func (v *units) Change(n string, severity ChangeSeverity) func() {
|
||||
return (*Model)(v).change("UnitListView."+n, PatchChange, severity)
|
||||
}
|
||||
|
||||
func (v *Units) cancel() {
|
||||
func (v *units) Cancel() {
|
||||
(*Model)(v).changeCancel = true
|
||||
}
|
||||
|
||||
func (v *Units) marshal(r Range) ([]byte, error) {
|
||||
func (v *units) Marshal(r Range) ([]byte, error) {
|
||||
m := (*Model)(v)
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return nil, errors.New("UnitListView.marshal: no instruments")
|
||||
@ -387,7 +346,7 @@ func (v *Units) marshal(r Range) ([]byte, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (v *Units) unmarshal(data []byte) (r Range, err error) {
|
||||
func (v *units) Unmarshal(data []byte) (r Range, err error) {
|
||||
m := (*Model)(v)
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return Range{}, errors.New("UnitListView.unmarshal: no instruments")
|
||||
@ -409,29 +368,18 @@ func (v *Units) unmarshal(data []byte) (r Range, err error) {
|
||||
return Range{sel, sel + len(pastedUnits.Units)}, nil
|
||||
}
|
||||
|
||||
// Tracks methods
|
||||
// tracks is a list of all the tracks, implementing ListData & MutableListData interfaces
|
||||
type tracks Model
|
||||
|
||||
func (v *Tracks) List() List {
|
||||
return List{v}
|
||||
}
|
||||
func (m *Model) Tracks() List { return List{(*tracks)(m)} }
|
||||
|
||||
func (v *Tracks) Selected() int {
|
||||
return max(min(v.d.Cursor.Track, v.Count()-1), 0)
|
||||
}
|
||||
func (v *tracks) Selected() int { return v.d.Cursor.Track }
|
||||
func (v *tracks) Selected2() int { return v.d.Cursor2.Track }
|
||||
func (v *tracks) SetSelected(value int) { v.d.Cursor.Track = value }
|
||||
func (v *tracks) SetSelected2(value int) { v.d.Cursor2.Track = value }
|
||||
func (v *tracks) Count() int { return len((*Model)(v).d.Song.Score.Tracks) }
|
||||
|
||||
func (v *Tracks) Selected2() int {
|
||||
return max(min(v.d.Cursor2.Track, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Tracks) SetSelected(value int) {
|
||||
v.d.Cursor.Track = max(min(value, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Tracks) SetSelected2(value int) {
|
||||
v.d.Cursor2.Track = max(min(value, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *Tracks) move(r Range, delta int) (ok bool) {
|
||||
func (v *tracks) Move(r Range, delta int) (ok bool) {
|
||||
voiceDelta := 0
|
||||
if delta < 0 {
|
||||
voiceDelta = -VoiceRange(v.d.Song.Score.Tracks, Range{r.Start + delta, r.Start}).Len()
|
||||
@ -445,28 +393,24 @@ func (v *Tracks) move(r Range, delta int) (ok bool) {
|
||||
return (*Model)(v).sliceInstrumentsTracks(v.linkInstrTrack, true, ranges[:]...)
|
||||
}
|
||||
|
||||
func (v *Tracks) delete(r Range) (ok bool) {
|
||||
func (v *tracks) Delete(r Range) (ok bool) {
|
||||
ranges := Complement(VoiceRange(v.d.Song.Score.Tracks, r))
|
||||
return (*Model)(v).sliceInstrumentsTracks(v.linkInstrTrack, true, ranges[:]...)
|
||||
}
|
||||
|
||||
func (v *Tracks) change(n string, severity ChangeSeverity) func() {
|
||||
func (v *tracks) Change(n string, severity ChangeSeverity) func() {
|
||||
return (*Model)(v).change("TrackList."+n, SongChange, severity)
|
||||
}
|
||||
|
||||
func (v *Tracks) cancel() {
|
||||
func (v *tracks) Cancel() {
|
||||
v.changeCancel = true
|
||||
}
|
||||
|
||||
func (v *Tracks) Count() int {
|
||||
return len((*Model)(v).d.Song.Score.Tracks)
|
||||
}
|
||||
|
||||
func (v *Tracks) marshal(r Range) ([]byte, error) {
|
||||
func (v *tracks) Marshal(r Range) ([]byte, error) {
|
||||
return (*Model)(v).marshalVoices(VoiceRange(v.d.Song.Score.Tracks, r))
|
||||
}
|
||||
|
||||
func (m *Tracks) unmarshal(data []byte) (r Range, err error) {
|
||||
func (m *tracks) Unmarshal(data []byte) (r Range, err error) {
|
||||
voiceIndex := m.d.Song.Score.FirstVoiceForTrack(m.d.Cursor.Track)
|
||||
_, r, ok := (*Model)(m).unmarshalVoices(voiceIndex, data, m.linkInstrTrack, true)
|
||||
if !ok {
|
||||
@ -475,37 +419,23 @@ func (m *Tracks) unmarshal(data []byte) (r Range, err error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// OrderRows methods
|
||||
// orderRows is a list of all the order rows, implementing ListData & MutableListData interfaces
|
||||
type orderRows Model
|
||||
|
||||
func (v *OrderRows) List() List {
|
||||
return List{v}
|
||||
}
|
||||
func (m *Model) OrderRows() List { return List{(*orderRows)(m)} }
|
||||
|
||||
func (v *OrderRows) Selected() int {
|
||||
p := v.d.Cursor.OrderRow
|
||||
p = max(min(p, v.Count()-1), 0)
|
||||
return p
|
||||
}
|
||||
|
||||
func (v *OrderRows) Selected2() int {
|
||||
p := v.d.Cursor2.OrderRow
|
||||
p = max(min(p, v.Count()-1), 0)
|
||||
return p
|
||||
}
|
||||
|
||||
func (v *OrderRows) SetSelected(value int) {
|
||||
y := max(min(value, v.Count()-1), 0)
|
||||
if y != v.d.Cursor.OrderRow {
|
||||
func (v *orderRows) Count() int { return v.d.Song.Score.Length }
|
||||
func (v *orderRows) Selected() int { return v.d.Cursor.OrderRow }
|
||||
func (v *orderRows) Selected2() int { return v.d.Cursor2.OrderRow }
|
||||
func (v *orderRows) SetSelected2(value int) { v.d.Cursor2.OrderRow = value }
|
||||
func (v *orderRows) SetSelected(value int) {
|
||||
if value != v.d.Cursor.OrderRow {
|
||||
v.follow = false
|
||||
}
|
||||
v.d.Cursor.OrderRow = y
|
||||
v.d.Cursor.OrderRow = value
|
||||
}
|
||||
|
||||
func (v *OrderRows) SetSelected2(value int) {
|
||||
v.d.Cursor2.OrderRow = max(min(value, v.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (v *OrderRows) move(r Range, delta int) (ok bool) {
|
||||
func (v *orderRows) Move(r Range, delta int) (ok bool) {
|
||||
swaps := r.Swaps(delta)
|
||||
for i, t := range v.d.Song.Score.Tracks {
|
||||
for a, b := range swaps {
|
||||
@ -517,7 +447,7 @@ func (v *OrderRows) move(r Range, delta int) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *OrderRows) delete(r Range) (ok bool) {
|
||||
func (v *orderRows) Delete(r Range) (ok bool) {
|
||||
for i, t := range v.d.Song.Score.Tracks {
|
||||
r2 := r.Intersect(Range{0, len(t.Order)})
|
||||
v.d.Song.Score.Tracks[i].Order = append(t.Order[:r2.Start], t.Order[r2.End:]...)
|
||||
@ -525,23 +455,19 @@ func (v *OrderRows) delete(r Range) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *OrderRows) change(n string, severity ChangeSeverity) func() {
|
||||
func (v *orderRows) Change(n string, severity ChangeSeverity) func() {
|
||||
return (*Model)(v).change("OrderRowList."+n, ScoreChange, severity)
|
||||
}
|
||||
|
||||
func (v *OrderRows) cancel() {
|
||||
func (v *orderRows) Cancel() {
|
||||
v.changeCancel = true
|
||||
}
|
||||
|
||||
func (v *OrderRows) Count() int {
|
||||
return v.d.Song.Score.Length
|
||||
}
|
||||
|
||||
type marshalOrderRows struct {
|
||||
Columns [][]int `yaml:",flow"`
|
||||
}
|
||||
|
||||
func (v *OrderRows) marshal(r Range) ([]byte, error) {
|
||||
func (v *orderRows) Marshal(r Range) ([]byte, error) {
|
||||
var table marshalOrderRows
|
||||
for i := range v.d.Song.Score.Tracks {
|
||||
table.Columns = append(table.Columns, make([]int, r.Len()))
|
||||
@ -552,7 +478,7 @@ func (v *OrderRows) marshal(r Range) ([]byte, error) {
|
||||
return yaml.Marshal(table)
|
||||
}
|
||||
|
||||
func (v *OrderRows) unmarshal(data []byte) (r Range, err error) {
|
||||
func (v *orderRows) Unmarshal(data []byte) (r Range, err error) {
|
||||
var table marshalOrderRows
|
||||
err = yaml.Unmarshal(data, &table)
|
||||
if err != nil {
|
||||
@ -581,33 +507,23 @@ func (v *OrderRows) unmarshal(data []byte) (r Range, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// NoteRows methods
|
||||
// noteRows is a list of all the note rows, implementing ListData & MutableListData interfaces
|
||||
type noteRows Model
|
||||
|
||||
func (v *NoteRows) List() List {
|
||||
return List{v}
|
||||
}
|
||||
func (m *Model) NoteRows() List { return List{(*noteRows)(m)} }
|
||||
|
||||
func (v *NoteRows) Selected() int {
|
||||
return v.d.Song.Score.SongRow(v.d.Song.Score.Clamp(v.d.Cursor.SongPos))
|
||||
}
|
||||
|
||||
func (v *NoteRows) Selected2() int {
|
||||
return v.d.Song.Score.SongRow(v.d.Song.Score.Clamp(v.d.Cursor2.SongPos))
|
||||
}
|
||||
|
||||
func (v *NoteRows) SetSelected(value int) {
|
||||
if value != v.d.Song.Score.SongRow(v.d.Cursor.SongPos) {
|
||||
v.follow = false
|
||||
func (n *noteRows) Count() int { return n.d.Song.Score.Length * n.d.Song.Score.RowsPerPattern }
|
||||
func (n *noteRows) Selected() int { return n.d.Song.Score.SongRow(n.d.Cursor.SongPos) }
|
||||
func (n *noteRows) Selected2() int { return n.d.Song.Score.SongRow(n.d.Cursor2.SongPos) }
|
||||
func (n *noteRows) SetSelected2(v int) { n.d.Cursor2.SongPos = n.d.Song.Score.SongPos(v) }
|
||||
func (n *noteRows) SetSelected(value int) {
|
||||
if value != n.d.Song.Score.SongRow(n.d.Cursor.SongPos) {
|
||||
n.follow = false
|
||||
}
|
||||
v.d.Cursor.SongPos = v.d.Song.Score.Clamp(v.d.Song.Score.SongPos(value))
|
||||
n.d.Cursor.SongPos = n.d.Song.Score.Clamp(n.d.Song.Score.SongPos(value))
|
||||
}
|
||||
|
||||
func (v *NoteRows) SetSelected2(value int) {
|
||||
v.d.Cursor2.SongPos = v.d.Song.Score.Clamp(v.d.Song.Score.SongPos(value))
|
||||
|
||||
}
|
||||
|
||||
func (v *NoteRows) move(r Range, delta int) (ok bool) {
|
||||
func (v *noteRows) Move(r Range, delta int) (ok bool) {
|
||||
for a, b := range r.Swaps(delta) {
|
||||
apos := v.d.Song.Score.SongPos(a)
|
||||
bpos := v.d.Song.Score.SongPos(b)
|
||||
@ -621,7 +537,7 @@ func (v *NoteRows) move(r Range, delta int) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *NoteRows) delete(r Range) (ok bool) {
|
||||
func (v *noteRows) Delete(r Range) (ok bool) {
|
||||
for _, track := range v.d.Song.Score.Tracks {
|
||||
for i := r.Start; i < r.End; i++ {
|
||||
pos := v.d.Song.Score.SongPos(i)
|
||||
@ -631,23 +547,19 @@ func (v *NoteRows) delete(r Range) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *NoteRows) change(n string, severity ChangeSeverity) func() {
|
||||
func (v *noteRows) Change(n string, severity ChangeSeverity) func() {
|
||||
return (*Model)(v).change("NoteRowList."+n, ScoreChange, severity)
|
||||
}
|
||||
|
||||
func (v *NoteRows) cancel() {
|
||||
func (v *noteRows) Cancel() {
|
||||
(*Model)(v).changeCancel = true
|
||||
}
|
||||
|
||||
func (v *NoteRows) Count() int {
|
||||
return (*Model)(v).d.Song.Score.Length * v.d.Song.Score.RowsPerPattern
|
||||
}
|
||||
|
||||
type marshalNoteRows struct {
|
||||
NoteRows [][]byte `yaml:",flow"`
|
||||
}
|
||||
|
||||
func (v *NoteRows) marshal(r Range) ([]byte, error) {
|
||||
func (v *noteRows) Marshal(r Range) ([]byte, error) {
|
||||
var table marshalNoteRows
|
||||
for i, track := range v.d.Song.Score.Tracks {
|
||||
table.NoteRows = append(table.NoteRows, make([]byte, r.Len()))
|
||||
@ -660,7 +572,7 @@ func (v *NoteRows) marshal(r Range) ([]byte, error) {
|
||||
return yaml.Marshal(table)
|
||||
}
|
||||
|
||||
func (v *NoteRows) unmarshal(data []byte) (r Range, err error) {
|
||||
func (v *noteRows) Unmarshal(data []byte) (r Range, err error) {
|
||||
var table marshalNoteRows
|
||||
if err := yaml.Unmarshal(data, &table); err != nil {
|
||||
return Range{}, fmt.Errorf("NoteRowList.unmarshal: %v", err)
|
||||
@ -683,57 +595,25 @@ func (v *NoteRows) unmarshal(data []byte) (r Range, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// SearchResults
|
||||
// searchResults is a unmutable list of all the search results, implementing ListData interface
|
||||
type (
|
||||
searchResults Model
|
||||
UnitSearchYieldFunc func(index int, item string) (ok bool)
|
||||
)
|
||||
|
||||
func (v *SearchResults) List() List {
|
||||
return List{v}
|
||||
}
|
||||
|
||||
func (l *SearchResults) Iterate(yield UnitSearchYieldFunc) {
|
||||
index := 0
|
||||
for _, name := range sointu.UnitNames {
|
||||
if !strings.HasPrefix(name, l.d.UnitSearchString) {
|
||||
continue
|
||||
}
|
||||
if !yield(index, name) {
|
||||
break
|
||||
}
|
||||
index++
|
||||
func (m *Model) SearchResults() List { return List{(*searchResults)(m)} }
|
||||
func (l *Model) SearchResult(i int) (name string, ok bool) {
|
||||
if i < 0 || i >= len(l.derived.searchResults) {
|
||||
return "", false
|
||||
}
|
||||
return l.derived.searchResults[i], true
|
||||
}
|
||||
|
||||
func (l *SearchResults) Item(index int) (name string, ok bool) {
|
||||
for i, n := range l.Iterate {
|
||||
if i == index {
|
||||
return n, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (l *SearchResults) Selected() int {
|
||||
return max(min(l.d.UnitSearchIndex, l.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (l *SearchResults) Selected2() int {
|
||||
return max(min(l.d.UnitSearchIndex, l.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (l *SearchResults) SetSelected(value int) {
|
||||
l.d.UnitSearchIndex = max(min(value, l.Count()-1), 0)
|
||||
}
|
||||
|
||||
func (l *SearchResults) SetSelected2(value int) {
|
||||
}
|
||||
|
||||
func (l *SearchResults) Count() (count int) {
|
||||
for _, n := range sointu.UnitNames {
|
||||
if strings.HasPrefix(n, l.d.UnitSearchString) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (l *searchResults) Selected() int { return l.d.UnitSearchIndex }
|
||||
func (l *searchResults) Selected2() int { return l.d.UnitSearchIndex }
|
||||
func (l *searchResults) SetSelected(value int) { l.d.UnitSearchIndex = value }
|
||||
func (l *searchResults) SetSelected2(value int) {}
|
||||
func (l *searchResults) Count() (count int) { return len(l.derived.searchResults) }
|
||||
|
||||
func (r Range) Len() int { return r.End - r.Start }
|
||||
|
||||
|
||||
@ -216,6 +216,8 @@ func NewModel(broker *Broker, synthers []sointu.Synther, midiContext MIDIContext
|
||||
m.updateDeriveData(SongChange)
|
||||
m.presets.load()
|
||||
m.updateDerivedPresetSearch()
|
||||
m.derived.searchResults = make([]string, 0, len(sointu.UnitNames))
|
||||
m.updateDerivedUnitSearch()
|
||||
return m
|
||||
}
|
||||
|
||||
|
||||
@ -44,12 +44,12 @@ func (s *modelFuzzState) Iterate(yield func(string, func(p string, t *testing.T)
|
||||
s.IterateInt("Step", s.model.Step(), yield, seed)
|
||||
s.IterateInt("Octave", s.model.Octave(), yield, seed)
|
||||
// Lists
|
||||
s.IterateList("Instruments", s.model.Instruments().List(), yield, seed)
|
||||
s.IterateList("Units", s.model.Units().List(), yield, seed)
|
||||
s.IterateList("Tracks", s.model.Tracks().List(), yield, seed)
|
||||
s.IterateList("OrderRows", s.model.OrderRows().List(), yield, seed)
|
||||
s.IterateList("NoteRows", s.model.NoteRows().List(), yield, seed)
|
||||
s.IterateList("UnitSearchResults", s.model.SearchResults().List(), yield, seed)
|
||||
s.IterateList("Instruments", s.model.Instruments(), yield, seed)
|
||||
s.IterateList("Units", s.model.Units(), yield, seed)
|
||||
s.IterateList("Tracks", s.model.Tracks(), yield, seed)
|
||||
s.IterateList("OrderRows", s.model.OrderRows(), yield, seed)
|
||||
s.IterateList("NoteRows", s.model.NoteRows(), yield, seed)
|
||||
s.IterateList("UnitSearchResults", s.model.SearchResults(), yield, seed)
|
||||
s.IterateList("PresetDirs", s.model.PresetDirList().List(), yield, seed)
|
||||
s.IterateList("PresetResults", s.model.PresetResultList().List(), yield, seed)
|
||||
// Bools
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vsariola/sointu"
|
||||
)
|
||||
|
||||
type (
|
||||
String struct {
|
||||
value StringValue
|
||||
@ -67,8 +73,18 @@ func (v *UnitSearch) Value() string {
|
||||
func (v *UnitSearch) SetValue(value string) bool {
|
||||
v.d.UnitSearchString = value
|
||||
v.d.UnitSearching = true
|
||||
(*Model)(v).updateDerivedUnitSearch()
|
||||
return true
|
||||
}
|
||||
func (v *Model) updateDerivedUnitSearch() {
|
||||
// update search results based on current search string
|
||||
v.derived.searchResults = v.derived.searchResults[:0]
|
||||
for _, name := range sointu.UnitNames {
|
||||
if strings.HasPrefix(name, v.UnitSearch().Value()) {
|
||||
v.derived.searchResults = append(v.derived.searchResults, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InstrumentNameString
|
||||
|
||||
|
||||
Reference in New Issue
Block a user