This commit is contained in:
5684185+vsariola@users.noreply.github.com 2025-05-02 18:28:04 +03:00
parent 8415660416
commit ec8c51b003
10 changed files with 179 additions and 187 deletions

View File

@ -115,51 +115,27 @@ func (t *TipIconButtonStyle) Layout(gtx C) D {
return t.TipArea.Layout(gtx, t.Tooltip, t.IconButtonStyle.Layout)
}
func ActionButton(gtx C, th *material.Theme, w *ActionClickable, text string) ButtonStyle {
func ActionButton(gtx C, th *Theme, style *ButtonStyle, w *ActionClickable, text string) Button {
for w.Clickable.Clicked(gtx) {
w.Action.Do()
}
ret := Button(th, &w.Clickable, text)
ret.Color = th.Palette.Fg
if !w.Action.Allowed() {
ret.Color = disabledTextColor
return Btn(th, &th.DisabledButton, &w.Clickable, text)
}
ret.Background = transparent
ret.Inset = layout.UniformInset(unit.Dp(6))
return ret
return Btn(th, style, &w.Clickable, text)
}
func ToggleButton(gtx C, th *material.Theme, b *BoolClickable, text string) ButtonStyle {
func ToggleButton(gtx C, th *Theme, b *BoolClickable, text string) Button {
for b.Clickable.Clicked(gtx) {
b.Bool.Toggle()
}
ret := Button(th, &b.Clickable, text)
ret.Background = transparent
ret.Inset = layout.UniformInset(unit.Dp(6))
if b.Bool.Value() {
ret.Color = th.Palette.ContrastFg
ret.Background = th.Palette.ContrastBg
} else {
ret.Color = th.Palette.ContrastBg
ret.Background = transparent
if !b.Bool.Enabled() {
return Btn(th, &th.DisabledButton, &b.Clickable, text)
}
return ret
}
func LowEmphasisButton(th *material.Theme, w *Clickable, text string) ButtonStyle {
ret := Button(th, w, text)
ret.Color = th.Palette.Fg
ret.Background = transparent
ret.Inset = layout.UniformInset(unit.Dp(6))
return ret
}
func HighEmphasisButton(th *material.Theme, w *Clickable, text string) ButtonStyle {
ret := Button(th, w, text)
ret.Color = th.Palette.ContrastFg
ret.Background = th.Palette.Fg
ret.Inset = layout.UniformInset(unit.Dp(6))
return ret
if b.Bool.Value() {
return Btn(th, &th.FilledButton, &b.Clickable, text)
}
return Btn(th, &th.TextButton, &b.Clickable, text)
}
// Clickable represents a clickable area.
@ -277,7 +253,6 @@ func (b *Clickable) update(t event.Tag, gtx layout.Context) (widget.Click, bool)
}
type ButtonStyle struct {
Text string
// Color is the text color.
Color color.NRGBA
Font font.Font
@ -285,14 +260,13 @@ type ButtonStyle struct {
Background color.NRGBA
CornerRadius unit.Dp
Inset layout.Inset
Button *Clickable
shaper *text.Shaper
}
type ButtonLayoutStyle struct {
Background color.NRGBA
CornerRadius unit.Dp
Button *Clickable
type Button struct {
Text string
Button *Clickable
shaper *text.Shaper
ButtonStyle
}
type IconButtonStyle struct {
@ -307,32 +281,17 @@ type IconButtonStyle struct {
Description string
}
func Button(th *material.Theme, button *Clickable, txt string) ButtonStyle {
b := ButtonStyle{
Text: txt,
Color: th.Palette.ContrastFg,
CornerRadius: 4,
Background: th.Palette.ContrastBg,
TextSize: th.TextSize * 14.0 / 16.0,
Inset: layout.Inset{
Top: 10, Bottom: 10,
Left: 12, Right: 12,
},
Button: button,
shaper: th.Shaper,
func Btn(th *Theme, style *ButtonStyle, button *Clickable, txt string) Button {
b := Button{
Text: txt,
ButtonStyle: *style,
Button: button,
shaper: th.Material.Shaper,
}
b.Font.Typeface = th.Face
b.Font = labelDefaultFont
return b
}
func ButtonLayout(th *material.Theme, button *Clickable) ButtonLayoutStyle {
return ButtonLayoutStyle{
Button: button,
Background: th.Palette.ContrastBg,
CornerRadius: 4,
}
}
func IconButton(th *material.Theme, button *Clickable, icon *widget.Icon, description string) IconButtonStyle {
return IconButtonStyle{
Background: th.Palette.ContrastBg,
@ -345,21 +304,7 @@ func IconButton(th *material.Theme, button *Clickable, icon *widget.Icon, descri
}
}
func (b ButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
return ButtonLayoutStyle{
Background: b.Background,
CornerRadius: b.CornerRadius,
Button: b.Button,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
colMacro := op.Record(gtx.Ops)
paint.ColorOp{Color: b.Color}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text, colMacro.Stop())
})
})
}
func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
func (b Button) Layout(gtx layout.Context) layout.Dimensions {
min := gtx.Constraints.Min
return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.Button.Add(gtx.Ops)
@ -380,7 +325,13 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Di
},
func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min = min
return layout.Center.Layout(gtx, w)
return layout.Center.Layout(gtx, func(gtx C) D {
return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
colMacro := op.Record(gtx.Ops)
paint.ColorOp{Color: b.Color}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text, colMacro.Stop())
})
})
},
)
})

