mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
parent
160eb8eea9
commit
9779beee99
@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### 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
|
- 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.
|
and if the pattern is used in multiple places, the pattern is copied first.
|
||||||
([#77][i77])
|
([#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
|
[i77]: https://github.com/vsariola/sointu/issues/77
|
||||||
[i94]: https://github.com/vsariola/sointu/issues/94
|
[i94]: https://github.com/vsariola/sointu/issues/94
|
||||||
[i112]: https://github.com/vsariola/sointu/issues/112
|
[i112]: https://github.com/vsariola/sointu/issues/112
|
||||||
|
[i114]: https://github.com/vsariola/sointu/issues/114
|
||||||
[i116]: https://github.com/vsariola/sointu/issues/116
|
[i116]: https://github.com/vsariola/sointu/issues/116
|
||||||
[i120]: https://github.com/vsariola/sointu/issues/120
|
[i120]: https://github.com/vsariola/sointu/issues/120
|
||||||
[i121]: https://github.com/vsariola/sointu/issues/121
|
[i121]: https://github.com/vsariola/sointu/issues/121
|
||||||
|
7
patch.go
7
patch.go
@ -49,6 +49,11 @@ type (
|
|||||||
// Disabled is a flag that can be set to true to disable the unit.
|
// 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 units are considered to be not present in the patch.
|
||||||
Disabled bool `yaml:",omitempty"`
|
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
|
// UnitParameter documents one parameter that an unit takes
|
||||||
@ -291,7 +296,7 @@ func (u *Unit) Copy() Unit {
|
|||||||
}
|
}
|
||||||
varArgs := make([]int, len(u.VarArgs))
|
varArgs := make([]int, len(u.VarArgs))
|
||||||
copy(varArgs, 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
|
// StackChange returns how this unit will affect the signal stack. "pop" and
|
||||||
|
@ -393,7 +393,8 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
|||||||
count := intMin(ie.unitDragList.TrackerList.Count(), 256)
|
count := intMin(ie.unitDragList.TrackerList.Count(), 256)
|
||||||
|
|
||||||
element := func(gtx C, i int) D {
|
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 {
|
if i < 0 || i > 255 {
|
||||||
return layout.Dimensions{Size: gtx.Constraints.Min}
|
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}
|
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)}
|
rightMargin := layout.Inset{Right: unit.Dp(10)}
|
||||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
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() {
|
if i == ie.unitDragList.TrackerList.Selected() {
|
||||||
editor := material.Editor(t.Theme, ie.searchEditor, "---")
|
editor := material.Editor(t.Theme, ie.searchEditor, "---")
|
||||||
editor.Color = color
|
editor.Color = color
|
||||||
@ -473,6 +474,11 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
|||||||
return unitNameLabel.Layout(gtx)
|
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 {
|
layout.Rigid(func(gtx C) D {
|
||||||
return rightMargin.Layout(gtx, stackLabel.Layout)
|
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,
|
return layout.Stack{Alignment: layout.SE}.Layout(gtx,
|
||||||
layout.Expanded(func(gtx C) D {
|
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()
|
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)
|
dims := unitList.Layout(gtx)
|
||||||
unitList.LayoutScrollBar(gtx)
|
unitList.LayoutScrollBar(gtx)
|
||||||
return dims
|
return dims
|
||||||
|
@ -32,7 +32,9 @@ type UnitEditor struct {
|
|||||||
ClearUnitBtn *ActionClickable
|
ClearUnitBtn *ActionClickable
|
||||||
DisableUnitBtn *BoolClickable
|
DisableUnitBtn *BoolClickable
|
||||||
SelectTypeBtn *widget.Clickable
|
SelectTypeBtn *widget.Clickable
|
||||||
|
commentEditor *widget.Editor
|
||||||
caser cases.Caser
|
caser cases.Caser
|
||||||
|
commentFilters []event.Filter
|
||||||
|
|
||||||
copyHint string
|
copyHint string
|
||||||
disableUnitHint string
|
disableUnitHint string
|
||||||
@ -46,10 +48,19 @@ func NewUnitEditor(m *tracker.Model) *UnitEditor {
|
|||||||
DisableUnitBtn: NewBoolClickable(m.UnitDisabled().Bool()),
|
DisableUnitBtn: NewBoolClickable(m.UnitDisabled().Bool()),
|
||||||
CopyUnitBtn: new(TipClickable),
|
CopyUnitBtn: new(TipClickable),
|
||||||
SelectTypeBtn: new(widget.Clickable),
|
SelectTypeBtn: new(widget.Clickable),
|
||||||
|
commentEditor: &widget.Editor{SingleLine: true, Submit: true, MaxLen: 16},
|
||||||
sliderList: NewDragList(m.Params().List(), layout.Vertical),
|
sliderList: NewDragList(m.Params().List(), layout.Vertical),
|
||||||
searchList: NewDragList(m.SearchResults().List(), layout.Vertical),
|
searchList: NewDragList(m.SearchResults().List(), layout.Vertical),
|
||||||
}
|
}
|
||||||
ret.caser = cases.Title(language.English)
|
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.copyHint = makeHint("Copy unit", " (%s)", "Copy")
|
||||||
ret.disableUnitHint = makeHint("Disable unit", " (%s)", "UnitDisabledToggle")
|
ret.disableUnitHint = makeHint("Disable unit", " (%s)", "UnitDisabledToggle")
|
||||||
ret.enableUnitHint = makeHint("Enable 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)
|
text = pe.caser.String(text)
|
||||||
}
|
}
|
||||||
hintText := Label(text, white, t.Theme.Shaper)
|
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,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(deleteUnitBtnStyle.Layout),
|
layout.Rigid(deleteUnitBtnStyle.Layout),
|
||||||
layout.Rigid(copyUnitBtnStyle.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)}
|
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)
|
targetInstrument := p.tracker.Instrument(targetI)
|
||||||
instrName = targetInstrument.Name
|
instrName = targetInstrument.Name
|
||||||
units := targetInstrument.Units
|
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))
|
unitItems = make([]MenuItem, len(units))
|
||||||
for j, unit := range units {
|
for j, unit := range units {
|
||||||
id := unit.ID
|
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].IconBytes = icons.NavigationChevronRight
|
||||||
unitItems[j].Doer = tracker.Allow(func() {
|
unitItems[j].Doer = tracker.Allow(func() {
|
||||||
tracker.Int{IntData: p.w.Parameter}.Set(id)
|
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),
|
layout.Rigid(p.tracker.layoutMenu(gtx, instrName, &p.w.instrBtn, &p.w.instrMenu, unit.Dp(200),
|
||||||
instrItems...,
|
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...,
|
unitItems...,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
@ -33,7 +33,7 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
UnitListItem struct {
|
UnitListItem struct {
|
||||||
Type string
|
Type, Comment string
|
||||||
Disabled bool
|
Disabled bool
|
||||||
StackNeed, StackBefore, StackAfter int
|
StackNeed, StackBefore, StackAfter int
|
||||||
}
|
}
|
||||||
@ -332,6 +332,7 @@ func (v *Units) Iterate(yield UnitYieldFunc) {
|
|||||||
stackAfter := stackBefore + unit.StackChange()
|
stackAfter := stackBefore + unit.StackChange()
|
||||||
if !yield(UnitListItem{
|
if !yield(UnitListItem{
|
||||||
Type: unit.Type,
|
Type: unit.Type,
|
||||||
|
Comment: unit.Comment,
|
||||||
Disabled: unit.Disabled,
|
Disabled: unit.Disabled,
|
||||||
StackNeed: unit.StackNeed(),
|
StackNeed: unit.StackNeed(),
|
||||||
StackBefore: stackBefore,
|
StackBefore: stackBefore,
|
||||||
|
@ -15,6 +15,7 @@ type (
|
|||||||
InstrumentName Model
|
InstrumentName Model
|
||||||
InstrumentComment Model
|
InstrumentComment Model
|
||||||
UnitSearch Model
|
UnitSearch Model
|
||||||
|
UnitComment Model
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v String) Set(value string) {
|
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) InstrumentName() *InstrumentName { return (*InstrumentName)(m) }
|
||||||
func (m *Model) InstrumentComment() *InstrumentComment { return (*InstrumentComment)(m) }
|
func (m *Model) InstrumentComment() *InstrumentComment { return (*InstrumentComment)(m) }
|
||||||
func (m *Model) UnitSearch() *UnitSearch { return (*UnitSearch)(m) }
|
func (m *Model) UnitSearch() *UnitSearch { return (*UnitSearch)(m) }
|
||||||
|
func (m *Model) UnitComment() *UnitComment { return (*UnitComment)(m) }
|
||||||
|
|
||||||
// FilePathString
|
// FilePathString
|
||||||
|
|
||||||
@ -108,3 +110,26 @@ func (v *InstrumentComment) setValue(value string) {
|
|||||||
func (v *InstrumentComment) change(kind string) func() {
|
func (v *InstrumentComment) change(kind string) func() {
|
||||||
return (*Model)(v).change("InstrumentComment."+kind, PatchChange, MinorChange)
|
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)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user