feat(tracker): add ability to copy, cut and paste units

This commit is contained in:
5684185+vsariola@users.noreply.github.com 2023-07-18 17:17:37 +03:00
parent 338529012a
commit 5a2e87982e
4 changed files with 56 additions and 1 deletions

View File

@ -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)

View File

@ -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 != "" {

View File

@ -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) {

View File

@ -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