mirror of
https://github.com/vsariola/sointu.git
synced 2026-02-12 19:23:12 -05:00
refactor(tracker): group Model methods, with each group in one source file
This commit is contained in:
parent
b93304adab
commit
86ca3fb300
387
tracker/unit.go
Normal file
387
tracker/unit.go
Normal file
@ -0,0 +1,387 @@
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/vsariola/sointu"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Unit returns the Unit view of the model, containing methods to manipulate the
|
||||
// units.
|
||||
func (m *Model) Unit() *UnitModel { return (*UnitModel)(m) }
|
||||
|
||||
type UnitModel Model
|
||||
|
||||
// Add returns an Action to add a new unit. If the before parameter is true,
|
||||
// then the new unit is added before the currently selected unit; otherwise,
|
||||
// after.
|
||||
func (m *UnitModel) Add(before bool) Action {
|
||||
return MakeAction(addUnit{Before: before, Model: (*Model)(m)})
|
||||
}
|
||||
|
||||
type addUnit struct {
|
||||
Before bool
|
||||
*Model
|
||||
}
|
||||
|
||||
func (a addUnit) Do() {
|
||||
m := (*Model)(a.Model)
|
||||
defer m.change("AddUnitAction", PatchChange, MajorChange)()
|
||||
if len(m.d.Song.Patch) == 0 { // no instruments, add one
|
||||
instr := sointu.Instrument{NumVoices: 1}
|
||||
instr.Units = make([]sointu.Unit, 0, 1)
|
||||
m.d.Song.Patch = append(m.d.Song.Patch, instr)
|
||||
m.d.UnitIndex = 0
|
||||
} else {
|
||||
if !a.Before {
|
||||
m.d.UnitIndex++
|
||||
}
|
||||
}
|
||||
m.d.InstrIndex = max(min(m.d.InstrIndex, len(m.d.Song.Patch)-1), 0)
|
||||
instr := m.d.Song.Patch[m.d.InstrIndex]
|
||||
newUnits := make([]sointu.Unit, len(instr.Units)+1)
|
||||
m.d.UnitIndex = clamp(m.d.UnitIndex, 0, len(newUnits)-1)
|
||||
m.d.UnitIndex2 = m.d.UnitIndex
|
||||
copy(newUnits, instr.Units[:m.d.UnitIndex])
|
||||
copy(newUnits[m.d.UnitIndex+1:], instr.Units[m.d.UnitIndex:])
|
||||
m.assignUnitIDs(newUnits[m.d.UnitIndex : m.d.UnitIndex+1])
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units = newUnits
|
||||
m.d.ParamIndex = 0
|
||||
}
|
||||
|
||||
// Delete returns an Action to delete the currently selected unit(s).
|
||||
func (m *UnitModel) Delete() Action { return MakeAction((*deleteUnit)(m)) }
|
||||
|
||||
type deleteUnit UnitModel
|
||||
|
||||
func (m *deleteUnit) Enabled() bool {
|
||||
i := (*Model)(m).d.InstrIndex
|
||||
return i >= 0 && i < len((*Model)(m).d.Song.Patch) && len((*Model)(m).d.Song.Patch[i].Units) > 1
|
||||
}
|
||||
func (m *deleteUnit) Do() {
|
||||
defer (*Model)(m).change("DeleteUnitAction", PatchChange, MajorChange)()
|
||||
(*UnitModel)(m).List().DeleteElements(true)
|
||||
}
|
||||
|
||||
// Clear returns an Action to clear the currently selected unit(s) i.e. they are
|
||||
// set as empty units, but are kept in the unit list.
|
||||
func (m *UnitModel) Clear() Action { return MakeAction((*clearUnit)(m)) }
|
||||
|
||||
type clearUnit UnitModel
|
||||
|
||||
func (m *clearUnit) Enabled() bool {
|
||||
i := (*Model)(m).d.InstrIndex
|
||||
return i >= 0 && i < len(m.d.Song.Patch) && len(m.d.Song.Patch[i].Units) > 0
|
||||
}
|
||||
func (m *clearUnit) Do() {
|
||||
defer (*Model)(m).change("DeleteUnitAction", PatchChange, MajorChange)()
|
||||
l := ((*UnitModel)(m)).List()
|
||||
r := l.listRange()
|
||||
for i := r.Start; i < r.End; i++ {
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units[i] = sointu.Unit{}
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units[i].ID = (*Model)(m).maxID() + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Searching returns a Bool telling whether the user is currently searching for
|
||||
// a unit (should the search resultsbe displayed).
|
||||
func (m *UnitModel) Searching() Bool { return MakeBool((*unitSearching)(m)) }
|
||||
|
||||
type unitSearching UnitModel
|
||||
|
||||
func (m *unitSearching) Value() bool { return m.d.UnitSearching }
|
||||
func (m *unitSearching) SetValue(val bool) {
|
||||
m.d.UnitSearching = val
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
m.d.UnitSearchString = ""
|
||||
return
|
||||
}
|
||||
if m.d.UnitIndex < 0 || m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
|
||||
m.d.UnitSearchString = ""
|
||||
return
|
||||
}
|
||||
m.d.UnitSearchString = m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Type
|
||||
(*UnitModel)(m).updateDerivedUnitSearch()
|
||||
}
|
||||
|
||||
// SearchTerm returns a String which is the search term user has typed when
|
||||
// searching for units.
|
||||
func (m *UnitModel) SearchTerm() String { return MakeString((*unitSearchTerm)(m)) }
|
||||
|
||||
type unitSearchTerm UnitModel
|
||||
|
||||
func (v *unitSearchTerm) Value() string {
|
||||
// return current unit type string if not searching
|
||||
if !v.d.UnitSearching {
|
||||
if v.d.InstrIndex < 0 || v.d.InstrIndex >= len(v.d.Song.Patch) {
|
||||
return ""
|
||||
}
|
||||
if v.d.UnitIndex < 0 || v.d.UnitIndex >= len(v.d.Song.Patch[v.d.InstrIndex].Units) {
|
||||
return ""
|
||||
}
|
||||
return v.d.Song.Patch[v.d.InstrIndex].Units[v.d.UnitIndex].Type
|
||||
} else {
|
||||
return v.d.UnitSearchString
|
||||
}
|
||||
}
|
||||
func (v *unitSearchTerm) SetValue(value string) bool {
|
||||
v.d.UnitSearchString = value
|
||||
v.d.UnitSearching = true
|
||||
(*UnitModel)(v).updateDerivedUnitSearch()
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *UnitModel) 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.SearchTerm().Value()) {
|
||||
v.derived.searchResults = append(v.derived.searchResults, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SearchResult returns the unit search result at a given index.
|
||||
func (l *UnitModel) SearchResult(index int) (name string, ok bool) {
|
||||
if index < 0 || index >= len(l.derived.searchResults) {
|
||||
return "", false
|
||||
}
|
||||
return l.derived.searchResults[index], true
|
||||
}
|
||||
|
||||
// SearchResults returns a List of all the unit names matching the given search
|
||||
// term.
|
||||
func (m *UnitModel) SearchResults() List { return List{(*unitSearchResults)(m)} }
|
||||
|
||||
type unitSearchResults UnitModel
|
||||
|
||||
func (l *unitSearchResults) Selected() int { return l.d.UnitSearchIndex }
|
||||
func (l *unitSearchResults) Selected2() int { return l.d.UnitSearchIndex }
|
||||
func (l *unitSearchResults) SetSelected(value int) { l.d.UnitSearchIndex = value }
|
||||
func (l *unitSearchResults) SetSelected2(value int) {}
|
||||
func (l *unitSearchResults) Count() (count int) { return len(l.derived.searchResults) }
|
||||
|
||||
// Comment returns a String representing the comment string of the current unit.
|
||||
func (m *UnitModel) Comment() String { return MakeString((*unitComment)(m)) }
|
||||
|
||||
type unitComment UnitModel
|
||||
|
||||
func (v *unitComment) Value() string {
|
||||
if v.d.InstrIndex < 0 || v.d.InstrIndex >= len(v.d.Song.Patch) ||
|
||||
v.d.UnitIndex < 0 || v.d.UnitIndex >= len(v.d.Song.Patch[v.d.InstrIndex].Units) {
|
||||
return ""
|
||||
}
|
||||
return v.d.Song.Patch[v.d.InstrIndex].Units[v.d.UnitIndex].Comment
|
||||
}
|
||||
func (v *unitComment) SetValue(value string) bool {
|
||||
if v.d.InstrIndex < 0 || v.d.InstrIndex >= len(v.d.Song.Patch) ||
|
||||
v.d.UnitIndex < 0 || v.d.UnitIndex >= len(v.d.Song.Patch[v.d.InstrIndex].Units) {
|
||||
return false
|
||||
}
|
||||
defer (*Model)(v).change("UnitComment", PatchChange, MinorChange)()
|
||||
v.d.Song.Patch[v.d.InstrIndex].Units[v.d.UnitIndex].Comment = value
|
||||
return true
|
||||
}
|
||||
|
||||
// Disabled returns a Bool controlling whether the currently selected unit(s)
|
||||
// are disabled.
|
||||
func (m *UnitModel) Disabled() Bool { return MakeBool((*unitDisabled)(m)) }
|
||||
|
||||
type unitDisabled UnitModel
|
||||
|
||||
func (m *unitDisabled) Value() bool {
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return false
|
||||
}
|
||||
if m.d.UnitIndex < 0 || m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
|
||||
return false
|
||||
}
|
||||
return m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Disabled
|
||||
}
|
||||
func (m *unitDisabled) SetValue(val bool) {
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return
|
||||
}
|
||||
l := ((*UnitModel)(m)).List()
|
||||
r := l.listRange()
|
||||
defer (*Model)(m).change("UnitDisabledSet", PatchChange, MajorChange)()
|
||||
for i := r.Start; i < r.End; i++ {
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units[i].Disabled = val
|
||||
}
|
||||
}
|
||||
func (m *unitDisabled) Enabled() bool {
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return false
|
||||
}
|
||||
if len(m.d.Song.Patch[m.d.InstrIndex].Units) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Item returns information about the unit at the given index.
|
||||
func (v *UnitModel) Item(index int) UnitListItem {
|
||||
i := v.d.InstrIndex
|
||||
if i < 0 || i >= len(v.d.Song.Patch) || index < 0 || index >= (*unitList)(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,
|
||||
}
|
||||
}
|
||||
|
||||
type UnitListItem struct {
|
||||
Type, Comment string
|
||||
Disabled bool
|
||||
Signals Rail
|
||||
}
|
||||
|
||||
// Type returns the type of the currently selected unit.
|
||||
func (m *UnitModel) Type() string {
|
||||
if m.d.InstrIndex < 0 ||
|
||||
m.d.InstrIndex >= len(m.d.Song.Patch) ||
|
||||
m.d.UnitIndex < 0 ||
|
||||
m.d.UnitIndex >= len(m.d.Song.Patch[m.d.InstrIndex].Units) {
|
||||
return ""
|
||||
}
|
||||
return m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex].Type
|
||||
}
|
||||
|
||||
// SetType sets the type of the currently selected unit.
|
||||
func (m *UnitModel) SetType(t string) {
|
||||
if m.d.InstrIndex < 0 ||
|
||||
m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return
|
||||
}
|
||||
if m.d.UnitIndex < 0 {
|
||||
m.d.UnitIndex = 0
|
||||
}
|
||||
for len(m.d.Song.Patch[m.d.InstrIndex].Units) <= m.d.UnitIndex {
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units = append(m.d.Song.Patch[m.d.InstrIndex].Units, sointu.Unit{})
|
||||
}
|
||||
unit, ok := defaultUnits[t]
|
||||
if !ok { // if the type is invalid, we just set it to empty unit
|
||||
unit = sointu.Unit{Parameters: make(map[string]int)}
|
||||
} else {
|
||||
unit = unit.Copy()
|
||||
}
|
||||
oldUnit := m.d.Song.Patch[m.d.InstrIndex].Units[m.d.UnitIndex]
|
||||
if oldUnit.Type == unit.Type {
|
||||
return
|
||||
}
|
||||
defer (*unitList)(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
|
||||
}
|
||||
|
||||
// List returns a List of all the units of the selected instrument, implementing
|
||||
// ListData & MutableListData interfaces
|
||||
func (m *UnitModel) List() List { return List{(*unitList)(m)} }
|
||||
|
||||
type unitList UnitModel
|
||||
|
||||
func (v *unitList) Selected() int { return v.d.UnitIndex }
|
||||
func (v *unitList) Selected2() int { return v.d.UnitIndex2 }
|
||||
func (v *unitList) SetSelected2(value int) { v.d.UnitIndex2 = value }
|
||||
func (m *unitList) SetSelected(value int) {
|
||||
m.d.UnitIndex = value
|
||||
m.d.ParamIndex = 0
|
||||
m.d.UnitSearching = false
|
||||
m.d.UnitSearchString = ""
|
||||
}
|
||||
func (v *unitList) Count() int {
|
||||
if v.d.InstrIndex < 0 || v.d.InstrIndex >= len(v.d.Song.Patch) {
|
||||
return 0
|
||||
}
|
||||
return len(v.d.Song.Patch[v.d.InstrIndex].Units)
|
||||
}
|
||||
|
||||
func (v *unitList) 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
|
||||
}
|
||||
units := m.d.Song.Patch[m.d.InstrIndex].Units
|
||||
for i, j := range r.Swaps(delta) {
|
||||
units[i], units[j] = units[j], units[i]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *unitList) Delete(r Range) (ok bool) {
|
||||
m := (*Model)(v)
|
||||
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
||||
return false
|
||||
}
|
||||
u := m.d.Song.Patch[m.d.InstrIndex].Units
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units = append(u[:r.Start], u[r.End:]...)
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *unitList) Change(n string, severity ChangeSeverity) func() {
|
||||
return (*Model)(v).change("UnitListView."+n, PatchChange, severity)
|
||||
}
|
||||
|
||||
func (v *unitList) Cancel() {
|
||||
(*Model)(v).changeCancel = true
|
||||
}
|
||||
|
||||
func (v *unitList) 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")
|
||||
}
|
||||
units := m.d.Song.Patch[m.d.InstrIndex].Units[r.Start:r.End]
|
||||
ret, err := yaml.Marshal(struct{ Units []sointu.Unit }{units})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("UnitListView.marshal: %v", err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (v *unitList) 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")
|
||||
}
|
||||
var pastedUnits struct{ Units []sointu.Unit }
|
||||
if err := yaml.Unmarshal(data, &pastedUnits); err != nil {
|
||||
return Range{}, fmt.Errorf("UnitListView.unmarshal: %v", err)
|
||||
}
|
||||
if len(pastedUnits.Units) == 0 {
|
||||
return Range{}, errors.New("UnitListView.unmarshal: no units")
|
||||
}
|
||||
m.assignUnitIDs(pastedUnits.Units)
|
||||
sel := v.Selected()
|
||||
var ok bool
|
||||
m.d.Song.Patch[m.d.InstrIndex].Units, ok = Insert(m.d.Song.Patch[m.d.InstrIndex].Units, sel, pastedUnits.Units...)
|
||||
if !ok {
|
||||
return Range{}, errors.New("UnitListView.unmarshal: insert failed")
|
||||
}
|
||||
return Range{sel, sel + len(pastedUnits.Units)}, nil
|
||||
}
|
||||
|
||||
func (s *UnitModel) RailError() RailError { return s.derived.railError }
|
||||
|
||||
func (s *UnitModel) RailWidth() int {
|
||||
i := s.d.InstrIndex
|
||||
if i < 0 || i >= len(s.derived.patch) {
|
||||
return 0
|
||||
}
|
||||
return s.derived.patch[i].railWidth
|
||||
}
|
||||
|
||||
func (e *RailError) Error() string { return e.Err.Error() }
|
||||
|
||||
func (s *Rail) StackAfter() int { return s.PassThrough + s.StackUse.NumOutputs }
|
||||
Reference in New Issue
Block a user