From bdd729efc13a00a8aff6da7400e78b064dd7d4a4 Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:41:05 +0300 Subject: [PATCH] drafting --- tracker/gioui/editor.go | 8 +-- tracker/gioui/instrument_presets.go | 69 +++++++++++++++++++++++--- tracker/presets.go | 76 +++++++++++++++++++++-------- 3 files changed, 119 insertions(+), 34 deletions(-) diff --git a/tracker/gioui/editor.go b/tracker/gioui/editor.go index dd7445f..309868d 100644 --- a/tracker/gioui/editor.go +++ b/tracker/gioui/editor.go @@ -66,13 +66,9 @@ 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) - } + 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/instrument_presets.go b/tracker/gioui/instrument_presets.go index d99bcb8..955805e 100644 --- a/tracker/gioui/instrument_presets.go +++ b/tracker/gioui/instrument_presets.go @@ -1,10 +1,15 @@ package gioui import ( + "image" + "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 ( @@ -14,6 +19,7 @@ type ( userPresetsBtn *Clickable builtinPresetsBtn *Clickable clearSearchBtn *Clickable + dirBtn *Clickable dirList *DragList resultList *DragList } @@ -21,13 +27,14 @@ type ( func NewInstrumentPresets(m *tracker.Model) *InstrumentPresets { return &InstrumentPresets{ - searchEditor: NewEditor(false, false, text.Start), + searchEditor: NewEditor(true, true, 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), + dirBtn: new(Clickable), + dirList: NewDragList(m.PresetDirList().List(), layout.Vertical), + resultList: NewDragList(m.PresetDirList().List(), layout.Vertical), } } @@ -37,19 +44,67 @@ func (ip *InstrumentPresets) layout(gtx C) D { 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") + dirElem := func(gtx C, i int) D { + return Label(tr.Theme, &tr.Theme.Dialog.Text, tr.Model.PresetDirList().Value(i)).Layout(gtx) + } + dirs := func(gtx C) D { + return FilledDragList(tr.Theme, ip.dirList).Layout(gtx, dirElem, nil) + } + bottom := func(gtx C) D { + return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, + layout.Rigid(dirs), + layout.Rigid(dirs), + ) + } // layout - return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + return layout.Flex{Axis: layout.Vertical, Alignment: layout.Start, Spacing: 6}.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.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), ) }) }), + layout.Rigid(bottom), + ) +} + +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.Material.ContrastFg) + 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 { + gtx.Constraints.Max.X = min(gtx.Dp(360), gtx.Constraints.Max.X) + return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(icon), + layout.Flexed(1, ed), + layout.Rigid(clr), + ) + } + return layout.Stack{}.Layout(gtx, + layout.Expanded(bg), + layout.Stacked(w), ) } diff --git a/tracker/presets.go b/tracker/presets.go index 29f8117..0300113 100644 --- a/tracker/presets.go +++ b/tracker/presets.go @@ -39,11 +39,15 @@ type ( UserPresetsFilter Model PresetDirectory Model PresetKind Model + ClearPresetSearch Model + PresetDirList Model derivedPresetSearch struct { - directory string - noGmDls bool - kind PresetKindEnum + dirIndex int + noGmDls bool + kind PresetKindEnum + dirs []string + results []int } PresetKindEnum int @@ -61,24 +65,23 @@ func (m *Model) updateDerivedPresetSearch() { lower := strings.ToLower(search) parts := strings.Fields(lower) // parse parts to see if they contain : + m.derived.presetSearch.dirs = []string{"All"} 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:]) + if strings.HasPrefix(part, "d:") && len(part) > 3 { + dir := strings.TrimSpace(part[3:]) + ind := slices.IndexFunc(m.derived.presetSearch.dirs, func(c string) bool { return c == dir }) + m.derived.presetSearch.dirIndex = max(ind, 0) + } else if strings.HasPrefix(part, "g:n") { + m.derived.presetSearch.noGmDls = true + } else if strings.HasPrefix(part, "t:") && len(part) > 2 { + val := strings.TrimSpace(part[2:3]) switch val { - case "builtin": + case "b": m.derived.presetSearch.kind = BuiltinPresets - case "user": + case "u": m.derived.presetSearch.kind = UserPresets - default: - m.derived.presetSearch.kind = AllPresets } } } @@ -101,9 +104,9 @@ func (m *NoGmDlsFilter) SetValue(val bool) { if m.derived.presetSearch.noGmDls == val { return } - m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "gmdls:") + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "g:") if val { - m.d.PresetSearchString = "gmdls:no " + m.d.PresetSearchString + m.d.PresetSearchString = "g:n " + m.d.PresetSearchString } (*Model)(m).updateDerivedPresetSearch() } @@ -115,9 +118,9 @@ func (m *UserPresetsFilter) SetValue(val bool) { if (m.derived.presetSearch.kind == UserPresets) == val { return } - m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "kind:") + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "t:") if val { - m.d.PresetSearchString = "kind:user " + m.d.PresetSearchString + m.d.PresetSearchString = "t:u " + m.d.PresetSearchString } (*Model)(m).updateDerivedPresetSearch() } @@ -129,9 +132,9 @@ func (m *BuiltinPresetsFilter) SetValue(val bool) { if (m.derived.presetSearch.kind == BuiltinPresets) == val { return } - m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "kind:") + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "t:") if val { - m.d.PresetSearchString = "kind:builtin " + m.d.PresetSearchString + m.d.PresetSearchString = "t:b " + m.d.PresetSearchString } (*Model)(m).updateDerivedPresetSearch() } @@ -156,6 +159,37 @@ func (m *PresetKind) SetValue(val int) bool { func (m *PresetKind) Enabled() bool { return true } func (m *PresetKind) Range() IntRange { return IntRange{Min: -1, Max: 1} } +func (m *Model) ClearPresetSearch() Action { return MakeAction((*ClearPresetSearch)(m)) } +func (m *ClearPresetSearch) Enabled() bool { return len(m.d.PresetSearchString) > 0 } +func (m *ClearPresetSearch) Do() { + m.d.PresetSearchString = "" + (*Model)(m).updateDerivedPresetSearch() +} + +func (m *Model) PresetDirList() *PresetDirList { return (*PresetDirList)(m) } +func (v *PresetDirList) List() List { return List{v} } +func (m *PresetDirList) Count() int { return len(m.derived.presetSearch.dirs) } +func (m *PresetDirList) Selected() int { return m.derived.presetSearch.dirIndex } +func (m *PresetDirList) Selected2() int { return m.derived.presetSearch.dirIndex } +func (m *PresetDirList) SetSelected2(i int) {} +func (m *PresetDirList) Value(i int) string { + if i < 0 || i >= len(m.derived.presetSearch.dirs) { + return "" + } + return m.derived.presetSearch.dirs[i] +} +func (m *PresetDirList) SetSelected(i int) { + i = min(max(i, 0), len(m.derived.presetSearch.dirs)-1) + if i < 0 || i >= len(m.derived.presetSearch.dirs) { + return + } + m.d.PresetSearchString = removeFilters(m.d.PresetSearchString, "d:") + if i > 0 { + m.d.PresetSearchString = "d: " + m.derived.presetSearch.dirs[i] + " " + m.d.PresetSearchString + } + (*Model)(m).updateDerivedPresetSearch() +} + func removeFilters(str string, prefix string) string { parts := strings.Fields(str) newParts := make([]string, 0, len(parts))