diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e897f..4a82435 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Added +- Units can have comments, to make it easier to distinguish between units of + same type within an instrument. These comments are also shown when choosing + the send target. ([#114][i114]) - A toggle button for copying non-unique patterns before editing. When enabled and if the pattern is used in multiple places, the pattern is copied first. ([#77][i77]) @@ -232,6 +235,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [i77]: https://github.com/vsariola/sointu/issues/77 [i94]: https://github.com/vsariola/sointu/issues/94 [i112]: https://github.com/vsariola/sointu/issues/112 +[i114]: https://github.com/vsariola/sointu/issues/114 [i116]: https://github.com/vsariola/sointu/issues/116 [i120]: https://github.com/vsariola/sointu/issues/120 [i121]: https://github.com/vsariola/sointu/issues/121 diff --git a/patch.go b/patch.go index 549572e..6b3c79d 100644 --- a/patch.go +++ b/patch.go @@ -49,6 +49,11 @@ type ( // Disabled is a flag that can be set to true to disable the unit. // Disabled units are considered to be not present in the patch. Disabled bool `yaml:",omitempty"` + + // Comment is a free-form comment about the unit that can be displayed + // instead of/besides the type of the unit in the GUI, to make it easier + // to track what the unit is doing & to make it easier to target sends. + Comment string `yaml:",omitempty"` } // UnitParameter documents one parameter that an unit takes @@ -291,7 +296,7 @@ func (u *Unit) Copy() Unit { } varArgs := make([]int, len(u.VarArgs)) copy(varArgs, u.VarArgs) - return Unit{Type: u.Type, Parameters: parameters, VarArgs: varArgs, ID: u.ID, Disabled: u.Disabled} + return Unit{Type: u.Type, Parameters: parameters, VarArgs: varArgs, ID: u.ID, Disabled: u.Disabled, Comment: u.Comment} } // StackChange returns how this unit will affect the signal stack. "pop" and diff --git a/tracker/gioui/instrument_editor.go b/tracker/gioui/instrument_editor.go index 7241927..966a5c7 100644 --- a/tracker/gioui/instrument_editor.go +++ b/tracker/gioui/instrument_editor.go @@ -393,7 +393,8 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { count := intMin(ie.unitDragList.TrackerList.Count(), 256) element := func(gtx C, i int) D { - gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(unit.Dp(120)), gtx.Dp(unit.Dp(20)))) + gtx.Constraints.Max.Y = gtx.Dp(20) + gtx.Constraints.Min.Y = gtx.Constraints.Max.Y if i < 0 || i > 255 { return layout.Dimensions{Size: gtx.Constraints.Min} } @@ -418,7 +419,7 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { stackLabel := LabelStyle{Text: stackText, ShadeColor: black, Color: mediumEmphasisTextColor, Font: labelDefaultFont, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper} rightMargin := layout.Inset{Right: unit.Dp(10)} return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, - layout.Flexed(1, func(gtx C) D { + layout.Rigid(func(gtx C) D { if i == ie.unitDragList.TrackerList.Selected() { editor := material.Editor(t.Theme, ie.searchEditor, "---") editor.Color = color @@ -473,6 +474,11 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { return unitNameLabel.Layout(gtx) } }), + layout.Flexed(1, func(gtx C) D { + unitNameLabel := LabelStyle{Text: u.Comment, ShadeColor: black, Color: mediumEmphasisTextColor, Font: f, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper} + inset := layout.Inset{Left: unit.Dp(5)} + return inset.Layout(gtx, unitNameLabel.Layout) + }), layout.Rigid(func(gtx C) D { return rightMargin.Layout(gtx, stackLabel.Layout) }), @@ -517,7 +523,7 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { return layout.Stack{Alignment: layout.SE}.Layout(gtx, layout.Expanded(func(gtx C) D { defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop() - gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(unit.Dp(120)), gtx.Constraints.Max.Y)) + gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(140), gtx.Constraints.Max.Y)) dims := unitList.Layout(gtx) unitList.LayoutScrollBar(gtx) return dims diff --git a/tracker/gioui/unit_editor.go b/tracker/gioui/unit_editor.go index b39bfc1..2738346 100644 --- a/tracker/gioui/unit_editor.go +++ b/tracker/gioui/unit_editor.go @@ -32,7 +32,9 @@ type UnitEditor struct { ClearUnitBtn *ActionClickable DisableUnitBtn *BoolClickable SelectTypeBtn *widget.Clickable + commentEditor *widget.Editor caser cases.Caser + commentFilters []event.Filter copyHint string disableUnitHint string @@ -46,10 +48,19 @@ func NewUnitEditor(m *tracker.Model) *UnitEditor { DisableUnitBtn: NewBoolClickable(m.UnitDisabled().Bool()), CopyUnitBtn: new(TipClickable), SelectTypeBtn: new(widget.Clickable), + commentEditor: &widget.Editor{SingleLine: true, Submit: true, MaxLen: 16}, sliderList: NewDragList(m.Params().List(), layout.Vertical), searchList: NewDragList(m.SearchResults().List(), layout.Vertical), } ret.caser = cases.Title(language.English) + for k, a := range keyBindingMap { + if len(a) < 4 || a[:4] != "Note" { + continue + } + ret.commentFilters = append(ret.commentFilters, key.Filter{Name: k.Name, Required: k.Modifiers, Focus: ret.commentEditor}) + } + ret.commentFilters = append(ret.commentFilters, key.Filter{Name: key.NameSpace, Focus: ret.commentEditor}) + ret.commentFilters = append(ret.commentFilters, key.Filter{Name: key.NameEscape, Focus: ret.commentEditor}) ret.copyHint = makeHint("Copy unit", " (%s)", "Copy") ret.disableUnitHint = makeHint("Disable unit", " (%s)", "UnitDisabledToggle") ret.enableUnitHint = makeHint("Enable unit", " (%s)", "UnitDisabledToggle") @@ -139,6 +150,11 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D { text = pe.caser.String(text) } hintText := Label(text, white, t.Theme.Shaper) + commentStyle := material.Editor(t.Theme, pe.commentEditor, "---") + commentStyle.Font = labelDefaultFont + commentStyle.TextSize = labelDefaultFontSize + commentStyle.Color = mediumEmphasisTextColor + commentStyle.HintColor = mediumEmphasisTextColor return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(deleteUnitBtnStyle.Layout), layout.Rigid(copyUnitBtnStyle.Layout), @@ -151,7 +167,39 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D { } return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)} }), - layout.Flexed(1, hintText), + layout.Rigid(func(gtx C) D { + gtx.Constraints.Min.X = gtx.Dp(120) + return hintText(gtx) + }), + layout.Flexed(1, func(gtx C) D { + s := t.UnitComment().String() + if pe.commentEditor.Text() != s.Value() { + pe.commentEditor.SetText(s.Value()) + } + for { + ev, ok := pe.commentEditor.Update(gtx) + if !ok { + break + } + _, ok = ev.(widget.SubmitEvent) + if ok { + t.InstrumentEditor.Focus() + continue + } + } + for { + event, ok := gtx.Event(pe.commentFilters...) + if !ok { + break + } + if e, ok := event.(key.Event); ok && e.State == key.Press && e.Name == key.NameEscape { + t.InstrumentEditor.Focus() + } + } + ret := commentStyle.Layout(gtx) + s.Set(commentStyle.Editor.Text()) + return ret + }), ) } @@ -317,11 +365,11 @@ func (p ParameterStyle) Layout(gtx C) D { targetInstrument := p.tracker.Instrument(targetI) instrName = targetInstrument.Name units := targetInstrument.Units - unitName = fmt.Sprintf("%v: %v", targetU, units[targetU].Type) + unitName = fmt.Sprintf("%d: %s %s", targetU, units[targetU].Type, units[targetU].Comment) unitItems = make([]MenuItem, len(units)) for j, unit := range units { id := unit.ID - unitItems[j].Text = fmt.Sprintf("%v: %v", j, unit.Type) + unitItems[j].Text = fmt.Sprintf("%d: %s %s", j, unit.Type, unit.Comment) unitItems[j].IconBytes = icons.NavigationChevronRight unitItems[j].Doer = tracker.Allow(func() { tracker.Int{IntData: p.w.Parameter}.Set(id) @@ -333,7 +381,7 @@ func (p ParameterStyle) Layout(gtx C) D { layout.Rigid(p.tracker.layoutMenu(gtx, instrName, &p.w.instrBtn, &p.w.instrMenu, unit.Dp(200), instrItems..., )), - layout.Rigid(p.tracker.layoutMenu(gtx, unitName, &p.w.unitBtn, &p.w.unitMenu, unit.Dp(200), + layout.Rigid(p.tracker.layoutMenu(gtx, unitName, &p.w.unitBtn, &p.w.unitMenu, unit.Dp(240), unitItems..., )), ) diff --git a/tracker/list.go b/tracker/list.go index 2a7db6d..8ba5731 100644 --- a/tracker/list.go +++ b/tracker/list.go @@ -33,7 +33,7 @@ type ( } UnitListItem struct { - Type string + Type, Comment string Disabled bool StackNeed, StackBefore, StackAfter int } @@ -332,6 +332,7 @@ func (v *Units) Iterate(yield UnitYieldFunc) { stackAfter := stackBefore + unit.StackChange() if !yield(UnitListItem{ Type: unit.Type, + Comment: unit.Comment, Disabled: unit.Disabled, StackNeed: unit.StackNeed(), StackBefore: stackBefore, diff --git a/tracker/string.go b/tracker/string.go index 55ee5f9..34e5b0c 100644 --- a/tracker/string.go +++ b/tracker/string.go @@ -15,6 +15,7 @@ type ( InstrumentName Model InstrumentComment Model UnitSearch Model + UnitComment Model ) func (v String) Set(value string) { @@ -30,6 +31,7 @@ func (m *Model) FilePath() *FilePath { return (*FilePath)(m) } func (m *Model) InstrumentName() *InstrumentName { return (*InstrumentName)(m) } func (m *Model) InstrumentComment() *InstrumentComment { return (*InstrumentComment)(m) } func (m *Model) UnitSearch() *UnitSearch { return (*UnitSearch)(m) } +func (m *Model) UnitComment() *UnitComment { return (*UnitComment)(m) } // FilePathString @@ -108,3 +110,26 @@ func (v *InstrumentComment) setValue(value string) { func (v *InstrumentComment) change(kind string) func() { return (*Model)(v).change("InstrumentComment."+kind, PatchChange, MinorChange) } + +// UnitComment + +func (v *UnitComment) String() String { return String{v} } +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) { + 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 + } + v.d.Song.Patch[v.d.InstrIndex].Units[v.d.UnitIndex].Comment = value +} + +func (v *UnitComment) change(kind string) func() { + return (*Model)(v).change("UnitComment."+kind, PatchChange, MinorChange) +}