Files
sointu/tracker/gioui/instrument_presets.go
5684185+vsariola@users.noreply.github.com cc03a7cf91 drafting
2025-10-18 22:44:32 +03:00

212 lines
6.9 KiB
Go

package gioui
import (
"image"
"gioui.org/io/key"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/text"
"gioui.org/unit"
"github.com/vsariola/sointu/tracker"
"golang.org/x/exp/shiny/materialdesign/icons"
)
type (
InstrumentPresets struct {
searchEditor *Editor
gmDlsBtn *Clickable
userPresetsBtn *Clickable
builtinPresetsBtn *Clickable
clearSearchBtn *Clickable
saveUserPreset *Clickable
deleteUserPreset *Clickable
dirList *DragList
resultList *DragList
}
)
func NewInstrumentPresets(m *tracker.Model) *InstrumentPresets {
return &InstrumentPresets{
searchEditor: NewEditor(true, true, text.Start),
gmDlsBtn: new(Clickable),
clearSearchBtn: new(Clickable),
userPresetsBtn: new(Clickable),
builtinPresetsBtn: new(Clickable),
saveUserPreset: new(Clickable),
deleteUserPreset: new(Clickable),
dirList: NewDragList(m.PresetDirList().List(), layout.Vertical),
resultList: NewDragList(m.PresetResultList().List(), layout.Vertical),
}
}
func (ip *InstrumentPresets) Tags(level int, yield TagYieldFunc) bool {
return yield(level, &ip.searchEditor.widgetEditor) &&
yield(level+1, ip.clearSearchBtn) &&
yield(level+1, ip.builtinPresetsBtn) &&
yield(level+1, ip.userPresetsBtn) &&
yield(level+1, ip.gmDlsBtn) &&
yield(level, ip.dirList) &&
yield(level, ip.resultList) &&
yield(level+1, ip.saveUserPreset) &&
yield(level+1, ip.deleteUserPreset)
}
func (ip *InstrumentPresets) update(gtx C) {
for {
event, ok := gtx.Event(
key.Filter{Focus: ip.resultList, Name: key.NameLeftArrow},
)
if !ok {
break
}
if e, ok := event.(key.Event); ok && e.State == key.Press {
switch e.Name {
case key.NameLeftArrow:
ip.dirList.Focus()
}
}
}
for {
event, ok := gtx.Event(
key.Filter{Focus: ip.dirList, Name: key.NameRightArrow},
)
if !ok {
break
}
if e, ok := event.(key.Event); ok && e.State == key.Press {
switch e.Name {
case key.NameRightArrow:
ip.resultList.Focus()
}
}
}
}
func (ip *InstrumentPresets) layout(gtx C) D {
ip.update(gtx)
// get tracker from values
tr := TrackerFromContext(gtx)
gmDlsBtn := ToggleBtn(tr.NoGmDls(), tr.Theme, ip.gmDlsBtn, "No gm.dls", "Exclude presets using gm.dls")
userPresetsFilterBtn := ToggleBtn(tr.UserPresetFilter(), tr.Theme, ip.userPresetsBtn, "User", "Show only user presets")
builtinPresetsFilterBtn := ToggleBtn(tr.BuiltinPresetsFilter(), tr.Theme, ip.builtinPresetsBtn, "Builtin", "Show only builtin presets")
saveUserPresetBtn := ActionIconBtn(tr.SaveAsUserPreset(), tr.Theme, ip.saveUserPreset, icons.ContentSave, "Save instrument as user preset")
deleteUserPresetBtn := ActionIconBtn(tr.TryDeleteUserPreset(), tr.Theme, ip.deleteUserPreset, icons.ActionDelete, "Delete user preset")
dirElem := func(gtx C, i int) D {
return Label(tr.Theme, &tr.Theme.InstrumentEditor.Presets.Directory, tr.Model.PresetDirList().Value(i)).Layout(gtx)
}
dirs := func(gtx C) D {
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(140), gtx.Constraints.Max.Y))
fdl := FilledDragList(tr.Theme, ip.dirList)
dims := fdl.Layout(gtx, dirElem, nil)
fdl.LayoutScrollBar(gtx)
return dims
}
dirSurface := func(gtx C) D {
return Surface{Gray: 36, Focus: tr.PatchPanel.TreeFocused(gtx)}.Layout(gtx, dirs)
}
resultElem := func(gtx C, i int) D {
gtx.Constraints.Min.X = gtx.Constraints.Max.X
n, d, u := tr.Model.PresetResultList().Value(i)
if u {
ln := Label(tr.Theme, &tr.Theme.InstrumentEditor.Presets.Results.User, n)
ld := Label(tr.Theme, &tr.Theme.InstrumentEditor.Presets.Results.UserDir, d)
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(ln.Layout),
layout.Rigid(layout.Spacer{Width: 6}.Layout),
layout.Rigid(ld.Layout),
)
}
return Label(tr.Theme, &tr.Theme.InstrumentEditor.Presets.Results.Builtin, n).Layout(gtx)
}
floatButtons := func(gtx C) D {
if tr.Model.DeleteUserPreset().Enabled() {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(deleteUserPresetBtn.Layout),
layout.Rigid(saveUserPresetBtn.Layout),
layout.Rigid(layout.Spacer{Width: 10}.Layout),
)
}
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(saveUserPresetBtn.Layout),
layout.Rigid(layout.Spacer{Width: 10}.Layout),
)
}
results := func(gtx C) D {
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
fdl := FilledDragList(tr.Theme, ip.resultList)
dims := fdl.Layout(gtx, resultElem, nil)
layout.SE.Layout(gtx, floatButtons)
fdl.LayoutScrollBar(gtx)
return dims
}
resultSurface := func(gtx C) D {
return Surface{Gray: 30, Focus: tr.PatchPanel.TreeFocused(gtx)}.Layout(gtx, results)
}
bottom := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(dirSurface),
layout.Flexed(1, resultSurface),
)
}
// layout
f := func(gtx C) D {
m := gtx.Constraints.Max
gtx.Constraints.Max.X = min(gtx.Dp(360), gtx.Constraints.Max.X)
layout.Flex{Axis: layout.Vertical, Alignment: layout.Start}.Layout(gtx,
layout.Rigid(ip.layoutSearch),
layout.Rigid(func(gtx C) D {
return layout.UniformInset(unit.Dp(4)).Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(userPresetsFilterBtn.Layout),
layout.Rigid(builtinPresetsFilterBtn.Layout),
layout.Rigid(gmDlsBtn.Layout),
)
})
}),
layout.Rigid(bottom),
)
return D{Size: m}
}
return Surface{Gray: 24, Focus: tr.PatchPanel.TreeFocused(gtx)}.Layout(gtx, f)
}
func (ip *InstrumentPresets) layoutSearch(gtx C) D {
// draw search icon on left and clear button on right
// return ip.searchEditor.Layout(gtx, tr.Model.PresetSearchString(), tr.Theme, &tr.Theme.InstrumentEditor.InstrumentComment, "Search presets")
tr := TrackerFromContext(gtx)
bg := func(gtx C) D {
rr := gtx.Dp(18)
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
paint.Fill(gtx.Ops, tr.Theme.InstrumentEditor.Presets.SearchBg)
return D{Size: gtx.Constraints.Min}
}
// icon, search editor, clear button
icon := func(gtx C) D {
return tr.Theme.IconButton.Enabled.Inset.Layout(gtx, func(gtx C) D {
return tr.Theme.Icon(icons.ActionSearch).Layout(gtx, tr.Theme.Material.Fg)
})
}
ed := func(gtx C) D {
return ip.searchEditor.Layout(gtx, tr.Model.PresetSearchString(), tr.Theme, &tr.Theme.InstrumentEditor.UnitComment, "Search presets")
}
clr := func(gtx C) D {
btn := ActionIconBtn(tr.ClearPresetSearch(), tr.Theme, ip.clearSearchBtn, icons.ContentClear, "Clear search")
return btn.Layout(gtx)
}
w := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(icon),
layout.Flexed(1, ed),
layout.Rigid(clr),
)
}
return layout.UniformInset(unit.Dp(4)).Layout(gtx, func(gtx C) D {
return layout.Stack{}.Layout(gtx,
layout.Expanded(bg),
layout.Stacked(w),
)
})
}