From 0bd652dcbbfb5b505b4c2e43cb3a7fac74d69758 Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Wed, 15 Oct 2025 23:12:43 +0300 Subject: [PATCH] drafting --- tracker/derived.go | 7 +- tracker/gioui/editor.go | 6 + .../{unit_editor.go => instrument_editor.go} | 0 tracker/gioui/instrument_presets.go | 55 ++++++++ ...nstr_props.go => instrument_properties.go} | 0 tracker/gioui/patch_panel.go | 24 ++-- tracker/model.go | 1 + tracker/presets.go | 133 ++++++++++++++++++ 8 files changed, 212 insertions(+), 14 deletions(-) rename tracker/gioui/{unit_editor.go => instrument_editor.go} (100%) create mode 100644 tracker/gioui/instrument_presets.go rename tracker/gioui/{instr_props.go => instrument_properties.go} (100%) diff --git a/tracker/derived.go b/tracker/derived.go index f754242..f81b048 100644 --- a/tracker/derived.go +++ b/tracker/derived.go @@ -33,9 +33,10 @@ type ( // corresponding part of the model changes. derivedModelData struct { // map Unit by ID, other entities by their respective index - patch []derivedInstrument - tracks []derivedTrack - railError RailError + patch []derivedInstrument + tracks []derivedTrack + railError RailError + presetSearch derivedPresetSearch } derivedInstrument struct { diff --git a/tracker/gioui/editor.go b/tracker/gioui/editor.go index 57f2485..dd7445f 100644 --- a/tracker/gioui/editor.go +++ b/tracker/gioui/editor.go @@ -66,7 +66,13 @@ func (e *Editor) Layout(gtx C, str tracker.String, th *Theme, style *EditorStyle // just consume all events if the user did not consume them } if e.widgetEditor.Text() != str.Value() { + line, col := e.widgetEditor.CaretPos() + // if the user has moved the caret, put it in the end e.widgetEditor.SetText(str.Value()) + if line > 0 || col > 0 { + l := len(e.widgetEditor.Text()) + e.widgetEditor.SetCaret(l, l) + } } me := material.Editor(&th.Material, &e.widgetEditor, hint) me.Font = style.Font diff --git a/tracker/gioui/unit_editor.go b/tracker/gioui/instrument_editor.go similarity index 100% rename from tracker/gioui/unit_editor.go rename to tracker/gioui/instrument_editor.go diff --git a/tracker/gioui/instrument_presets.go b/tracker/gioui/instrument_presets.go new file mode 100644 index 0000000..d99bcb8 --- /dev/null +++ b/tracker/gioui/instrument_presets.go @@ -0,0 +1,55 @@ +package gioui + +import ( + "gioui.org/layout" + "gioui.org/text" + "gioui.org/unit" + "github.com/vsariola/sointu/tracker" +) + +type ( + InstrumentPresets struct { + searchEditor *Editor + gmDlsBtn *Clickable + userPresetsBtn *Clickable + builtinPresetsBtn *Clickable + clearSearchBtn *Clickable + dirList *DragList + resultList *DragList + } +) + +func NewInstrumentPresets(m *tracker.Model) *InstrumentPresets { + return &InstrumentPresets{ + searchEditor: NewEditor(false, false, text.Start), + gmDlsBtn: new(Clickable), + clearSearchBtn: new(Clickable), + userPresetsBtn: new(Clickable), + builtinPresetsBtn: new(Clickable), + dirList: NewDragList(m.Instruments().List(), layout.Vertical), + resultList: NewDragList(m.Instruments().List(), layout.Vertical), + } +} + +func (ip *InstrumentPresets) layout(gtx C) D { + // 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") + // layout + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + 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.Flexed(1, func(gtx C) D { + return ip.searchEditor.Layout(gtx, tr.Model.PresetSearchString(), tr.Theme, &tr.Theme.InstrumentEditor.InstrumentComment, "Search presets") + }), + layout.Rigid(userPresetsFilterBtn.Layout), + layout.Rigid(builtinPresetsFilterBtn.Layout), + layout.Rigid(gmDlsBtn.Layout), + ) + }) + }), + ) +} diff --git a/tracker/gioui/instr_props.go b/tracker/gioui/instrument_properties.go similarity index 100% rename from tracker/gioui/instr_props.go rename to tracker/gioui/instrument_properties.go diff --git a/tracker/gioui/patch_panel.go b/tracker/gioui/patch_panel.go index 23d9882..3fb6cab 100644 --- a/tracker/gioui/patch_panel.go +++ b/tracker/gioui/patch_panel.go @@ -20,11 +20,12 @@ import ( type ( PatchPanel struct { - instrList InstrumentList - tools InstrumentTools - unitList UnitList - unitEditor UnitEditor - instrProps InstrumentProperties + instrList InstrumentList + tools InstrumentTools + unitList UnitList + unitEditor UnitEditor + instrProps InstrumentProperties + instrPresets InstrumentPresets } InstrumentList struct { @@ -66,11 +67,12 @@ type ( func NewPatchPanel(model *tracker.Model) *PatchPanel { return &PatchPanel{ - instrList: MakeInstrList(model), - tools: MakeInstrumentTools(model), - unitList: MakeUnitList(model), - unitEditor: *NewUnitEditor(model), - instrProps: *NewInstrumentProperties(), + instrList: MakeInstrList(model), + tools: MakeInstrumentTools(model), + unitList: MakeUnitList(model), + unitEditor: *NewUnitEditor(model), + instrProps: *NewInstrumentProperties(), + instrPresets: *NewInstrumentPresets(model), } } @@ -81,7 +83,7 @@ func (pp *PatchPanel) Layout(gtx C) D { case tr.InstrComment().Value(): return pp.instrProps.layout(gtx) case tr.InstrPresets().Value(): - return pp.instrProps.layout(gtx) + return pp.instrPresets.layout(gtx) default: // editor return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(pp.unitList.Layout), diff --git a/tracker/model.go b/tracker/model.go index d3a0948..bb84d4f 100644 --- a/tracker/model.go +++ b/tracker/model.go @@ -37,6 +37,7 @@ type ( ChangedSinceRecovery bool SendSource int InstrumentTab InstrumentTab + PresetSearchString string } Model struct { diff --git a/tracker/presets.go b/tracker/presets.go index 3e71da2..29f8117 100644 --- a/tracker/presets.go +++ b/tracker/presets.go @@ -32,8 +32,141 @@ type ( Index int *Model } + + PresetSearchString Model + NoGmDlsFilter Model + BuiltinPresetsFilter Model + UserPresetsFilter Model + PresetDirectory Model + PresetKind Model + + derivedPresetSearch struct { + directory string + noGmDls bool + kind PresetKindEnum + } + + PresetKindEnum int ) +const ( + BuiltinPresets PresetKindEnum = -1 + AllPresets PresetKindEnum = 0 + UserPresets PresetKindEnum = 1 +) + +func (m *Model) updateDerivedPresetSearch() { + // parse filters from the search string. in: dir, gmdls: yes/no, kind: builtin/user/all + search := strings.TrimSpace(m.d.PresetSearchString) + lower := strings.ToLower(search) + parts := strings.Fields(lower) + // parse parts to see if they contain : + m.derived.presetSearch.noGmDls = false + m.derived.presetSearch.directory = "" + m.derived.presetSearch.kind = AllPresets + for _, part := range parts { + if strings.HasPrefix(part, "in:") && len(part) > 3 { + m.derived.presetSearch.directory = strings.TrimSpace(part[3:]) + } else if strings.HasPrefix(part, "gmdls:") && len(part) > 6 { + val := strings.TrimSpace(part[6:]) + m.derived.presetSearch.noGmDls = val == "no" + } else if strings.HasPrefix(part, "kind:") && len(part) > 5 { + val := strings.TrimSpace(part[5:]) + switch val { + case "builtin": + m.derived.presetSearch.kind = BuiltinPresets + case "user": + m.derived.presetSearch.kind = UserPresets + default: + m.derived.presetSearch.kind = AllPresets + } + } + } +} + +func (m *Model) PresetSearchString() String { return MakeString((*PresetSearchString)(m)) } +func (m *PresetSearchString) Value() string { return m.d.PresetSearchString } +func (m *PresetSearchString) SetValue(value string) bool { + if m.d.PresetSearchString == value { + return false + } + m.d.PresetSearchString = value + (*Model)(m).updateDerivedPresetSearch() + return true +} + +func (m *Model) NoGmDls() Bool { return MakeBool((*NoGmDlsFilter)(m)) } +func (m *NoGmDlsFilter) Value() bool { return m.derived.presetSearch.noGmDls } +func (m *NoGmDlsFilter) SetValue(val bool) { + if m.derived.presetSearch.noGmDls == val { + return + } + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "gmdls:") + if val { + m.d.PresetSearchString = "gmdls:no " + m.d.PresetSearchString + } + (*Model)(m).updateDerivedPresetSearch() +} +func (m *NoGmDlsFilter) Enabled() bool { return true } + +func (m *Model) UserPresetFilter() Bool { return MakeBool((*UserPresetsFilter)(m)) } +func (m *UserPresetsFilter) Value() bool { return m.derived.presetSearch.kind == UserPresets } +func (m *UserPresetsFilter) SetValue(val bool) { + if (m.derived.presetSearch.kind == UserPresets) == val { + return + } + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "kind:") + if val { + m.d.PresetSearchString = "kind:user " + m.d.PresetSearchString + } + (*Model)(m).updateDerivedPresetSearch() +} +func (m *UserPresetsFilter) Enabled() bool { return true } + +func (m *Model) BuiltinPresetsFilter() Bool { return MakeBool((*BuiltinPresetsFilter)(m)) } +func (m *BuiltinPresetsFilter) Value() bool { return m.derived.presetSearch.kind == BuiltinPresets } +func (m *BuiltinPresetsFilter) SetValue(val bool) { + if (m.derived.presetSearch.kind == BuiltinPresets) == val { + return + } + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "kind:") + if val { + m.d.PresetSearchString = "kind:builtin " + m.d.PresetSearchString + } + (*Model)(m).updateDerivedPresetSearch() +} +func (m *BuiltinPresetsFilter) Enabled() bool { return true } + +func (m *Model) PresetKind() Int { return MakeInt((*PresetKind)(m)) } +func (m *PresetKind) Value() int { return int(m.derived.presetSearch.kind) } +func (m *PresetKind) SetValue(val int) bool { + if int(m.derived.presetSearch.kind) == val { + return false + } + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "kind:") + switch PresetKindEnum(val) { + case BuiltinPresets: + m.d.PresetSearchString = "kind:builtin " + m.d.PresetSearchString + case UserPresets: + m.d.PresetSearchString = "kind:user " + m.d.PresetSearchString + } + (*Model)(m).updateDerivedPresetSearch() + return true +} +func (m *PresetKind) Enabled() bool { return true } +func (m *PresetKind) Range() IntRange { return IntRange{Min: -1, Max: 1} } + +func removeFilters(str string, prefix string) string { + parts := strings.Fields(str) + newParts := make([]string, 0, len(parts)) + for _, part := range parts { + if !strings.HasPrefix(strings.ToLower(part), prefix) { + newParts = append(newParts, part) + } + } + return strings.Join(newParts, " ") +} + // gmDlsEntryMap is a reverse map, to find the index of the GmDlsEntry in the // GmDlsEntries list based on the sample offset. Do not modify during runtime. var gmDlsEntryMap = make(map[vm.SampleOffset]int)