mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-18 21:14:31 -04:00
feat(tracker): add menu to load instrument presets
The presets are embedded in the executable, so there's no additional files. Closes #91
This commit is contained in:
parent
b65d11cbb7
commit
ce7c8a0d3e
@ -34,6 +34,7 @@ type InstrumentEditor struct {
|
||||
loadInstrumentBtn *TipClickable
|
||||
addUnitBtn *TipClickable
|
||||
commentExpandBtn *TipClickable
|
||||
presetMenuBtn *TipClickable
|
||||
commentEditor *widget.Editor
|
||||
nameEditor *widget.Editor
|
||||
unitTypeEditor *widget.Editor
|
||||
@ -48,10 +49,12 @@ type InstrumentEditor struct {
|
||||
wasFocused bool
|
||||
commentExpanded bool
|
||||
voiceStates [vm.MAX_VOICES]float32
|
||||
presetMenuItems []MenuItem
|
||||
presetMenu Menu
|
||||
}
|
||||
|
||||
func NewInstrumentEditor() *InstrumentEditor {
|
||||
return &InstrumentEditor{
|
||||
ret := &InstrumentEditor{
|
||||
newInstrumentBtn: new(TipClickable),
|
||||
enlargeBtn: new(TipClickable),
|
||||
deleteInstrumentBtn: new(TipClickable),
|
||||
@ -60,6 +63,7 @@ func NewInstrumentEditor() *InstrumentEditor {
|
||||
loadInstrumentBtn: new(TipClickable),
|
||||
addUnitBtn: new(TipClickable),
|
||||
commentExpandBtn: new(TipClickable),
|
||||
presetMenuBtn: new(TipClickable),
|
||||
commentEditor: new(widget.Editor),
|
||||
nameEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Middle},
|
||||
unitTypeEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Start},
|
||||
@ -69,7 +73,12 @@ func NewInstrumentEditor() *InstrumentEditor {
|
||||
unitScrollBar: &ScrollBar{Axis: layout.Vertical},
|
||||
confirmInstrDelete: new(Dialog),
|
||||
paramEditor: NewParamEditor(),
|
||||
presetMenuItems: []MenuItem{},
|
||||
}
|
||||
for _, instr := range tracker.Presets {
|
||||
ret.presetMenuItems = append(ret.presetMenuItems, MenuItem{Text: instr.Name, IconBytes: icons.ImageAudiotrack})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (t *InstrumentEditor) ExpandComment() {
|
||||
@ -85,7 +94,8 @@ func (ie *InstrumentEditor) Focused() bool {
|
||||
}
|
||||
|
||||
func (ie *InstrumentEditor) ChildFocused() bool {
|
||||
return ie.paramEditor.Focused() || ie.instrumentDragList.Focused() || ie.commentEditor.Focused() || ie.nameEditor.Focused() || ie.unitTypeEditor.Focused()
|
||||
return ie.paramEditor.Focused() || ie.instrumentDragList.Focused() || ie.commentEditor.Focused() || ie.nameEditor.Focused() || ie.unitTypeEditor.Focused() ||
|
||||
ie.addUnitBtn.Clickable.Focused() || ie.commentExpandBtn.Clickable.Focused() || ie.presetMenuBtn.Clickable.Focused() || ie.deleteInstrumentBtn.Clickable.Focused() || ie.copyInstrumentBtn.Clickable.Focused()
|
||||
}
|
||||
|
||||
func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
||||
@ -109,6 +119,7 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
||||
icon = icons.NavigationFullscreenExit
|
||||
enlargeTip = "Shrink"
|
||||
}
|
||||
|
||||
fullscreenBtnStyle := IconButton(t.Theme, ie.enlargeBtn, icon, true, enlargeTip)
|
||||
for ie.enlargeBtn.Clickable.Clicked() {
|
||||
t.SetInstrEnlarged(!t.InstrEnlarged())
|
||||
@ -175,11 +186,18 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
}
|
||||
|
||||
commentExpandBtnStyle := IconButton(t.Theme, ie.commentExpandBtn, collapseIcon, true, commentTip)
|
||||
presetMenuBtnStyle := IconButton(t.Theme, ie.presetMenuBtn, icons.NavigationMenu, true, "Load preset")
|
||||
copyInstrumentBtnStyle := IconButton(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, true, "Copy instrument")
|
||||
saveInstrumentBtnStyle := IconButton(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, true, "Save instrument")
|
||||
loadInstrumentBtnStyle := IconButton(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, true, "Load instrument")
|
||||
deleteInstrumentBtnStyle := IconButton(t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, t.CanDeleteInstrument(), "Delete\ninstrument")
|
||||
|
||||
m := PopupMenu(t.Theme, &ie.presetMenu)
|
||||
|
||||
for item, clicked := ie.presetMenu.Clicked(); clicked; item, clicked = ie.presetMenu.Clicked() {
|
||||
t.SetInstrument(tracker.Presets[item])
|
||||
}
|
||||
|
||||
header := func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(Label("Voices: ", white)),
|
||||
@ -193,11 +211,24 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
}),
|
||||
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
|
||||
layout.Rigid(commentExpandBtnStyle.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
//defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
||||
dims := presetMenuBtnStyle.Layout(gtx)
|
||||
op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops)
|
||||
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(500))
|
||||
m.Layout(gtx, ie.presetMenuItems...)
|
||||
return dims
|
||||
}),
|
||||
layout.Rigid(saveInstrumentBtnStyle.Layout),
|
||||
layout.Rigid(loadInstrumentBtnStyle.Layout),
|
||||
layout.Rigid(copyInstrumentBtnStyle.Layout),
|
||||
layout.Rigid(deleteInstrumentBtnStyle.Layout))
|
||||
}
|
||||
|
||||
for ie.presetMenuBtn.Clickable.Clicked() {
|
||||
ie.presetMenu.Visible = true
|
||||
}
|
||||
|
||||
for ie.commentExpandBtn.Clickable.Clicked() {
|
||||
ie.commentExpanded = !ie.commentExpanded
|
||||
if !ie.commentExpanded {
|
||||
@ -234,6 +265,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
}
|
||||
return header(gtx)
|
||||
}
|
||||
|
||||
for ie.copyInstrumentBtn.Clickable.Clicked() {
|
||||
contents, err := yaml.Marshal(t.Instrument())
|
||||
if err == nil {
|
||||
|
@ -20,6 +20,7 @@ type Menu struct {
|
||||
tags []bool
|
||||
clicks []int
|
||||
hover int
|
||||
list layout.List
|
||||
}
|
||||
|
||||
type MenuStyle struct {
|
||||
@ -54,8 +55,7 @@ func (m *Menu) Clicked() (int, bool) {
|
||||
|
||||
func (m *MenuStyle) Layout(gtx C, items ...MenuItem) D {
|
||||
contents := func(gtx C) D {
|
||||
flexChildren := make([]layout.FlexChild, len(items))
|
||||
for i, item := range items {
|
||||
for i := range items {
|
||||
// make sure we have a tag for every item
|
||||
for len(m.Menu.tags) <= i {
|
||||
m.Menu.tags = append(m.Menu.tags, false)
|
||||
@ -78,60 +78,58 @@ func (m *MenuStyle) Layout(gtx C, items ...MenuItem) D {
|
||||
}
|
||||
}
|
||||
}
|
||||
// layout contents for this item
|
||||
i2 := i // avoid loop variable getting updated in closure
|
||||
item2 := item
|
||||
flexChildren[i] = layout.Rigid(func(gtx C) D {
|
||||
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
||||
var macro op.MacroOp
|
||||
if i2 == m.Menu.hover-1 && !item2.Disabled {
|
||||
macro = op.Record(gtx.Ops)
|
||||
}
|
||||
icon := widgetForIcon(item2.IconBytes)
|
||||
iconColor := m.IconColor
|
||||
if item2.Disabled {
|
||||
iconColor = mediumEmphasisTextColor
|
||||
}
|
||||
iconInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(6)}
|
||||
textLabel := LabelStyle{Text: item2.Text, FontSize: m.FontSize, Color: m.TextColor}
|
||||
if item2.Disabled {
|
||||
textLabel.Color = mediumEmphasisTextColor
|
||||
}
|
||||
shortcutLabel := LabelStyle{Text: item2.ShortcutText, FontSize: m.FontSize, Color: m.ShortCutColor}
|
||||
shortcutInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(12), Bottom: unit.Dp(2), Top: unit.Dp(2)}
|
||||
dims := layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return iconInset.Layout(gtx, func(gtx C) D {
|
||||
p := gtx.Dp(unit.Dp(m.IconSize))
|
||||
gtx.Constraints.Min = image.Pt(p, p)
|
||||
return icon.Layout(gtx, iconColor)
|
||||
})
|
||||
}),
|
||||
layout.Rigid(textLabel.Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return D{Size: image.Pt(gtx.Constraints.Max.X, 1)} }),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return shortcutInset.Layout(gtx, shortcutLabel.Layout)
|
||||
}),
|
||||
)
|
||||
if i2 == m.Menu.hover-1 && !item2.Disabled {
|
||||
recording := macro.Stop()
|
||||
paint.FillShape(gtx.Ops, m.HoverColor, clip.Rect{
|
||||
Max: image.Pt(dims.Size.X, dims.Size.Y),
|
||||
}.Op())
|
||||
recording.Add(gtx.Ops)
|
||||
}
|
||||
if !item2.Disabled {
|
||||
rect := image.Rect(0, 0, dims.Size.X, dims.Size.Y)
|
||||
area := clip.Rect(rect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: &m.Menu.tags[i2],
|
||||
Types: pointer.Press | pointer.Enter | pointer.Leave,
|
||||
}.Add(gtx.Ops)
|
||||
area.Pop()
|
||||
}
|
||||
return dims
|
||||
})
|
||||
}
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, flexChildren...)
|
||||
m.Menu.list.Axis = layout.Vertical
|
||||
return m.Menu.list.Layout(gtx, len(items), func(gtx C, i int) D {
|
||||
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
||||
var macro op.MacroOp
|
||||
item := &items[i]
|
||||
if i == m.Menu.hover-1 && !item.Disabled {
|
||||
macro = op.Record(gtx.Ops)
|
||||
}
|
||||
icon := widgetForIcon(item.IconBytes)
|
||||
iconColor := m.IconColor
|
||||
if item.Disabled {
|
||||
iconColor = mediumEmphasisTextColor
|
||||
}
|
||||
iconInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(6)}
|
||||
textLabel := LabelStyle{Text: item.Text, FontSize: m.FontSize, Color: m.TextColor}
|
||||
if item.Disabled {
|
||||
textLabel.Color = mediumEmphasisTextColor
|
||||
}
|
||||
shortcutLabel := LabelStyle{Text: item.ShortcutText, FontSize: m.FontSize, Color: m.ShortCutColor}
|
||||
shortcutInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(12), Bottom: unit.Dp(2), Top: unit.Dp(2)}
|
||||
dims := layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return iconInset.Layout(gtx, func(gtx C) D {
|
||||
p := gtx.Dp(unit.Dp(m.IconSize))
|
||||
gtx.Constraints.Min = image.Pt(p, p)
|
||||
return icon.Layout(gtx, iconColor)
|
||||
})
|
||||
}),
|
||||
layout.Rigid(textLabel.Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return D{Size: image.Pt(gtx.Constraints.Max.X, 1)} }),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return shortcutInset.Layout(gtx, shortcutLabel.Layout)
|
||||
}),
|
||||
)
|
||||
if i == m.Menu.hover-1 && !item.Disabled {
|
||||
recording := macro.Stop()
|
||||
paint.FillShape(gtx.Ops, m.HoverColor, clip.Rect{
|
||||
Max: image.Pt(dims.Size.X, dims.Size.Y),
|
||||
}.Op())
|
||||
recording.Add(gtx.Ops)
|
||||
}
|
||||
if !item.Disabled {
|
||||
rect := image.Rect(0, 0, dims.Size.X, dims.Size.Y)
|
||||
area := clip.Rect(rect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: &m.Menu.tags[i],
|
||||
Types: pointer.Press | pointer.Enter | pointer.Leave,
|
||||
}.Add(gtx.Ops)
|
||||
area.Pop()
|
||||
}
|
||||
return dims
|
||||
})
|
||||
}
|
||||
popup := Popup(&m.Menu.Visible)
|
||||
popup.NE = unit.Dp(0)
|
||||
|
Reference in New Issue
Block a user