View File

@ -130,7 +130,7 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
octave := func(gtx C) D {
in := layout.UniformInset(unit.Dp(1))
numStyle := NumericUpDown(&t.Theme.Material, t.OctaveNumberInput, ie.octaveHint)
numStyle := NumUpDown(t.Theme, t.OctaveNumberInput, ie.octaveHint)
dims := in.Layout(gtx, numStyle.Layout)
return dims
}
@ -217,7 +217,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
layout.Rigid(LabelStyle{Text: "Voices", Color: disabledTextColor, Alignment: layout.W, FontSize: t.Theme.Material.TextSize * 14.0 / 16.0, Shaper: t.Theme.Material.Shaper}.Layout),
layout.Rigid(layout.Spacer{Width: 4}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
numStyle := NumericUpDown(&t.Theme.Material, t.InstrumentVoices, "Number of voices for this instrument")
numStyle := NumUpDown(t.Theme, t.InstrumentVoices, "Number of voices for this instrument")
dims := numStyle.Layout(gtx)
return dims
}),

View File

@ -173,7 +173,7 @@ func (tr *Tracker) layoutMenu(gtx C, title string, clickable *Clickable, menu *M
m := PopupMenu(menu, tr.Theme.Material.Shaper)
return func(gtx C) D {
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
titleBtn := Button(&tr.Theme.Material, clickable, title)
titleBtn := Btn(tr.Theme, &tr.Theme.MenuButton, clickable, title)
titleBtn.Color = white
titleBtn.Background = transparent
titleBtn.CornerRadius = unit.Dp(0)

View File

@ -150,22 +150,22 @@ func (te *NoteEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions {
func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
return Surface{Gray: 37, Focus: te.scrollTable.Focused() || te.scrollTable.ChildFocused()}.Layout(gtx, func(gtx C) D {
addSemitoneBtnStyle := ActionButton(gtx, &t.Theme.Material, te.AddSemitoneBtn, "+1")
subtractSemitoneBtnStyle := ActionButton(gtx, &t.Theme.Material, te.SubtractSemitoneBtn, "-1")
addOctaveBtnStyle := ActionButton(gtx, &t.Theme.Material, te.AddOctaveBtn, "+12")
subtractOctaveBtnStyle := ActionButton(gtx, &t.Theme.Material, te.SubtractOctaveBtn, "-12")
noteOffBtnStyle := ActionButton(gtx, &t.Theme.Material, te.NoteOffBtn, "Note Off")
addSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.AddSemitoneBtn, "+1")
subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.SubtractSemitoneBtn, "-1")
addOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.AddOctaveBtn, "+12")
subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.SubtractOctaveBtn, "-12")
noteOffBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.NoteOffBtn, "Note Off")
deleteTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint)
splitTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint)
newTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint)
in := layout.UniformInset(unit.Dp(1))
voiceUpDown := func(gtx C) D {
numStyle := NumericUpDown(&t.Theme.Material, te.TrackVoices, "Track voices")
numStyle := NumUpDown(t.Theme, te.TrackVoices, "Track voices")
return in.Layout(gtx, numStyle.Layout)
}
effectBtnStyle := ToggleButton(gtx, &t.Theme.Material, te.EffectBtn, "Hex")
effectBtnStyle := ToggleButton(gtx, t.Theme, te.EffectBtn, "Hex")
uniqueBtnStyle := ToggleIcon(gtx, &t.Theme.Material, te.UniqueBtn, icons.ToggleStarBorder, icons.ToggleStar, te.uniqueOffTip, te.uniqueOnTip)
midiInBtnStyle := ToggleButton(gtx, &t.Theme.Material, te.TrackMidiInBtn, "MIDI")
midiInBtnStyle := ToggleButton(gtx, t.Theme, te.TrackMidiInBtn, "MIDI")
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(func(gtx C) D { return layout.Dimensions{Size: image.Pt(gtx.Dp(unit.Dp(12)), 0)} }),
layout.Rigid(addSemitoneBtnStyle.Layout),

