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) 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) { for w.Clickable.Clicked(gtx) {
w.Action.Do() w.Action.Do()
} }
ret := Button(th, &w.Clickable, text)
ret.Color = th.Palette.Fg
if !w.Action.Allowed() { if !w.Action.Allowed() {
ret.Color = disabledTextColor return Btn(th, &th.DisabledButton, &w.Clickable, text)
} }
ret.Background = transparent return Btn(th, style, &w.Clickable, text)
ret.Inset = layout.UniformInset(unit.Dp(6))
return ret
} }
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) { for b.Clickable.Clicked(gtx) {
b.Bool.Toggle() b.Bool.Toggle()
} }
ret := Button(th, &b.Clickable, text) if !b.Bool.Enabled() {
ret.Background = transparent return Btn(th, &th.DisabledButton, &b.Clickable, text)
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
} }
return ret if b.Bool.Value() {
} return Btn(th, &th.FilledButton, &b.Clickable, text)
}
func LowEmphasisButton(th *material.Theme, w *Clickable, text string) ButtonStyle { return Btn(th, &th.TextButton, &b.Clickable, text)
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
} }
// Clickable represents a clickable area. // 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 { type ButtonStyle struct {
Text string
// Color is the text color. // Color is the text color.
Color color.NRGBA Color color.NRGBA
Font font.Font Font font.Font
@ -285,14 +260,13 @@ type ButtonStyle struct {
Background color.NRGBA Background color.NRGBA
CornerRadius unit.Dp CornerRadius unit.Dp
Inset layout.Inset Inset layout.Inset
Button *Clickable
shaper *text.Shaper
} }
type ButtonLayoutStyle struct { type Button struct {
Background color.NRGBA Text string
CornerRadius unit.Dp Button *Clickable
Button *Clickable shaper *text.Shaper
ButtonStyle
} }
type IconButtonStyle struct { type IconButtonStyle struct {
@ -307,32 +281,17 @@ type IconButtonStyle struct {
Description string Description string
} }
func Button(th *material.Theme, button *Clickable, txt string) ButtonStyle { func Btn(th *Theme, style *ButtonStyle, button *Clickable, txt string) Button {
b := ButtonStyle{ b := Button{
Text: txt, Text: txt,
Color: th.Palette.ContrastFg, ButtonStyle: *style,
CornerRadius: 4, Button: button,
Background: th.Palette.ContrastBg, shaper: th.Material.Shaper,
TextSize: th.TextSize * 14.0 / 16.0,
Inset: layout.Inset{
Top: 10, Bottom: 10,
Left: 12, Right: 12,
},
Button: button,
shaper: th.Shaper,
} }
b.Font.Typeface = th.Face b.Font = labelDefaultFont
return b 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 { func IconButton(th *material.Theme, button *Clickable, icon *widget.Icon, description string) IconButtonStyle {
return IconButtonStyle{ return IconButtonStyle{
Background: th.Palette.ContrastBg, 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 { func (b Button) 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 {
min := gtx.Constraints.Min min := gtx.Constraints.Min
return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.Button.Add(gtx.Ops) 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 { func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min = min 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 { octave := func(gtx C) D {
in := layout.UniformInset(unit.Dp(1)) 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) dims := in.Layout(gtx, numStyle.Layout)
return dims 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(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(layout.Spacer{Width: 4}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { 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) dims := numStyle.Layout(gtx)
return dims 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) m := PopupMenu(menu, tr.Theme.Material.Shaper)
return func(gtx C) D { return func(gtx C) D {
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop() 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.Color = white
titleBtn.Background = transparent titleBtn.Background = transparent
titleBtn.CornerRadius = unit.Dp(0) 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 { 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 { 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") addSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.AddSemitoneBtn, "+1")
subtractSemitoneBtnStyle := ActionButton(gtx, &t.Theme.Material, te.SubtractSemitoneBtn, "-1") subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.SubtractSemitoneBtn, "-1")
addOctaveBtnStyle := ActionButton(gtx, &t.Theme.Material, te.AddOctaveBtn, "+12") addOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.AddOctaveBtn, "+12")
subtractOctaveBtnStyle := ActionButton(gtx, &t.Theme.Material, te.SubtractOctaveBtn, "-12") subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.SubtractOctaveBtn, "-12")
noteOffBtnStyle := ActionButton(gtx, &t.Theme.Material, te.NoteOffBtn, "Note Off") noteOffBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.NoteOffBtn, "Note Off")
deleteTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint) deleteTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint)
splitTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint) splitTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint)
newTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint) newTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint)
in := layout.UniformInset(unit.Dp(1)) in := layout.UniformInset(unit.Dp(1))
voiceUpDown := func(gtx C) D { 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) 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) 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, 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(func(gtx C) D { return layout.Dimensions{Size: image.Pt(gtx.Dp(unit.Dp(12)), 0)} }),
layout.Rigid(addSemitoneBtnStyle.Layout), layout.Rigid(addSemitoneBtnStyle.Layout),

View File

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

View File

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

View File

@ -32,7 +32,7 @@ type SongPanel struct {
Step *NumberInput Step *NumberInput
SongLength *NumberInput SongLength *NumberInput
Scope *Oscilloscope Scope *OscilloscopeState
MenuBar *MenuBar MenuBar *MenuBar
PlayBar *PlayBar PlayBar *PlayBar
@ -97,7 +97,7 @@ func (s *SongPanel) Layout(gtx C, t *Tracker) D {
func (t *SongPanel) layoutSongOptions(gtx C, tr *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()) 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 var weightingTxt string
switch tracker.WeightingType(tr.Model.DetectorWeighting().Value()) { switch tracker.WeightingType(tr.Model.DetectorWeighting().Value()) {
@ -111,15 +111,13 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
weightingTxt = "No weight (RMS)" weightingTxt = "No weight (RMS)"
} }
weightingBtn := LowEmphasisButton(&tr.Theme.Material, t.WeightingTypeBtn, weightingTxt) weightingBtn := Btn(tr.Theme, &tr.Theme.TextButton, t.WeightingTypeBtn, weightingTxt)
weightingBtn.Color = mediumEmphasisTextColor
oversamplingTxt := "Sample peak" oversamplingTxt := "Sample peak"
if tr.Model.Oversampling().Value() { if tr.Model.Oversampling().Value() {
oversamplingTxt = "True peak" oversamplingTxt = "True peak"
} }
oversamplingBtn := LowEmphasisButton(&tr.Theme.Material, t.OversamplingBtn, oversamplingTxt) oversamplingBtn := Btn(tr.Theme, &tr.Theme.TextButton, t.OversamplingBtn, oversamplingTxt)
oversamplingBtn.Color = mediumEmphasisTextColor
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
@ -130,19 +128,19 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
func(gtx C) D { func(gtx C) D {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D { 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 { 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 { 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 { 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 { 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 { type Theme struct {
Material material.Theme Material material.Theme
Oscilloscope struct { FilledButton ButtonStyle
CurveColors [2]color.NRGBA `yaml:",flow"` TextButton ButtonStyle
LimitColor color.NRGBA `yaml:",flow"` DisabledButton ButtonStyle
CursorColor color.NRGBA `yaml:",flow"` MenuButton ButtonStyle
} Oscilloscope OscilloscopeStyle
NumericUpDown struct { NumericUpDown NumericUpDownStyle
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
}
} }
//go:embed theme.yml //go:embed theme.yml
@ -39,7 +29,10 @@ var defaultTheme []byte
func NewTheme() *Theme { func NewTheme() *Theme {
var theme 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) str, _ := yaml.Marshal(theme)
fmt.Printf(string(str)) fmt.Printf(string(str))
ReadCustomConfigYml("theme.yml", &theme) 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: material:
textsize: 16 textsize: 16
fingersize: 38 fingersize: 38
palette: palette:
bg: &bg { r: 18, g: 18, b: 18, a: 255 } bg: &bg { r: 18, g: 18, b: 18, a: 255 }
fg: &fg { r: 255, g: 255, b: 255, 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 } 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: oscilloscope:
curvecolors: curvecolors: [*primarycolor, *secondarycolor]
[{ r: 206, g: 147, b: 216, a: 255 }, { r: 128, g: 222, b: 234, a: 255 }]
limitcolor: { r: 255, g: 255, b: 255, a: 8 } limitcolor: { r: 255, g: 255, b: 255, a: 8 }
cursorcolor: { r: 252, g: 186, b: 3, a: 255 } cursorcolor: { r: 252, g: 186, b: 3, a: 255 }
numericupdown: numericupdown:
bgcolor: { r: 255, g: 255, b: 255, A: 3 } bgcolor: { r: 255, g: 255, b: 255, A: 3 }
textcolor: *fg textcolor: *fg
iconcolor: *contrastbg iconcolor: *primarycolor
cornerradius: 4 cornerradius: 4
buttonwidth: 16 buttonwidth: 16
unitsperstep: 8 dpperstep: 8
textsize: 14 textsize: 14
width: 70 width: 70
height: 20 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) 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 := material.Slider(p.Theme, &p.w.floatWidget)
sliderStyle.Color = p.Theme.Fg
if isSendTarget { if isSendTarget {
sliderStyle.Color = paramIsSendTargetColor sliderStyle.Color = paramIsSendTargetColor
} }
@ -319,7 +318,6 @@ func (p ParameterStyle) Layout(gtx C) D {
p.w.boolWidget.Value = p.w.Parameter.Value() > ra.Min p.w.boolWidget.Value = p.w.Parameter.Value() > ra.Min
boolStyle := material.Switch(p.Theme, &p.w.boolWidget, "Toggle boolean parameter") boolStyle := material.Switch(p.Theme, &p.w.boolWidget, "Toggle boolean parameter")
boolStyle.Color.Disabled = p.Theme.Fg boolStyle.Color.Disabled = p.Theme.Fg
boolStyle.Color.Enabled = p.Theme.ContrastBg
defer pointer.PassOp{}.Push(gtx.Ops).Pop() defer pointer.PassOp{}.Push(gtx.Ops).Pop()
dims := layout.Center.Layout(gtx, boolStyle.Layout) dims := layout.Center.Layout(gtx, boolStyle.Layout)
if p.w.boolWidget.Value { if p.w.boolWidget.Value {