diff --git a/tracker/gioui/instrumenteditor.go b/tracker/gioui/instrumenteditor.go index ad7a5d3..b778bfd 100644 --- a/tracker/gioui/instrumenteditor.go +++ b/tracker/gioui/instrumenteditor.go @@ -462,7 +462,7 @@ func (ie *InstrumentEditor) layoutInstrumentEditor(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() - key.InputOp{Tag: ie.unitDragList, Keys: "→|⏎|⌫|⌦|⎋|Ctrl-⏎"}.Add(gtx.Ops) + key.InputOp{Tag: ie.unitDragList, Keys: "→|⏎|⌫|⌦|⎋|Ctrl-⏎|Ctrl-C|Ctrl-X"}.Add(gtx.Ops) for _, event := range gtx.Events(ie.unitDragList) { switch e := event.(type) { case key.Event: @@ -480,6 +480,19 @@ func (ie *InstrumentEditor) layoutInstrumentEditor(gtx C, t *Tracker) D { ie.unitTypeEditor.SetCaret(l, l) case key.NameDeleteForward: t.DeleteUnit(true) + case "C": + contents, err := yaml.Marshal(t.Unit()) + if err == nil { + clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) + t.Alert.Update("Unit copied to clipboard", Notify, time.Second*3) + } + case "X": + contents, err := yaml.Marshal(t.Unit()) + if err == nil { + clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) + t.Alert.Update("Unit cut to clipboard", Notify, time.Second*3) + } + t.DeleteUnit(true) case key.NameReturn: if e.Modifiers.Contain(key.ModShortcut) { t.AddUnit(true) diff --git a/tracker/gioui/parameditor.go b/tracker/gioui/parameditor.go index 7b799d6..09c4454 100644 --- a/tracker/gioui/parameditor.go +++ b/tracker/gioui/parameditor.go @@ -4,7 +4,9 @@ import ( "image" "image/color" "strings" + "time" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/layout" @@ -15,6 +17,7 @@ import ( "gioui.org/widget" "github.com/vsariola/sointu/tracker" "golang.org/x/exp/shiny/materialdesign/icons" + "gopkg.in/yaml.v3" ) type ParamEditor struct { @@ -22,6 +25,7 @@ type ParamEditor struct { scrollBar *ScrollBar Parameters []*ParameterWidget DeleteUnitBtn *widget.Clickable + CopyUnitBtn *widget.Clickable ClearUnitBtn *widget.Clickable ChooseUnitTypeBtns []*widget.Clickable tag bool @@ -41,6 +45,7 @@ func NewParamEditor() *ParamEditor { ret := &ParamEditor{ DeleteUnitBtn: new(widget.Clickable), ClearUnitBtn: new(widget.Clickable), + CopyUnitBtn: new(widget.Clickable), list: &layout.List{Axis: layout.Vertical}, scrollBar: &ScrollBar{Axis: layout.Vertical}, } @@ -178,6 +183,15 @@ func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget { op.InvalidateOp{}.Add(gtx.Ops) t.InstrumentEditor.unitDragList.Focus() } + for pe.CopyUnitBtn.Clicked() { + op.InvalidateOp{}.Add(gtx.Ops) + contents, err := yaml.Marshal(t.Unit()) + if err == nil { + clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) + t.Alert.Update("Unit copied to clipboard", Notify, time.Second*3) + } + } + copyUnitBtnStyle := IconButton(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, true) deleteUnitBtnStyle := IconButton(t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, t.CanDeleteUnit()) text := t.Unit().Type if text == "" { @@ -188,6 +202,7 @@ func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget { hintText := Label(text, white) return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(deleteUnitBtnStyle.Layout), + layout.Rigid(copyUnitBtnStyle.Layout), layout.Rigid(func(gtx C) D { var dims D if t.Unit().Type != "" { diff --git a/tracker/gioui/tracker.go b/tracker/gioui/tracker.go index 0812223..db75640 100644 --- a/tracker/gioui/tracker.go +++ b/tracker/gioui/tracker.go @@ -67,6 +67,15 @@ type Tracker struct { } func (t *Tracker) UnmarshalContent(bytes []byte) error { + var unit sointu.Unit + if errJSON := json.Unmarshal(bytes, &unit); errJSON == nil { + t.PasteUnit(unit) + return nil + } + if errYaml := yaml.Unmarshal(bytes, &unit); errYaml == nil { + t.PasteUnit(unit) + return nil + } var instr sointu.Instrument if errJSON := json.Unmarshal(bytes, &instr); errJSON == nil { if t.SetInstrument(instr) { diff --git a/tracker/model.go b/tracker/model.go index 47d9dc8..e86b82d 100644 --- a/tracker/model.go +++ b/tracker/model.go @@ -641,6 +641,24 @@ func (m *Model) SetUnitType(t string) { m.notifyPatchChange() } +func (m *Model) PasteUnit(unit sointu.Unit) { + m.saveUndo("PasteUnit", 0) + newUnits := make([]sointu.Unit, len(m.Instrument().Units)+1) + m.unitIndex++ + copy(newUnits, m.Instrument().Units[:m.unitIndex]) + copy(newUnits[m.unitIndex+1:], m.Instrument().Units[m.unitIndex:]) + if _, ok := m.usedIDs[unit.ID]; ok { + m.maxID++ + unit.ID = m.maxID + } + m.usedIDs[unit.ID] = true + newUnits[m.unitIndex] = unit + m.song.Patch[m.instrIndex].Units = newUnits + m.paramIndex = 0 + m.clampPositions() + m.notifyPatchChange() +} + func (m *Model) SetUnitIndex(value int) { m.unitIndex = value m.paramIndex = 0