View File

@ -2,14 +2,17 @@ package gioui
import (
"image"
"image/color"
"strconv"
"github.com/vsariola/sointu/tracker"
"golang.org/x/exp/shiny/materialdesign/icons"
"gioui.org/font"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/x/component"
@ -30,26 +33,41 @@ type NumberInput struct {
}
type NumericUpDownStyle struct {
Theme *Theme
TextColor color.NRGBA `yaml:",flow"`
IconColor color.NRGBA `yaml:",flow"`
BgColor color.NRGBA `yaml:",flow"`
CornerRadius unit.Dp
ButtonWidth unit.Dp
Width unit.Dp
Height unit.Dp
TextSize unit.Sp
DpPerStep unit.Dp
}
type NumericUpDown struct {
NumberInput *NumberInput
Tooltip component.Tooltip
Shaper *text.Shaper
Font font.Font
NumericUpDownStyle
}
func NewNumberInput(v tracker.Int) *NumberInput {
return &NumberInput{Int: v}
}
func NumericUpDown(th *Theme, number *NumberInput, tooltip string) NumericUpDownStyle {
return NumericUpDownStyle{
NumberInput: number,
Theme: th,
Tooltip: Tooltip(&th.Material, tooltip),
func NumUpDown(th *Theme, number *NumberInput, tooltip string) NumericUpDown {
return NumericUpDown{
NumberInput: number,
Shaper: th.Material.Shaper,
Tooltip: Tooltip(&th.Material, tooltip),
NumericUpDownStyle: th.NumericUpDown,
}
}
func (s *NumericUpDownStyle) Update(gtx layout.Context) {
func (s *NumericUpDown) Update(gtx layout.Context) {
// handle dragging
pxPerStep := float32(gtx.Dp(s.Theme.NumericUpDown.DpPerStep))
pxPerStep := float32(gtx.Dp(s.DpPerStep))
for {
ev, ok := gtx.Event(pointer.Filter{
Target: s.NumberInput,
@ -84,22 +102,22 @@ func (s *NumericUpDownStyle) Update(gtx layout.Context) {
}
}
func (s NumericUpDownStyle) Layout(gtx C) D {
func (s NumericUpDown) Layout(gtx C) D {
if s.Tooltip.Text.Text != "" {
return s.NumberInput.tipArea.Layout(gtx, s.Tooltip, s.actualLayout)
}
return s.actualLayout(gtx)
}
func (s *NumericUpDownStyle) actualLayout(gtx C) D {
func (s *NumericUpDown) actualLayout(gtx C) D {
s.Update(gtx)
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(s.Theme.NumericUpDown.Width), gtx.Dp(s.Theme.NumericUpDown.Height)))
width := gtx.Dp(s.Theme.NumericUpDown.ButtonWidth)
height := gtx.Dp(s.Theme.NumericUpDown.Height)
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(s.Width), gtx.Dp(s.Height)))
width := gtx.Dp(s.ButtonWidth)
height := gtx.Dp(s.Height)
return layout.Background{}.Layout(gtx,
func(gtx C) D {
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(s.Theme.NumericUpDown.CornerRadius)).Push(gtx.Ops).Pop()
paint.Fill(gtx.Ops, s.Theme.NumericUpDown.BgColor)
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
return D{Size: gtx.Constraints.Min}
},
@ -113,12 +131,12 @@ func (s *NumericUpDownStyle) actualLayout(gtx C) D {
s.NumberInput.clickDecrease.Add(gtx.Ops)
return D{Size: gtx.Constraints.Min}
},
func(gtx C) D { return widgetForIcon(icons.ContentRemove).Layout(gtx, s.Theme.NumericUpDown.IconColor) },
func(gtx C) D { return widgetForIcon(icons.ContentRemove).Layout(gtx, s.IconColor) },
)
}),
layout.Flexed(1, func(gtx C) D {
paint.ColorOp{Color: s.Theme.NumericUpDown.TextColor}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, s.Theme.Material.Shaper, s.Font, s.Theme.NumericUpDown.TextSize, strconv.Itoa(s.NumberInput.Int.Value()), op.CallOp{})
paint.ColorOp{Color: s.TextColor}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, s.Shaper, s.Font, s.TextSize, strconv.Itoa(s.NumberInput.Int.Value()), op.CallOp{})
}),
layout.Rigid(func(gtx C) D {
gtx.Constraints = layout.Exact(image.Pt(width, height))

View File

@ -2,6 +2,7 @@ package gioui
import (
"image"
"image/color"
"math"
"gioui.org/f32"
@ -15,7 +16,7 @@ import (
)
type (
Oscilloscope struct {
OscilloscopeState struct {
onceBtn *BoolClickable
wrapBtn *BoolClickable
lengthInBeatsNumber *NumberInput
@ -29,14 +30,21 @@ type (
}
OscilloscopeStyle struct {
Oscilloscope *Oscilloscope
Wave tracker.RingBuffer[[2]float32]
Theme *Theme
CurveColors [2]color.NRGBA `yaml:",flow"`
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) *Oscilloscope {
return &Oscilloscope{
func NewOscilloscope(model *tracker.Model) *OscilloscopeState {
return &OscilloscopeState{
onceBtn: NewBoolClickable(model.SignalAnalyzer().Once().Bool()),
wrapBtn: NewBoolClickable(model.SignalAnalyzer().Wrap().Bool()),
lengthInBeatsNumber: NewNumberInput(model.SignalAnalyzer().LengthInBeats().Int()),
@ -44,15 +52,15 @@ func NewOscilloscope(model *tracker.Model) *Oscilloscope {
}
}
func LineOscilloscope(s *Oscilloscope, wave tracker.RingBuffer[[2]float32], th *Theme) *OscilloscopeStyle {
return &OscilloscopeStyle{Oscilloscope: s, Wave: wave, Theme: th}
func Scope(s *OscilloscopeState, wave tracker.RingBuffer[[2]float32], th *Theme) *Oscilloscope {
return &Oscilloscope{State: s, Wave: wave, Theme: th}
}
func (s *OscilloscopeStyle) Layout(gtx C) D {
wrapBtnStyle := ToggleButton(gtx, &s.Theme.Material, s.Oscilloscope.wrapBtn, "Wrap")
onceBtnStyle := ToggleButton(gtx, &s.Theme.Material, s.Oscilloscope.onceBtn, "Once")
triggerChannelStyle := NumericUpDown(&s.Theme.Material, s.Oscilloscope.triggerChannelNumber, "Trigger channel")
lengthNumberStyle := NumericUpDown(&s.Theme.Material, s.Oscilloscope.lengthInBeatsNumber, "Buffer length in beats")
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")
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
@ -82,13 +90,13 @@ func (s *OscilloscopeStyle) Layout(gtx C) D {
)
}
func (s *OscilloscopeStyle) layoutWave(gtx C) D {
func (s *Oscilloscope) layoutWave(gtx C) D {
s.update(gtx)
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.Oscilloscope)
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)))
fillRect(gtx, clip.Rect{Min: image.Pt(cursorX, 0), Max: image.Pt(cursorX+1, gtx.Constraints.Max.Y)})
@ -134,10 +142,10 @@ func fillRect(gtx C, rect clip.Rect) {
stack.Pop()
}
func (o *OscilloscopeStyle) update(gtx C) {
func (o *Oscilloscope) update(gtx C) {
for {
ev, ok := gtx.Event(pointer.Filter{
Target: o.Oscilloscope,
Target: o.State,
Kinds: pointer.Scroll | pointer.Press | pointer.Drag | pointer.Release | pointer.Cancel,
ScrollY: pointer.ScrollRange{Min: -1e6, Max: 1e6},
})
@ -148,58 +156,58 @@ func (o *OscilloscopeStyle) update(gtx C) {
switch e.Kind {
case pointer.Scroll:
s1 := o.pxToSample(gtx, e.Position.X)
o.Oscilloscope.xScale += min(max(-1, int(e.Scroll.Y)), 1)
o.State.xScale += min(max(-1, int(e.Scroll.Y)), 1)
s2 := o.pxToSample(gtx, e.Position.X)
o.Oscilloscope.xOffset -= s1 - s2
o.State.xOffset -= s1 - s2
case pointer.Press:
if e.Buttons&pointer.ButtonSecondary != 0 {
o.Oscilloscope.xOffset = 0
o.Oscilloscope.xScale = 0
o.Oscilloscope.yScale = 0
o.State.xOffset = 0
o.State.xScale = 0
o.State.yScale = 0
}
if e.Buttons&pointer.ButtonPrimary != 0 {
o.Oscilloscope.dragging = true
o.Oscilloscope.dragId = e.PointerID
o.Oscilloscope.dragStartPoint = e.Position
o.State.dragging = true
o.State.dragId = e.PointerID
o.State.dragStartPoint = e.Position
}
case pointer.Drag:
if e.Buttons&pointer.ButtonPrimary != 0 && o.Oscilloscope.dragging && e.PointerID == o.Oscilloscope.dragId {
deltaX := o.pxToSample(gtx, e.Position.X) - o.pxToSample(gtx, o.Oscilloscope.dragStartPoint.X)
o.Oscilloscope.xOffset += deltaX
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
num := o.yToAmp(gtx, e.Position.Y)
den := o.yToAmp(gtx, o.Oscilloscope.dragStartPoint.Y)
den := o.yToAmp(gtx, o.State.dragStartPoint.Y)
if l := math.Abs(float64(num / den)); l > 1e-3 && l < 1e3 {
o.Oscilloscope.yScale += math.Log(l)
o.Oscilloscope.yScale = min(max(o.Oscilloscope.yScale, -1e3), 1e3)
o.State.yScale += math.Log(l)
o.State.yScale = min(max(o.State.yScale, -1e3), 1e3)
}
o.Oscilloscope.dragStartPoint = e.Position
o.State.dragStartPoint = e.Position
}
case pointer.Release | pointer.Cancel:
o.Oscilloscope.dragging = false
o.State.dragging = false
}
}
}
}
func (o *OscilloscopeStyle) scaleFactor() float32 {
return float32(math.Pow(1.1, float64(o.Oscilloscope.xScale)))
func (o *Oscilloscope) scaleFactor() float32 {
return float32(math.Pow(1.1, float64(o.State.xScale)))
}
func (s *OscilloscopeStyle) pxToSample(gtx C, px float32) float32 {
return px*s.scaleFactor()*float32(len(s.Wave.Buffer))/float32(gtx.Constraints.Max.X) - s.Oscilloscope.xOffset
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 *OscilloscopeStyle) sampleToPx(gtx C, sample float32) float32 {
return (sample + s.Oscilloscope.xOffset) * float32(gtx.Constraints.Max.X) / float32(len(s.Wave.Buffer)) / s.scaleFactor()
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 *OscilloscopeStyle) ampToY(gtx C, amp float32) float32 {
scale := float32(math.Exp(s.Oscilloscope.yScale))
func (s *Oscilloscope) ampToY(gtx C, amp float32) float32 {
scale := float32(math.Exp(s.State.yScale))
return (1 - amp*scale) / 2 * float32(gtx.Constraints.Max.Y-1)
}
func (s *OscilloscopeStyle) yToAmp(gtx C, y float32) float32 {
scale := float32(math.Exp(s.Oscilloscope.yScale))
func (s *Oscilloscope) yToAmp(gtx C, y float32) float32 {
scale := float32(math.Exp(s.State.yScale))
return (1 - y/float32(gtx.Constraints.Max.Y-1)*2) / scale
}

View File

@ -32,7 +32,7 @@ type SongPanel struct {
Step *NumberInput
SongLength *NumberInput
Scope *Oscilloscope
Scope *OscilloscopeState
MenuBar *MenuBar
PlayBar *PlayBar
@ -97,7 +97,7 @@ func (s *SongPanel) Layout(gtx C, t *Tracker) D {
func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
paint.FillShape(gtx.Ops, songSurfaceColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
scopeStyle := LineOscilloscope(t.Scope, tr.SignalAnalyzer().Waveform(), tr.Theme)
scopeStyle := Scope(t.Scope, tr.SignalAnalyzer().Waveform(), tr.Theme)
var weightingTxt string
switch tracker.WeightingType(tr.Model.DetectorWeighting().Value()) {
@ -111,15 +111,13 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
weightingTxt = "No weight (RMS)"
}
weightingBtn := LowEmphasisButton(&tr.Theme.Material, t.WeightingTypeBtn, weightingTxt)
weightingBtn.Color = mediumEmphasisTextColor
weightingBtn := Btn(tr.Theme, &tr.Theme.TextButton, t.WeightingTypeBtn, weightingTxt)
oversamplingTxt := "Sample peak"
if tr.Model.Oversampling().Value() {
oversamplingTxt = "True peak"
}
oversamplingBtn := LowEmphasisButton(&tr.Theme.Material, t.OversamplingBtn, oversamplingTxt)
oversamplingBtn.Color = mediumEmphasisTextColor
oversamplingBtn := Btn(tr.Theme, &tr.Theme.TextButton, t.OversamplingBtn, oversamplingTxt)
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D {
@ -130,19 +128,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.Material, "BPM", NumericUpDown(&tr.Theme.Material, t.BPM, "Song Length").Layout)
return layoutSongOptionRow(gtx, &tr.Theme.Material, "BPM", NumUpDown(tr.Theme, t.BPM, "Song Length").Layout)
}),
layout.Rigid(func(gtx C) D {
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Song length", NumericUpDown(&tr.Theme.Material, t.SongLength, "Song Length").Layout)
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Song length", NumUpDown(tr.Theme, t.SongLength, "Song Length").Layout)
}),
layout.Rigid(func(gtx C) D {
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Rows per pat", NumericUpDown(&tr.Theme.Material, t.RowsPerPattern, "Rows per pattern").Layout)
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Rows per pat", NumUpDown(tr.Theme, t.RowsPerPattern, "Rows per pattern").Layout)
}),
layout.Rigid(func(gtx C) D {
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Rows per beat", NumericUpDown(&tr.Theme.Material, t.RowsPerBeat, "Rows per beat").Layout)
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Rows per beat", NumUpDown(tr.Theme, t.RowsPerBeat, "Rows per beat").Layout)
}),
layout.Rigid(func(gtx C) D {
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Cursor step", NumericUpDown(&tr.Theme.Material, t.Step, "Cursor step").Layout)
return layoutSongOptionRow(gtx, &tr.Theme.Material, "Cursor step", NumUpDown(tr.Theme, t.Step, "Cursor step").Layout)
}),
)
})

View File

@ -15,23 +15,13 @@ import (
)
type Theme struct {
Material material.Theme
Oscilloscope struct {
CurveColors [2]color.NRGBA `yaml:",flow"`
LimitColor color.NRGBA `yaml:",flow"`
CursorColor color.NRGBA `yaml:",flow"`
}
NumericUpDown struct {
TextColor color.NRGBA `yaml:",flow"`
IconColor color.NRGBA `yaml:",flow"`
BgColor color.NRGBA `yaml:",flow"`
CornerRadius unit.Dp
ButtonWidth unit.Dp
Width unit.Dp
Height unit.Dp
TextSize unit.Sp
DpPerStep unit.Dp
}
Material material.Theme
FilledButton ButtonStyle
TextButton ButtonStyle
DisabledButton ButtonStyle
MenuButton ButtonStyle
Oscilloscope OscilloscopeStyle
NumericUpDown NumericUpDownStyle
}
//go:embed theme.yml
@ -39,7 +29,10 @@ var defaultTheme []byte
func NewTheme() *Theme {
var theme Theme
yaml.Unmarshal(defaultTheme, &theme)
err := yaml.Unmarshal(defaultTheme, &theme)
if err != nil {
panic(fmt.Errorf("failed to default theme: %w", err))
}
str, _ := yaml.Marshal(theme)
fmt.Printf(string(str))
ReadCustomConfigYml("theme.yml", &theme)

View File

@ -1,23 +1,49 @@
primarycolor: &primarycolor { r: 243, g: 187, b: 252, a: 255 }
secondarycolor: &secondarycolor { r: 187, g: 245, b: 252, a: 255 }
transparentcolor: &transparentcolor { r: 0, g: 0, b: 0, a: 0 }
material:
textsize: 16
fingersize: 38
palette:
bg: &bg { r: 18, g: 18, b: 18, a: 255 }
fg: &fg { r: 255, g: 255, b: 255, a: 255 }
contrastbg: &contrastbg { r: 206, g: 147, b: 216, a: 255 }
contrastbg: *primarycolor
contrastfg: &contrastfg { r: 0, g: 0, b: 0, a: 255 }
filledbutton:
background: *primarycolor
color: *contrastfg
textsize: &buttontextsize 14
cornerradius: &buttoncornerradius 12
inset: &buttoninset { top: 0, bottom: 0, left: 6, right: 6 }
textbutton:
background: *transparentcolor
color: *primarycolor
textsize: *buttontextsize
cornerradius: *buttoncornerradius
inset: *buttoninset
disabledbutton:
background: { r: 53, g: 51, b: 55, a: 255 }
color: { r: 120, g: 116, b: 121, a: 255 }
textsize: *buttontextsize
cornerradius: *buttoncornerradius
inset: *buttoninset
menubutton:
background: *transparentcolor
color: { r: 255, g: 255, b: 255, a: 255 }
textsize: *buttontextsize
cornerradius: 0
inset: *buttoninset
oscilloscope:
curvecolors:
[{ r: 206, g: 147, b: 216, a: 255 }, { r: 128, g: 222, b: 234, a: 255 }]
curvecolors: [*primarycolor, *secondarycolor]
limitcolor: { r: 255, g: 255, b: 255, a: 8 }
cursorcolor: { r: 252, g: 186, b: 3, a: 255 }
numericupdown:
bgcolor: { r: 255, g: 255, b: 255, A: 3 }
textcolor: *fg
iconcolor: *contrastbg
iconcolor: *primarycolor
cornerradius: 4
buttonwidth: 16
unitsperstep: 8
dpperstep: 8
textsize: 14
width: 70
height: 20

View File

@ -299,7 +299,6 @@ func (p ParameterStyle) Layout(gtx C) D {
p.w.floatWidget.Value = (float32(p.w.Parameter.Value()) - float32(ra.Min)) / float32(ra.Max-ra.Min)
}
sliderStyle := material.Slider(p.Theme, &p.w.floatWidget)
sliderStyle.Color = p.Theme.Fg
if isSendTarget {
sliderStyle.Color = paramIsSendTargetColor
}
@ -319,7 +318,6 @@ func (p ParameterStyle) Layout(gtx C) D {
p.w.boolWidget.Value = p.w.Parameter.Value() > ra.Min
boolStyle := material.Switch(p.Theme, &p.w.boolWidget, "Toggle boolean parameter")
boolStyle.Color.Disabled = p.Theme.Fg
boolStyle.Color.Enabled = p.Theme.ContrastBg
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
dims := layout.Center.Layout(gtx, boolStyle.Layout)
if p.w.boolWidget.Value {