mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-19 05:24:48 -04:00
refactor(tracker/gioui): bind tracker.Int to NumericUpDown on Layout
This commit is contained in:
parent
289bfb0605
commit
beef8fe1e0
@ -144,9 +144,9 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
||||
|
||||
octave := func(gtx C) D {
|
||||
in := layout.UniformInset(unit.Dp(1))
|
||||
numStyle := NumUpDown(t.Theme, t.OctaveNumberInput, ie.octaveHint)
|
||||
dims := in.Layout(gtx, numStyle.Layout)
|
||||
return dims
|
||||
return in.Layout(gtx, func(gtx C) D {
|
||||
return t.OctaveNumberInput.Layout(gtx, t.Octave(), t.Theme, &t.Theme.NumericUpDown, "Octave")
|
||||
})
|
||||
}
|
||||
|
||||
newBtnStyle := ActionIcon(gtx, t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint)
|
||||
@ -231,9 +231,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
layout.Rigid(Label(t.Theme, &t.Theme.InstrumentEditor.Voices, "Voices").Layout),
|
||||
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
numStyle := NumUpDown(t.Theme, t.InstrumentVoices, "Number of voices for this instrument")
|
||||
dims := numStyle.Layout(gtx)
|
||||
return dims
|
||||
return t.InstrumentVoices.Layout(gtx, t.Model.InstrumentVoices(), t.Theme, &t.Theme.NumericUpDown, "Number of voices for this instrument")
|
||||
}),
|
||||
layout.Rigid(splitInstrumentBtnStyle.Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
|
||||
|
@ -293,7 +293,7 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) {
|
||||
break
|
||||
}
|
||||
instr := t.InstrumentEditor.instrumentDragList.TrackerList.Selected()
|
||||
n := noteAsValue(t.OctaveNumberInput.Int.Value(), val-12)
|
||||
n := noteAsValue(t.Model.Octave().Value(), val-12)
|
||||
t.KeyNoteMap.Press(e.Name, tracker.NoteEvent{Channel: instr, Note: n})
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func init() {
|
||||
}
|
||||
|
||||
type NoteEditor struct {
|
||||
TrackVoices *NumberInput
|
||||
TrackVoices *NumericUpDown
|
||||
NewTrackBtn *ActionClickable
|
||||
DeleteTrackBtn *ActionClickable
|
||||
SplitTrackBtn *ActionClickable
|
||||
@ -76,7 +76,7 @@ type NoteEditor struct {
|
||||
|
||||
func NewNoteEditor(model *tracker.Model) *NoteEditor {
|
||||
ret := &NoteEditor{
|
||||
TrackVoices: NewNumberInput(model.TrackVoices()),
|
||||
TrackVoices: NewNumericUpDown(),
|
||||
NewTrackBtn: NewActionClickable(model.AddTrack()),
|
||||
DeleteTrackBtn: NewActionClickable(model.DeleteTrack()),
|
||||
SplitTrackBtn: NewActionClickable(model.SplitTrack()),
|
||||
@ -174,8 +174,9 @@ func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
|
||||
newTrackBtnStyle := ActionIcon(gtx, t.Theme, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint)
|
||||
in := layout.UniformInset(unit.Dp(1))
|
||||
voiceUpDown := func(gtx C) D {
|
||||
numStyle := NumUpDown(t.Theme, te.TrackVoices, "Track voices")
|
||||
return in.Layout(gtx, numStyle.Layout)
|
||||
return in.Layout(gtx, func(gtx C) D {
|
||||
return te.TrackVoices.Layout(gtx, t.Model.TrackVoices(), t.Theme, &t.Theme.NumericUpDown, "Track voices")
|
||||
})
|
||||
}
|
||||
effectBtnStyle := ToggleButton(gtx, t.Theme, te.EffectBtn, "Hex")
|
||||
uniqueBtnStyle := ToggleIcon(gtx, t.Theme, te.UniqueBtn, icons.ToggleStarBorder, icons.ToggleStar, te.uniqueOffTip, te.uniqueOnTip)
|
||||
@ -390,7 +391,7 @@ func (te *NoteEditor) command(t *Tracker, e key.Event) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = noteAsValue(t.OctaveNumberInput.Int.Value(), val-12)
|
||||
n = noteAsValue(t.Octave().Value(), val-12)
|
||||
ev := t.Model.Notes().Input(n)
|
||||
t.KeyNoteMap.Press(e.Name, ev)
|
||||
}
|
||||
|
@ -23,8 +23,9 @@ import (
|
||||
"gioui.org/text"
|
||||
)
|
||||
|
||||
type NumberInput struct {
|
||||
Int tracker.Int
|
||||
type NumericUpDown struct {
|
||||
DpPerStep unit.Dp
|
||||
|
||||
dragStartValue int
|
||||
dragStartXY float32
|
||||
clickDecrease gesture.Click
|
||||
@ -41,36 +42,19 @@ type NumericUpDownStyle struct {
|
||||
Width unit.Dp
|
||||
Height unit.Dp
|
||||
TextSize unit.Sp
|
||||
DpPerStep unit.Dp
|
||||
Font font.Font
|
||||
}
|
||||
|
||||
type NumericUpDown struct {
|
||||
NumberInput *NumberInput
|
||||
Tooltip component.Tooltip
|
||||
Theme *Theme
|
||||
Font font.Font
|
||||
NumericUpDownStyle
|
||||
func NewNumericUpDown() *NumericUpDown {
|
||||
return &NumericUpDown{DpPerStep: unit.Dp(8)}
|
||||
}
|
||||
|
||||
func NewNumberInput(v tracker.Int) *NumberInput {
|
||||
return &NumberInput{Int: v}
|
||||
}
|
||||
|
||||
func NumUpDown(th *Theme, number *NumberInput, tooltip string) NumericUpDown {
|
||||
return NumericUpDown{
|
||||
NumberInput: number,
|
||||
Theme: th,
|
||||
Tooltip: Tooltip(th, tooltip),
|
||||
NumericUpDownStyle: th.NumericUpDown,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NumericUpDown) Update(gtx layout.Context) {
|
||||
func (s *NumericUpDown) Update(gtx layout.Context, v tracker.Int) {
|
||||
// handle dragging
|
||||
pxPerStep := float32(gtx.Dp(s.DpPerStep))
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: s.NumberInput,
|
||||
Target: s,
|
||||
Kinds: pointer.Press | pointer.Drag | pointer.Release,
|
||||
})
|
||||
if !ok {
|
||||
@ -79,46 +63,54 @@ func (s *NumericUpDown) Update(gtx layout.Context) {
|
||||
if e, ok := ev.(pointer.Event); ok {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
s.NumberInput.dragStartValue = s.NumberInput.Int.Value()
|
||||
s.NumberInput.dragStartXY = e.Position.X - e.Position.Y
|
||||
s.dragStartValue = v.Value()
|
||||
s.dragStartXY = e.Position.X - e.Position.Y
|
||||
case pointer.Drag:
|
||||
var deltaCoord float32
|
||||
deltaCoord = e.Position.X - e.Position.Y - s.NumberInput.dragStartXY
|
||||
s.NumberInput.Int.SetValue(s.NumberInput.dragStartValue + int(deltaCoord/pxPerStep+0.5))
|
||||
deltaCoord = e.Position.X - e.Position.Y - s.dragStartXY
|
||||
v.SetValue(s.dragStartValue + int(deltaCoord/pxPerStep+0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
// handle decrease clicks
|
||||
for ev, ok := s.NumberInput.clickDecrease.Update(gtx.Source); ok; ev, ok = s.NumberInput.clickDecrease.Update(gtx.Source) {
|
||||
for ev, ok := s.clickDecrease.Update(gtx.Source); ok; ev, ok = s.clickDecrease.Update(gtx.Source) {
|
||||
if ev.Kind == gesture.KindClick {
|
||||
s.NumberInput.Int.Add(-1)
|
||||
v.Add(-1)
|
||||
}
|
||||
}
|
||||
// handle increase clicks
|
||||
for ev, ok := s.NumberInput.clickIncrease.Update(gtx.Source); ok; ev, ok = s.NumberInput.clickIncrease.Update(gtx.Source) {
|
||||
for ev, ok := s.clickIncrease.Update(gtx.Source); ok; ev, ok = s.clickIncrease.Update(gtx.Source) {
|
||||
if ev.Kind == gesture.KindClick {
|
||||
s.NumberInput.Int.Add(1)
|
||||
v.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s NumericUpDown) Layout(gtx C) D {
|
||||
if s.Tooltip.Text.Text != "" {
|
||||
return s.NumberInput.tipArea.Layout(gtx, s.Tooltip, s.actualLayout)
|
||||
func (s *NumericUpDown) Widget(v tracker.Int, th *Theme, st *NumericUpDownStyle, tooltip string) func(gtx C) D {
|
||||
return func(gtx C) D {
|
||||
return s.Layout(gtx, v, th, st, tooltip)
|
||||
}
|
||||
return s.actualLayout(gtx)
|
||||
}
|
||||
|
||||
func (s *NumericUpDown) actualLayout(gtx C) D {
|
||||
s.Update(gtx)
|
||||
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(s.Width), gtx.Dp(s.Height)))
|
||||
width := gtx.Dp(s.ButtonWidth)
|
||||
height := gtx.Dp(s.Height)
|
||||
func (s *NumericUpDown) Layout(gtx C, v tracker.Int, th *Theme, st *NumericUpDownStyle, tooltip string) D {
|
||||
s.Update(gtx, v)
|
||||
if tooltip != "" {
|
||||
return s.tipArea.Layout(gtx, Tooltip(th, tooltip), func(gtx C) D {
|
||||
return s.actualLayout(gtx, v, th, st)
|
||||
})
|
||||
}
|
||||
return s.actualLayout(gtx, v, th, st)
|
||||
}
|
||||
|
||||
func (s *NumericUpDown) actualLayout(gtx C, v tracker.Int, th *Theme, st *NumericUpDownStyle) D {
|
||||
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(st.Width), gtx.Dp(st.Height)))
|
||||
width := gtx.Dp(st.ButtonWidth)
|
||||
height := gtx.Dp(st.Height)
|
||||
return layout.Background{}.Layout(gtx,
|
||||
func(gtx C) D {
|
||||
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(s.CornerRadius)).Push(gtx.Ops).Pop()
|
||||
paint.Fill(gtx.Ops, s.BgColor)
|
||||
event.Op(gtx.Ops, s.NumberInput) // register drag inputs, if not hitting the clicks
|
||||
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(st.CornerRadius)).Push(gtx.Ops).Pop()
|
||||
paint.Fill(gtx.Ops, st.BgColor)
|
||||
event.Op(gtx.Ops, s) // register drag inputs, if not hitting the clicks
|
||||
return D{Size: gtx.Constraints.Min}
|
||||
},
|
||||
func(gtx C) D {
|
||||
@ -128,25 +120,25 @@ func (s *NumericUpDown) actualLayout(gtx C) D {
|
||||
return layout.Background{}.Layout(gtx,
|
||||
func(gtx C) D {
|
||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Push(gtx.Ops).Pop()
|
||||
s.NumberInput.clickDecrease.Add(gtx.Ops)
|
||||
s.clickDecrease.Add(gtx.Ops)
|
||||
return D{Size: gtx.Constraints.Min}
|
||||
},
|
||||
func(gtx C) D { return s.Theme.Icon(icons.ContentRemove).Layout(gtx, s.IconColor) },
|
||||
func(gtx C) D { return th.Icon(icons.ContentRemove).Layout(gtx, st.IconColor) },
|
||||
)
|
||||
}),
|
||||
layout.Flexed(1, func(gtx C) D {
|
||||
paint.ColorOp{Color: s.TextColor}.Add(gtx.Ops)
|
||||
return widget.Label{Alignment: text.Middle}.Layout(gtx, s.Theme.Material.Shaper, s.Font, s.TextSize, strconv.Itoa(s.NumberInput.Int.Value()), op.CallOp{})
|
||||
paint.ColorOp{Color: st.TextColor}.Add(gtx.Ops)
|
||||
return widget.Label{Alignment: text.Middle}.Layout(gtx, th.Material.Shaper, st.Font, st.TextSize, strconv.Itoa(v.Value()), op.CallOp{})
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
gtx.Constraints = layout.Exact(image.Pt(width, height))
|
||||
return layout.Background{}.Layout(gtx,
|
||||
func(gtx C) D {
|
||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Push(gtx.Ops).Pop()
|
||||
s.NumberInput.clickIncrease.Add(gtx.Ops)
|
||||
s.clickIncrease.Add(gtx.Ops)
|
||||
return D{Size: gtx.Constraints.Min}
|
||||
},
|
||||
func(gtx C) D { return s.Theme.Icon(icons.ContentAdd).Layout(gtx, s.IconColor) },
|
||||
func(gtx C) D { return th.Icon(icons.ContentAdd).Layout(gtx, st.IconColor) },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
@ -19,8 +19,8 @@ type (
|
||||
OscilloscopeState struct {
|
||||
onceBtn *BoolClickable
|
||||
wrapBtn *BoolClickable
|
||||
lengthInBeatsNumber *NumberInput
|
||||
triggerChannelNumber *NumberInput
|
||||
lengthInBeatsNumber *NumericUpDown
|
||||
triggerChannelNumber *NumericUpDown
|
||||
xScale int
|
||||
xOffset float32
|
||||
yScale float64
|
||||
@ -34,98 +34,89 @@ type (
|
||||
LimitColor color.NRGBA `yaml:",flow"`
|
||||
CursorColor color.NRGBA `yaml:",flow"`
|
||||
}
|
||||
|
||||
Oscilloscope struct {
|
||||
State *OscilloscopeState
|
||||
Wave tracker.RingBuffer[[2]float32]
|
||||
Theme *Theme
|
||||
OscilloscopeStyle
|
||||
}
|
||||
)
|
||||
|
||||
func NewOscilloscope(model *tracker.Model) *OscilloscopeState {
|
||||
return &OscilloscopeState{
|
||||
onceBtn: NewBoolClickable(model.SignalAnalyzer().Once()),
|
||||
wrapBtn: NewBoolClickable(model.SignalAnalyzer().Wrap()),
|
||||
lengthInBeatsNumber: NewNumberInput(model.SignalAnalyzer().LengthInBeats()),
|
||||
triggerChannelNumber: NewNumberInput(model.SignalAnalyzer().TriggerChannel()),
|
||||
lengthInBeatsNumber: NewNumericUpDown(),
|
||||
triggerChannelNumber: NewNumericUpDown(),
|
||||
}
|
||||
}
|
||||
|
||||
func Scope(s *OscilloscopeState, wave tracker.RingBuffer[[2]float32], th *Theme) *Oscilloscope {
|
||||
return &Oscilloscope{State: s, Wave: wave, Theme: th}
|
||||
}
|
||||
|
||||
func (s *Oscilloscope) Layout(gtx C) D {
|
||||
wrapBtnStyle := ToggleButton(gtx, s.Theme, s.State.wrapBtn, "Wrap")
|
||||
onceBtnStyle := ToggleButton(gtx, s.Theme, s.State.onceBtn, "Once")
|
||||
triggerChannelStyle := NumUpDown(s.Theme, s.State.triggerChannelNumber, "Trigger channel")
|
||||
lengthNumberStyle := NumUpDown(s.Theme, s.State.lengthInBeatsNumber, "Buffer length in beats")
|
||||
func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, wave tracker.RingBuffer[[2]float32], th *Theme, st *OscilloscopeStyle) D {
|
||||
wrapBtnStyle := ToggleButton(gtx, th, s.wrapBtn, "Wrap")
|
||||
onceBtnStyle := ToggleButton(gtx, th, s.onceBtn, "Once")
|
||||
|
||||
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
||||
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
|
||||
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx C) D { return s.layoutWave(gtx) }),
|
||||
layout.Flexed(1, func(gtx C) D { return s.layoutWave(gtx, wave, th) }),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(leftSpacer),
|
||||
layout.Rigid(Label(s.Theme, &s.Theme.SongPanel.RowHeader, "Trigger").Layout),
|
||||
layout.Rigid(Label(th, &th.SongPanel.RowHeader, "Trigger").Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
||||
layout.Rigid(onceBtnStyle.Layout),
|
||||
layout.Rigid(triggerChannelStyle.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return s.triggerChannelNumber.Layout(gtx, vtrig, th, &th.NumericUpDown, "Trigger channel")
|
||||
}),
|
||||
layout.Rigid(rightSpacer),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(leftSpacer),
|
||||
layout.Rigid(Label(s.Theme, &s.Theme.SongPanel.RowHeader, "Buffer").Layout),
|
||||
layout.Rigid(Label(th, &th.SongPanel.RowHeader, "Buffer").Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
||||
layout.Rigid(wrapBtnStyle.Layout),
|
||||
layout.Rigid(lengthNumberStyle.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return s.lengthInBeatsNumber.Layout(gtx, vlen, th, &th.NumericUpDown, "Buffer length in beats")
|
||||
}),
|
||||
layout.Rigid(rightSpacer),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Oscilloscope) layoutWave(gtx C) D {
|
||||
s.update(gtx)
|
||||
func (s *OscilloscopeState) layoutWave(gtx C, wave tracker.RingBuffer[[2]float32], th *Theme) D {
|
||||
s.update(gtx, wave)
|
||||
if gtx.Constraints.Max.X == 0 || gtx.Constraints.Max.Y == 0 {
|
||||
return D{}
|
||||
}
|
||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
||||
event.Op(gtx.Ops, s.State)
|
||||
paint.ColorOp{Color: s.Theme.Oscilloscope.CursorColor}.Add(gtx.Ops)
|
||||
cursorX := int(s.sampleToPx(gtx, float32(s.Wave.Cursor)))
|
||||
event.Op(gtx.Ops, s)
|
||||
paint.ColorOp{Color: th.Oscilloscope.CursorColor}.Add(gtx.Ops)
|
||||
cursorX := int(s.sampleToPx(gtx, float32(wave.Cursor), wave))
|
||||
fillRect(gtx, clip.Rect{Min: image.Pt(cursorX, 0), Max: image.Pt(cursorX+1, gtx.Constraints.Max.Y)})
|
||||
paint.ColorOp{Color: s.Theme.Oscilloscope.LimitColor}.Add(gtx.Ops)
|
||||
paint.ColorOp{Color: th.Oscilloscope.LimitColor}.Add(gtx.Ops)
|
||||
minusOneY := int(s.ampToY(gtx, -1))
|
||||
fillRect(gtx, clip.Rect{Min: image.Pt(0, minusOneY), Max: image.Pt(gtx.Constraints.Max.X, minusOneY+1)})
|
||||
plusOneY := int(s.ampToY(gtx, 1))
|
||||
fillRect(gtx, clip.Rect{Min: image.Pt(0, plusOneY), Max: image.Pt(gtx.Constraints.Max.X, plusOneY+1)})
|
||||
leftX := int(s.sampleToPx(gtx, 0))
|
||||
leftX := int(s.sampleToPx(gtx, 0, wave))
|
||||
fillRect(gtx, clip.Rect{Min: image.Pt(leftX, 0), Max: image.Pt(leftX+1, gtx.Constraints.Max.Y)})
|
||||
rightX := int(s.sampleToPx(gtx, float32(len(s.Wave.Buffer)-1)))
|
||||
rightX := int(s.sampleToPx(gtx, float32(len(wave.Buffer)-1), wave))
|
||||
fillRect(gtx, clip.Rect{Min: image.Pt(rightX, 0), Max: image.Pt(rightX+1, gtx.Constraints.Max.Y)})
|
||||
for chn := range 2 {
|
||||
paint.ColorOp{Color: s.Theme.Oscilloscope.CurveColors[chn]}.Add(gtx.Ops)
|
||||
paint.ColorOp{Color: th.Oscilloscope.CurveColors[chn]}.Add(gtx.Ops)
|
||||
for px := range gtx.Constraints.Max.X {
|
||||
// left and right is the sample range covered by the pixel
|
||||
left := int(s.pxToSample(gtx, float32(px)-0.5))
|
||||
right := int(s.pxToSample(gtx, float32(px)+0.5))
|
||||
if right < 0 || left >= len(s.Wave.Buffer) {
|
||||
left := int(s.pxToSample(gtx, float32(px)-0.5, wave))
|
||||
right := int(s.pxToSample(gtx, float32(px)+0.5, wave))
|
||||
if right < 0 || left >= len(wave.Buffer) {
|
||||
continue
|
||||
}
|
||||
right = min(right, len(s.Wave.Buffer)-1)
|
||||
right = min(right, len(wave.Buffer)-1)
|
||||
left = max(left, 0)
|
||||
// smin and smax are the smallest and largest sample values in the pixel range
|
||||
smax := float32(math.Inf(-1))
|
||||
smin := float32(math.Inf(1))
|
||||
for x := left; x <= right; x++ {
|
||||
smax = max(smax, s.Wave.Buffer[x][chn])
|
||||
smin = min(smin, s.Wave.Buffer[x][chn])
|
||||
smax = max(smax, wave.Buffer[x][chn])
|
||||
smin = min(smin, wave.Buffer[x][chn])
|
||||
}
|
||||
// y1 and y2 are the pixel range covered by the sample value
|
||||
y1 := min(max(int(s.ampToY(gtx, smax)+0.5), 0), gtx.Constraints.Max.Y-1)
|
||||
@ -142,10 +133,10 @@ func fillRect(gtx C, rect clip.Rect) {
|
||||
stack.Pop()
|
||||
}
|
||||
|
||||
func (o *Oscilloscope) update(gtx C) {
|
||||
func (o *OscilloscopeState) update(gtx C, wave tracker.RingBuffer[[2]float32]) {
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: o.State,
|
||||
Target: o,
|
||||
Kinds: pointer.Scroll | pointer.Press | pointer.Drag | pointer.Release | pointer.Cancel,
|
||||
ScrollY: pointer.ScrollRange{Min: -1e6, Max: 1e6},
|
||||
})
|
||||
@ -155,59 +146,59 @@ func (o *Oscilloscope) update(gtx C) {
|
||||
if e, ok := ev.(pointer.Event); ok {
|
||||
switch e.Kind {
|
||||
case pointer.Scroll:
|
||||
s1 := o.pxToSample(gtx, e.Position.X)
|
||||
o.State.xScale += min(max(-1, int(e.Scroll.Y)), 1)
|
||||
s2 := o.pxToSample(gtx, e.Position.X)
|
||||
o.State.xOffset -= s1 - s2
|
||||
s1 := o.pxToSample(gtx, e.Position.X, wave)
|
||||
o.xScale += min(max(-1, int(e.Scroll.Y)), 1)
|
||||
s2 := o.pxToSample(gtx, e.Position.X, wave)
|
||||
o.xOffset -= s1 - s2
|
||||
case pointer.Press:
|
||||
if e.Buttons&pointer.ButtonSecondary != 0 {
|
||||
o.State.xOffset = 0
|
||||
o.State.xScale = 0
|
||||
o.State.yScale = 0
|
||||
o.xOffset = 0
|
||||
o.xScale = 0
|
||||
o.yScale = 0
|
||||
}
|
||||
if e.Buttons&pointer.ButtonPrimary != 0 {
|
||||
o.State.dragging = true
|
||||
o.State.dragId = e.PointerID
|
||||
o.State.dragStartPoint = e.Position
|
||||
o.dragging = true
|
||||
o.dragId = e.PointerID
|
||||
o.dragStartPoint = e.Position
|
||||
}
|
||||
case pointer.Drag:
|
||||
if e.Buttons&pointer.ButtonPrimary != 0 && o.State.dragging && e.PointerID == o.State.dragId {
|
||||
deltaX := o.pxToSample(gtx, e.Position.X) - o.pxToSample(gtx, o.State.dragStartPoint.X)
|
||||
o.State.xOffset += deltaX
|
||||
if e.Buttons&pointer.ButtonPrimary != 0 && o.dragging && e.PointerID == o.dragId {
|
||||
deltaX := o.pxToSample(gtx, e.Position.X, wave) - o.pxToSample(gtx, o.dragStartPoint.X, wave)
|
||||
o.xOffset += deltaX
|
||||
num := o.yToAmp(gtx, e.Position.Y)
|
||||
den := o.yToAmp(gtx, o.State.dragStartPoint.Y)
|
||||
den := o.yToAmp(gtx, o.dragStartPoint.Y)
|
||||
if l := math.Abs(float64(num / den)); l > 1e-3 && l < 1e3 {
|
||||
o.State.yScale += math.Log(l)
|
||||
o.State.yScale = min(max(o.State.yScale, -1e3), 1e3)
|
||||
o.yScale += math.Log(l)
|
||||
o.yScale = min(max(o.yScale, -1e3), 1e3)
|
||||
}
|
||||
o.State.dragStartPoint = e.Position
|
||||
o.dragStartPoint = e.Position
|
||||
|
||||
}
|
||||
case pointer.Release | pointer.Cancel:
|
||||
o.State.dragging = false
|
||||
o.dragging = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Oscilloscope) scaleFactor() float32 {
|
||||
return float32(math.Pow(1.1, float64(o.State.xScale)))
|
||||
func (o *OscilloscopeState) scaleFactor() float32 {
|
||||
return float32(math.Pow(1.1, float64(o.xScale)))
|
||||
}
|
||||
|
||||
func (s *Oscilloscope) pxToSample(gtx C, px float32) float32 {
|
||||
return px*s.scaleFactor()*float32(len(s.Wave.Buffer))/float32(gtx.Constraints.Max.X) - s.State.xOffset
|
||||
func (s *OscilloscopeState) pxToSample(gtx C, px float32, wave tracker.RingBuffer[[2]float32]) float32 {
|
||||
return px*s.scaleFactor()*float32(len(wave.Buffer))/float32(gtx.Constraints.Max.X) - s.xOffset
|
||||
}
|
||||
|
||||
func (s *Oscilloscope) sampleToPx(gtx C, sample float32) float32 {
|
||||
return (sample + s.State.xOffset) * float32(gtx.Constraints.Max.X) / float32(len(s.Wave.Buffer)) / s.scaleFactor()
|
||||
func (s *OscilloscopeState) sampleToPx(gtx C, sample float32, wave tracker.RingBuffer[[2]float32]) float32 {
|
||||
return (sample + s.xOffset) * float32(gtx.Constraints.Max.X) / float32(len(wave.Buffer)) / s.scaleFactor()
|
||||
}
|
||||
|
||||
func (s *Oscilloscope) ampToY(gtx C, amp float32) float32 {
|
||||
scale := float32(math.Exp(s.State.yScale))
|
||||
func (s *OscilloscopeState) ampToY(gtx C, amp float32) float32 {
|
||||
scale := float32(math.Exp(s.yScale))
|
||||
return (1 - amp*scale) / 2 * float32(gtx.Constraints.Max.Y-1)
|
||||
}
|
||||
|
||||
func (s *Oscilloscope) yToAmp(gtx C, y float32) float32 {
|
||||
scale := float32(math.Exp(s.State.yScale))
|
||||
func (s *OscilloscopeState) yToAmp(gtx C, y float32) float32 {
|
||||
scale := float32(math.Exp(s.yScale))
|
||||
return (1 - y/float32(gtx.Constraints.Max.Y-1)*2) / scale
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ type SongPanel struct {
|
||||
WeightingTypeBtn *Clickable
|
||||
OversamplingBtn *Clickable
|
||||
|
||||
BPM *NumberInput
|
||||
RowsPerPattern *NumberInput
|
||||
RowsPerBeat *NumberInput
|
||||
Step *NumberInput
|
||||
SongLength *NumberInput
|
||||
BPM *NumericUpDown
|
||||
RowsPerPattern *NumericUpDown
|
||||
RowsPerBeat *NumericUpDown
|
||||
Step *NumericUpDown
|
||||
SongLength *NumericUpDown
|
||||
|
||||
Scope *OscilloscopeState
|
||||
|
||||
@ -39,11 +39,11 @@ type SongPanel struct {
|
||||
|
||||
func NewSongPanel(model *tracker.Model) *SongPanel {
|
||||
ret := &SongPanel{
|
||||
BPM: NewNumberInput(model.BPM()),
|
||||
RowsPerPattern: NewNumberInput(model.RowsPerPattern()),
|
||||
RowsPerBeat: NewNumberInput(model.RowsPerBeat()),
|
||||
Step: NewNumberInput(model.Step()),
|
||||
SongLength: NewNumberInput(model.SongLength()),
|
||||
BPM: NewNumericUpDown(),
|
||||
RowsPerPattern: NewNumericUpDown(),
|
||||
RowsPerBeat: NewNumericUpDown(),
|
||||
Step: NewNumericUpDown(),
|
||||
SongLength: NewNumericUpDown(),
|
||||
Scope: NewOscilloscope(model),
|
||||
MenuBar: NewMenuBar(model),
|
||||
PlayBar: NewPlayBar(model),
|
||||
@ -115,19 +115,19 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
||||
func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "BPM", NumUpDown(tr.Theme, t.BPM, "BPM").Layout)
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "BPM", t.BPM.Widget(tr.Model.BPM(), tr.Theme, &tr.Theme.NumericUpDown, "BPM"))
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Song length", NumUpDown(tr.Theme, t.SongLength, "Song Length").Layout)
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Song length", t.SongLength.Widget(tr.Model.SongLength(), tr.Theme, &tr.Theme.NumericUpDown, "Song length"))
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Rows per pat", NumUpDown(tr.Theme, t.RowsPerPattern, "Rows per pattern").Layout)
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Rows per pat", t.RowsPerPattern.Widget(tr.Model.RowsPerPattern(), tr.Theme, &tr.Theme.NumericUpDown, "Rows per pattern"))
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Rows per beat", NumUpDown(tr.Theme, t.RowsPerBeat, "Rows per beat").Layout)
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Rows per beat", t.RowsPerBeat.Widget(tr.Model.RowsPerBeat(), tr.Theme, &tr.Theme.NumericUpDown, "Rows per beat"))
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", NumUpDown(tr.Theme, t.Step, "Cursor step").Layout)
|
||||
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", t.Step.Widget(tr.Model.Step(), tr.Theme, &tr.Theme.NumericUpDown, "Cursor step"))
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
cpuload := tr.Model.CPULoad()
|
||||
@ -200,7 +200,9 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
||||
)
|
||||
}),
|
||||
layout.Flexed(1, func(gtx C) D {
|
||||
return t.ScopeExpander.Layout(gtx, tr.Theme, "Oscilloscope", func(gtx C) D { return D{} }, Scope(t.Scope, tr.SignalAnalyzer().Waveform(), tr.Theme).Layout)
|
||||
return t.ScopeExpander.Layout(gtx, tr.Theme, "Oscilloscope", func(gtx C) D { return D{} }, func(gtx C) D {
|
||||
return t.Scope.Layout(gtx, tr.Model.SignalAnalyzer().TriggerChannel(), tr.Model.SignalAnalyzer().LengthInBeats(), tr.Model.SignalAnalyzer().Waveform(), tr.Theme, &tr.Theme.Oscilloscope)
|
||||
})
|
||||
}),
|
||||
layout.Rigid(Label(tr.Theme, &tr.Theme.SongPanel.Version, version.VersionOrHash).Layout),
|
||||
)
|
||||
|
@ -64,7 +64,6 @@ numericupdown:
|
||||
iconcolor: *primarycolor
|
||||
cornerradius: 4
|
||||
buttonwidth: 16
|
||||
dpperstep: 8
|
||||
textsize: 14
|
||||
width: 70
|
||||
height: 20
|
||||
|
@ -28,8 +28,8 @@ var canQuit = true // set to false in init() if plugin tag is enabled
|
||||
type (
|
||||
Tracker struct {
|
||||
Theme *Theme
|
||||
OctaveNumberInput *NumberInput
|
||||
InstrumentVoices *NumberInput
|
||||
OctaveNumberInput *NumericUpDown
|
||||
InstrumentVoices *NumericUpDown
|
||||
TopHorizontalSplit *Split
|
||||
BottomHorizontalSplit *Split
|
||||
VerticalSplit *Split
|
||||
@ -70,8 +70,8 @@ var ZoomFactors = []float32{.25, 1. / 3, .5, 2. / 3, .75, .8, 1, 1.1, 1.25, 1.5,
|
||||
|
||||
func NewTracker(model *tracker.Model) *Tracker {
|
||||
t := &Tracker{
|
||||
OctaveNumberInput: NewNumberInput(model.Octave()),
|
||||
InstrumentVoices: NewNumberInput(model.InstrumentVoices()),
|
||||
OctaveNumberInput: NewNumericUpDown(),
|
||||
InstrumentVoices: NewNumericUpDown(),
|
||||
|
||||
TopHorizontalSplit: &Split{Ratio: -.5, MinSize1: 180, MinSize2: 180},
|
||||
BottomHorizontalSplit: &Split{Ratio: -.6, MinSize1: 180, MinSize2: 180},
|
||||
|
Reference in New Issue
Block a user