From 9abb34e57562578293f48d6c638ad7873d88966d Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Tue, 8 Jul 2025 18:56:21 +0300 Subject: [PATCH] drafting --- tracker/action.go | 4 + tracker/gioui/{knob.go => param.go} | 121 +++++++++++++++++++++++++--- tracker/gioui/port.go | 66 --------------- tracker/gioui/signal_rail.go | 12 +-- tracker/gioui/theme.go | 2 +- tracker/gioui/unit_editor.go | 75 ++--------------- tracker/presets/KEYS/clavi.yml | 18 ++--- 7 files changed, 137 insertions(+), 161 deletions(-) rename tracker/gioui/{knob.go => param.go} (71%) delete mode 100644 tracker/gioui/port.go diff --git a/tracker/action.go b/tracker/action.go index 18c2939..9a30337 100644 --- a/tracker/action.go +++ b/tracker/action.go @@ -536,6 +536,10 @@ func (m *Model) ChooseSendSource(id int) Action { } func (s ChooseSendSource) Do() { defer (*Model)(s.Model).change("ChooseSendSource", NoChange, MinorChange)() + if s.Model.d.SendSource == s.ID { + s.Model.d.SendSource = 0 // unselect + return + } s.Model.d.SendSource = s.ID } diff --git a/tracker/gioui/knob.go b/tracker/gioui/param.go similarity index 71% rename from tracker/gioui/knob.go rename to tracker/gioui/param.go index c847268..e8ea94d 100644 --- a/tracker/gioui/knob.go +++ b/tracker/gioui/param.go @@ -22,12 +22,33 @@ import ( ) type ( - KnobState struct { - click gesture.Click + ParamState struct { drag gesture.Drag dragStartPt f32.Point // used to calculate the drag amount dragStartVal int tipArea TipArea + click gesture.Click + clickable Clickable + } + + ParamWidget struct { + Parameter tracker.Parameter + State *ParamState + Theme *Theme + Focus bool + Disabled bool + } + + PortStyle struct { + Diameter unit.Dp + StrokeWidth unit.Dp + Color color.NRGBA + } + + PortWidget struct { + Theme *Theme + Style *PortStyle + State *ParamState } KnobStyle struct { @@ -55,7 +76,7 @@ type ( KnobWidget struct { Theme *Theme Value tracker.Parameter - State *KnobState + State *ParamState Style *KnobStyle Hint string Scroll bool @@ -84,7 +105,7 @@ type ( SwitchWidget struct { Theme *Theme Value tracker.Parameter - State *KnobState + State *ParamState Style *SwitchStyle Hint string Scroll bool @@ -92,9 +113,58 @@ type ( } ) -// KnobState +// ParamState -func (s *KnobState) update(gtx C, param tracker.Parameter, scroll bool) { +func Param(Parameter tracker.Parameter, th *Theme, paramWidget *ParamState, focus, disabled bool) ParamWidget { + return ParamWidget{ + Theme: th, + State: paramWidget, + Parameter: Parameter, + Focus: focus, + Disabled: disabled, + } +} + +func (p ParamWidget) Layout(gtx C) D { + title := Label(p.Theme, &p.Theme.UnitEditor.Name, p.Parameter.Name()) + t := TrackerFromContext(gtx) + widget := func(gtx C) D { + if port, ok := p.Parameter.Port(); t.IsChoosingSendTarget() && ok { + for p.State.clickable.Clicked(gtx) { + t.ChooseSendTarget(p.Parameter.UnitID(), port).Do() + } + k := Port(p.Theme, p.State) + return k.Layout(gtx) + } + switch p.Parameter.Type() { + case tracker.IntegerParameter: + k := Knob(p.Parameter, p.Theme, p.State, p.Parameter.Hint().Label, p.Focus, p.Disabled) + return k.Layout(gtx) + case tracker.BoolParameter: + s := Switch(p.Parameter, p.Theme, p.State, p.Parameter.Hint().Label, p.Focus, p.Disabled) + return s.Layout(gtx) + case tracker.IDParameter: + for p.State.clickable.Clicked(gtx) { + t.ChooseSendSource(p.Parameter.UnitID()).Do() + } + btn := Btn(t.Theme, &t.Theme.Button.Text, &p.State.clickable, "Set", p.Parameter.Hint().Label) + if p.Disabled { + btn.Style = &t.Theme.Button.Disabled + } + return btn.Layout(gtx) + } + if _, ok := p.Parameter.Port(); ok { + k := Port(p.Theme, p.State) + return k.Layout(gtx) + } + return D{} + } + title.Layout(gtx) + layout.Center.Layout(gtx, widget) + return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)} +} + +func (s *ParamState) update(gtx C, param tracker.Parameter, scroll bool) { for { p, ok := s.drag.Update(gtx.Metric, gtx.Source, gesture.Both) if !ok { @@ -140,9 +210,9 @@ func (s *KnobState) update(gtx C, param tracker.Parameter, scroll bool) { } } -// Knob +// KnobWidget -func Knob(v tracker.Parameter, th *Theme, state *KnobState, hint string, scroll, disabled bool) KnobWidget { +func Knob(v tracker.Parameter, th *Theme, state *ParamState, hint string, scroll, disabled bool) KnobWidget { ret := KnobWidget{ Theme: th, Value: v, @@ -251,9 +321,9 @@ func (k *KnobWidget) strokeIndicator(gtx C, amount float32) { paint.FillShape(gtx.Ops, k.Style.Indicator.Color, s.Op(gtx.Ops)) } -// Switch +// SwitchWidget -func Switch(v tracker.Parameter, th *Theme, state *KnobState, hint string, scroll, disabled bool) SwitchWidget { +func Switch(v tracker.Parameter, th *Theme, state *ParamState, hint string, scroll, disabled bool) SwitchWidget { return SwitchWidget{ Theme: th, Value: v, @@ -325,3 +395,34 @@ func (s *SwitchWidget) Layout(gtx C) D { w.Layout(gtx, bg) return D{Size: image.Pt(width, height)} } + +// + +func Port(t *Theme, p *ParamState) PortWidget { + return PortWidget{Theme: t, Style: &t.Port, State: p} +} + +func (p *PortWidget) Layout(gtx C) D { + return p.State.clickable.layout(p.State, gtx, func(gtx C) D { + d := gtx.Dp(p.Style.Diameter) + defer clip.Rect(image.Rectangle{Max: image.Pt(d, d)}).Push(gtx.Ops).Pop() + p.strokeCircle(gtx) + return D{Size: image.Pt(d, d)} + }) +} + +func (p *PortWidget) strokeCircle(gtx C) { + sw := float32(gtx.Dp(p.Style.StrokeWidth)) + d := float32(gtx.Dp(p.Style.Diameter)) + rad := d / 2 + center := f32.Point{X: rad, Y: rad} + var path clip.Path + path.Begin(gtx.Ops) + path.MoveTo(f32.Pt(sw/2, rad)) + path.ArcTo(center, center, float32(math.Pi*2)) + paint.FillShape(gtx.Ops, p.Style.Color, + clip.Stroke{ + Path: path.End(), + Width: sw, + }.Op()) +} diff --git a/tracker/gioui/port.go b/tracker/gioui/port.go deleted file mode 100644 index 36b3b1b..0000000 --- a/tracker/gioui/port.go +++ /dev/null @@ -1,66 +0,0 @@ -package gioui - -import ( - "image" - "image/color" - "math" - - "gioui.org/f32" - "gioui.org/gesture" - "gioui.org/io/event" - "gioui.org/op/clip" - "gioui.org/op/paint" - "gioui.org/unit" -) - -type ( - PortState struct { - click gesture.Click - } - - PortStyle struct { - Diameter unit.Dp - StrokeWidth unit.Dp - Color color.NRGBA - } - - PortWidget struct { - Theme *Theme - Style *PortStyle - State *PortState - } -) - -func Port(t *Theme, p *PortState) PortWidget { - return PortWidget{Theme: t, Style: &t.Port, State: p} -} - -func (p *PortWidget) Layout(gtx C) D { - d := gtx.Dp(p.Style.Diameter) - defer clip.Rect(image.Rectangle{Max: image.Pt(d, d)}).Push(gtx.Ops).Pop() - event.Op(gtx.Ops, p.State) - p.State.click.Add(gtx.Ops) - p.strokeCircle(gtx) - return D{Size: image.Pt(d, d)} -} - -func (p *PortState) Clicked(gtx C) bool { - ev, ok := p.click.Update(gtx.Source) - return ok && ev.Kind == gesture.KindClick -} - -func (p *PortWidget) strokeCircle(gtx C) { - sw := float32(gtx.Dp(p.Style.StrokeWidth)) - d := float32(gtx.Dp(p.Style.Diameter)) - rad := d / 2 - center := f32.Point{X: rad, Y: rad} - var path clip.Path - path.Begin(gtx.Ops) - path.MoveTo(f32.Pt(sw/2, rad)) - path.ArcTo(center, center, float32(math.Pi*2)) - paint.FillShape(gtx.Ops, p.Style.Color, - clip.Stroke{ - Path: path.End(), - Width: sw, - }.Op()) -} diff --git a/tracker/gioui/signal_rail.go b/tracker/gioui/signal_rail.go index a2d8315..a91f149 100644 --- a/tracker/gioui/signal_rail.go +++ b/tracker/gioui/signal_rail.go @@ -15,7 +15,7 @@ import ( const maxSignalsDrawn = 16 type ( - SignalRailStyle struct { + RailStyle struct { Color color.NRGBA LineWidth unit.Dp SignalWidth unit.Dp @@ -23,22 +23,22 @@ type ( PortColor color.NRGBA } - SignalRailWidget struct { - Style *SignalRailStyle + RailWidget struct { + Style *RailStyle Signal tracker.Rail Height unit.Dp } ) -func SignalRail(th *Theme, signal tracker.Rail) SignalRailWidget { - return SignalRailWidget{ +func Rail(th *Theme, signal tracker.Rail) RailWidget { + return RailWidget{ Style: &th.SignalRail, Signal: signal, Height: th.UnitEditor.Height, } } -func (s SignalRailWidget) Layout(gtx C) D { +func (s RailWidget) Layout(gtx C) D { sw := gtx.Dp(s.Style.SignalWidth) h := gtx.Dp(s.Height) if s.Signal.PassThrough == 0 && len(s.Signal.StackUse.Inputs) == 0 && s.Signal.StackUse.NumOutputs == 0 { diff --git a/tracker/gioui/theme.go b/tracker/gioui/theme.go index 9f8a21e..4b5f788 100644 --- a/tracker/gioui/theme.go +++ b/tracker/gioui/theme.go @@ -118,7 +118,7 @@ type Theme struct { Knob KnobStyle DisabledKnob KnobStyle Switch SwitchStyle - SignalRail SignalRailStyle + SignalRail RailStyle Port PortStyle // iconCache is used to cache the icons created from iconvg data diff --git a/tracker/gioui/unit_editor.go b/tracker/gioui/unit_editor.go index 7e3f73f..25067ec 100644 --- a/tracker/gioui/unit_editor.go +++ b/tracker/gioui/unit_editor.go @@ -26,7 +26,7 @@ type ( UnitEditor struct { paramTable *ScrollTable searchList *DragList - Parameters [][]*ParameterState + Parameters [][]*ParamState DeleteUnitBtn *Clickable CopyUnitBtn *Clickable ClearUnitBtn *Clickable @@ -140,7 +140,7 @@ func (pe *UnitEditor) update(gtx C, t *Tracker) { } c := t.Model.Params().Cursor() if c.X >= 0 && c.Y >= 0 && c.Y < len(pe.Parameters) && c.X < len(pe.Parameters[c.Y]) { - ta := &pe.Parameters[c.Y][c.X].knobState.tipArea + ta := &pe.Parameters[c.Y][c.X].tipArea ta.Appear(gtx.Now) ta.Exit.SetTarget(gtx.Now.Add(ta.ExitDuration)) } @@ -188,7 +188,7 @@ func (pe *UnitEditor) layoutRack(gtx C) D { // create enough parameter widget to match the number of parameters width := pe.paramTable.Table.Width() for len(pe.Parameters) < pe.paramTable.Table.Height() { - pe.Parameters = append(pe.Parameters, make([]*ParameterState, 0)) + pe.Parameters = append(pe.Parameters, make([]*ParamState, 0)) } cellWidth := gtx.Dp(t.Theme.UnitEditor.Width) cellHeight := gtx.Dp(t.Theme.UnitEditor.Height) @@ -199,7 +199,7 @@ func (pe *UnitEditor) layoutRack(gtx C) D { columnTitleHeight := gtx.Dp(0) for i := range pe.Parameters { for len(pe.Parameters[i]) < width { - pe.Parameters[i] = append(pe.Parameters[i], &ParameterState{knobState: KnobState{tipArea: TipArea{ExitDuration: time.Second * 2}}}) + pe.Parameters[i] = append(pe.Parameters[i], &ParamState{tipArea: TipArea{ExitDuration: time.Second * 2}}) } } coltitle := func(gtx C, x int) D { @@ -210,7 +210,7 @@ func (pe *UnitEditor) layoutRack(gtx C) D { return D{} } item := t.Units().Item(y) - sr := SignalRail(t.Theme, item.Signals) + sr := Rail(t.Theme, item.Signals) label := Label(t.Theme, &t.Theme.UnitEditor.UnitList.Name, item.Type) switch { case item.Disabled: @@ -248,7 +248,7 @@ func (pe *UnitEditor) layoutRack(gtx C) D { } param := t.Model.Params().Item(point) - paramStyle := t.ParamStyle(param, t.Theme, pe.Parameters[y][x], pe.paramTable.Table.Cursor() == point, t.Units().Item(y).Disabled) + paramStyle := Param(param, t.Theme, pe.Parameters[y][x], pe.paramTable.Table.Cursor() == point, t.Units().Item(y).Disabled) paramStyle.Layout(gtx) comment := t.Units().Item(y).Comment if comment != "" && x == t.Model.Params().RowWidth(y) { @@ -443,66 +443,3 @@ func (t *UnitEditor) Tags(level int, yield TagYieldFunc) bool { } return yield(level+1, t.paramTable.RowTitleList) && yield(level, t.paramTable) && yield(level+1, &t.commentEditor.widgetEditor) } - -type ParameterState struct { - knobState KnobState - clickable Clickable - portState PortState -} - -type ParameterStyle struct { - Parameter tracker.Parameter - State *ParameterState - Theme *Theme - Focus bool - Disabled bool -} - -func (t *Tracker) ParamStyle(Parameter tracker.Parameter, th *Theme, paramWidget *ParameterState, focus, disabled bool) ParameterStyle { - return ParameterStyle{ - Theme: th, - State: paramWidget, - Parameter: Parameter, - Focus: focus, - Disabled: disabled, - } -} - -func (p ParameterStyle) Layout(gtx C) D { - title := Label(p.Theme, &p.Theme.UnitEditor.Name, p.Parameter.Name()) - t := TrackerFromContext(gtx) - widget := func(gtx C) D { - if port, ok := p.Parameter.Port(); t.IsChoosingSendTarget() && ok { - for p.State.portState.Clicked(gtx) { - t.ChooseSendTarget(p.Parameter.UnitID(), port).Do() - } - k := Port(p.Theme, &p.State.portState) - return k.Layout(gtx) - } - switch p.Parameter.Type() { - case tracker.IntegerParameter: - k := Knob(p.Parameter, p.Theme, &p.State.knobState, p.Parameter.Hint().Label, p.Focus, p.Disabled) - return k.Layout(gtx) - case tracker.BoolParameter: - s := Switch(p.Parameter, p.Theme, &p.State.knobState, p.Parameter.Hint().Label, p.Focus, p.Disabled) - return s.Layout(gtx) - case tracker.IDParameter: - for p.State.clickable.Clicked(gtx) { - t.ChooseSendSource(p.Parameter.UnitID()).Do() - } - btn := Btn(t.Theme, &t.Theme.Button.Text, &p.State.clickable, "Set", p.Parameter.Hint().Label) - if p.Disabled { - btn.Style = &t.Theme.Button.Disabled - } - return btn.Layout(gtx) - } - if _, ok := p.Parameter.Port(); ok { - k := Port(p.Theme, &p.State.portState) - return k.Layout(gtx) - } - return D{} - } - title.Layout(gtx) - layout.Center.Layout(gtx, widget) - return D{Size: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y)} -} diff --git a/tracker/presets/KEYS/clavi.yml b/tracker/presets/KEYS/clavi.yml index 834cfa6..bb356dc 100644 --- a/tracker/presets/KEYS/clavi.yml +++ b/tracker/presets/KEYS/clavi.yml @@ -1,21 +1,21 @@ -name: Clavi +name: clavi numvoices: 1 units: - type: envelope - id: 1 + id: 225 parameters: {attack: 0, decay: 62, gain: 77, release: 52, stereo: 1, sustain: 66} - type: oscillator - id: 2 + id: 226 parameters: {color: 64, detune: 68, gain: 67, looplength: 84, loopstart: 290, phase: 0, samplestart: 401297, shape: 90, stereo: 1, transpose: 76, type: 4, unison: 3} - type: mulp - id: 3 + id: 227 parameters: {stereo: 1} - type: pan - id: 5 - parameters: {damp: 0, dry: 128, feedback: 96, notetracking: 2, panning: 64, pregain: 40, stereo: 1} + id: 228 + parameters: {panning: 64, stereo: 1} - type: filter id: 1058 - parameters: {bandpass: -1, frequency: 96, highpass: -1, lowpass: 1, panning: 64, resonance: 128, stereo: 1} + parameters: {bandpass: -1, frequency: 96, highpass: -1, lowpass: 1, resonance: 128, stereo: 1} - type: outaux - id: 6 - parameters: {auxgain: 15, outgain: 54, panning: 64, stereo: 1} + id: 1059 + parameters: {auxgain: 15, outgain: 54, stereo: 1}