drafting new buttons

This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-06-22 21:48:32 +03:00
parent f587c5d865
commit 64ec5f17f3
9 changed files with 315 additions and 219 deletions

View File

@ -30,8 +30,8 @@ type (
}
ClickableTip struct {
Clickable Clickable
TipArea component.TipArea
Clickable
component.TipArea
}
ButtonStyle struct {
@ -54,127 +54,128 @@ type (
Inset layout.Inset
}
TipIconButton struct {
th *Theme
st *IconButtonStyle
t *ClickableTip
icon []byte
tip string
enabled bool
Button struct {
Theme *Theme
Style ButtonStyle
Text string
Tip string
*ClickableTip
}
ActionButton struct {
act tracker.Action
DisabledStyle ButtonStyle
Button
}
ToggleButton struct {
b tracker.Bool
DisabledStyle ButtonStyle
OffStyle ButtonStyle
Button
}
IconButton struct {
Theme *Theme
Style IconButtonStyle
Icon *widget.Icon
Tip string
*ClickableTip
}
ActionIconButton struct {
act tracker.Action
DisabledStyle IconButtonStyle
IconButton
}
ToggleIconButton struct {
b tracker.Bool
DisabledStyle IconButtonStyle
OffIcon *widget.Icon
OffTip string
IconButton
}
)
func TipIconBtn(th *Theme, st *IconButtonStyle, t *ClickableTip, icon []byte, tip string, enabled bool) TipIconButton {
return TipIconButton{
th: th,
st: st,
t: t,
icon: icon,
tip: tip,
enabled: enabled,
func Btn(th *Theme, st *ButtonStyle, b *ClickableTip, txt string, tip string) Button {
return Button{
Theme: th,
Style: *st,
ClickableTip: b,
Text: txt,
Tip: tip,
}
}
func (t TipIconButton) Layout(gtx C) D {
iconBtn := IconBtn(t.th, t.st, &t.t.Clickable, t.th.Icon(t.icon), t.enabled)
if t.tip != "" {
return t.t.TipArea.Layout(gtx, Tooltip(t.th, t.tip), iconBtn)
} else {
return iconBtn(gtx)
func ActionBtn(act tracker.Action, th *Theme, b *ClickableTip, txt string, tip string) ActionButton {
return ActionButton{
act: act,
DisabledStyle: th.Button.Disabled,
Button: Btn(th, &th.Button.Text, b, txt, tip),
}
}
func ActionIconBtn(act tracker.Action, th *Theme, t *ClickableTip, icon []byte, tip string) layout.Widget {
return func(gtx C) D {
for t.Clickable.Clicked(gtx) {
act.Do()
}
iconBtn := IconBtn(th, &th.IconButton, &t.Clickable, th.Icon(icon), act.Enabled())
if tip != "" {
return t.TipArea.Layout(gtx, Tooltip(th, tip), iconBtn)
} else {
return iconBtn(gtx)
}
func ToggleBtn(b tracker.Bool, th *Theme, c *ClickableTip, text string, tip string) ToggleButton {
return ToggleButton{
b: b,
DisabledStyle: th.Button.Disabled,
OffStyle: th.Button.Text,
Button: Btn(th, &th.Button.Filled, c, text, tip),
}
}
func ToggleIconBtn(b tracker.Bool, th *Theme, t *ClickableTip, offIcon, onIcon []byte, offTip, onTip string) layout.Widget {
return func(gtx C) D {
icon := offIcon
tip := offTip
if b.Value() {
icon = onIcon
tip = onTip
}
for t.Clickable.Clicked(gtx) {
b.Toggle()
}
iconBtn := IconBtn(th, &th.IconButton, &t.Clickable, th.Icon(icon), b.Enabled())
if tip != "" {
return t.TipArea.Layout(gtx, Tooltip(th, tip), iconBtn)
} else {
return iconBtn(gtx)
}
func IconBtn(th *Theme, st *IconButtonStyle, b *ClickableTip, icon []byte, tip string) IconButton {
return IconButton{
Theme: th,
Style: *st,
ClickableTip: b,
Icon: th.Icon(icon),
Tip: tip,
}
}
func ActionBtn(a tracker.Action, th *Theme, c *ClickableTip, text string, tip string) layout.Widget {
return func(gtx C) D {
for c.Clickable.Clicked(gtx) {
a.Do()
}
var btn layout.Widget
if !a.Enabled() {
btn = Btn(th, &th.Button.Disabled, &c.Clickable, text)
} else {
btn = Btn(th, &th.Button.Text, &c.Clickable, text)
}
if tip != "" {
return c.TipArea.Layout(gtx, Tooltip(th, tip), btn)
} else {
return btn(gtx)
}
func ActionIconBtn(act tracker.Action, th *Theme, b *ClickableTip, icon []byte, tip string) ActionIconButton {
return ActionIconButton{
act: act,
DisabledStyle: th.IconButton.Disabled,
IconButton: IconBtn(th, &th.IconButton.Enabled, b, icon, tip),
}
}
func ToggleBtn(b tracker.Bool, th *Theme, c *ClickableTip, text string, tip string) layout.Widget {
return func(gtx C) D {
for c.Clickable.Clicked(gtx) {
b.Toggle()
}
var btn layout.Widget
if !b.Enabled() {
btn = Btn(th, &th.Button.Disabled, &c.Clickable, text)
} else if b.Value() {
btn = Btn(th, &th.Button.Filled, &c.Clickable, text)
} else {
btn = Btn(th, &th.Button.Text, &c.Clickable, text)
}
if tip != "" {
return c.TipArea.Layout(gtx, Tooltip(th, tip), btn)
} else {
return btn(gtx)
}
func ToggleIconBtn(b tracker.Bool, th *Theme, c *ClickableTip, offIcon, onIcon []byte, offTip, onTip string) ToggleIconButton {
return ToggleIconButton{
b: b,
DisabledStyle: th.IconButton.Disabled,
OffIcon: th.Icon(offIcon),
OffTip: offTip,
IconButton: IconBtn(th, &th.IconButton.Enabled, c, onIcon, onTip),
}
}
func Btn(th *Theme, st *ButtonStyle, b *Clickable, txt string) layout.Widget {
return func(gtx C) D {
func (b *Button) Layout(gtx C) D {
if b.Tip != "" {
return b.ClickableTip.TipArea.Layout(gtx, Tooltip(b.Theme, b.Tip), b.actualLayout)
}
return b.actualLayout(gtx)
}
func (b *Button) actualLayout(gtx C) D {
min := gtx.Constraints.Min
min.Y = gtx.Dp(st.Height)
return b.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
min.Y = gtx.Dp(b.Style.Height)
return b.Clickable.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.Button.Add(gtx.Ops)
return layout.Background{}.Layout(gtx,
func(gtx layout.Context) layout.Dimensions {
rr := gtx.Dp(st.CornerRadius)
rr := gtx.Dp(b.Style.CornerRadius)
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
background := st.Background
background := b.Style.Background
switch {
case b.Hovered():
case b.Clickable.Hovered():
background = hoveredColor(background)
}
paint.Fill(gtx.Ops, background)
for _, c := range b.History() {
for _, c := range b.Clickable.History() {
drawInk(gtx, (widget.Press)(c))
}
return layout.Dimensions{Size: gtx.Constraints.Min}
@ -182,44 +183,71 @@ func Btn(th *Theme, st *ButtonStyle, b *Clickable, txt string) layout.Widget {
func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min = min
return layout.Center.Layout(gtx, func(gtx C) D {
return st.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return b.Style.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
colMacro := op.Record(gtx.Ops)
paint.ColorOp{Color: st.Color}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, th.Material.Shaper, st.Font, st.TextSize, txt, colMacro.Stop())
paint.ColorOp{Color: b.Style.Color}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, b.Theme.Material.Shaper, b.Style.Font, b.Style.TextSize, b.Text, colMacro.Stop())
})
})
},
)
})
}
}
func IconBtn(th *Theme, st *IconButtonStyle, b *Clickable, icon *widget.Icon, enabled bool) layout.Widget {
return func(gtx C) D {
func (b *ActionButton) Layout(gtx C) D {
for b.Clickable.Clicked(gtx) {
b.act.Do()
}
if !b.act.Enabled() {
b.Style = b.DisabledStyle
}
return b.Button.Layout(gtx)
}
func (b *ToggleButton) Layout(gtx C) D {
for b.Clickable.Clicked(gtx) {
b.b.Toggle()
}
if !b.b.Enabled() {
b.Style = b.DisabledStyle
} else if !b.b.Value() {
b.Style = b.OffStyle
}
return b.Button.Layout(gtx)
}
func (i *IconButton) Layout(gtx C) D {
if i.Tip != "" {
return i.ClickableTip.TipArea.Layout(gtx, Tooltip(i.Theme, i.Tip), i.actualLayout)
}
return i.actualLayout(gtx)
}
func (i *IconButton) actualLayout(gtx C) D {
m := op.Record(gtx.Ops)
dims := b.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
dims := i.Clickable.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.Button.Add(gtx.Ops)
return layout.Background{}.Layout(gtx,
func(gtx layout.Context) layout.Dimensions {
rr := (gtx.Constraints.Min.X + gtx.Constraints.Min.Y) / 4
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
background := st.Background
background := i.Style.Background
switch {
case b.Hovered():
case i.Clickable.Hovered():
background = hoveredColor(background)
}
paint.Fill(gtx.Ops, background)
for _, c := range b.History() {
for _, c := range i.Clickable.History() {
drawInk(gtx, (widget.Press)(c))
}
return layout.Dimensions{Size: gtx.Constraints.Min}
},
func(gtx layout.Context) layout.Dimensions {
return st.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
size := gtx.Dp(st.Size)
if icon != nil {
return i.Style.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
size := gtx.Dp(i.Style.Size)
if i.Icon != nil {
gtx.Constraints.Min = image.Point{X: size}
icon.Layout(gtx, st.Color)
i.Icon.Layout(gtx, i.Style.Color)
}
return layout.Dimensions{
Size: image.Point{X: size, Y: size},
@ -233,7 +261,30 @@ func IconBtn(th *Theme, st *IconButtonStyle, b *Clickable, icon *widget.Icon, en
defer clip.Ellipse(bounds).Push(gtx.Ops).Pop()
c.Add(gtx.Ops)
return dims
}
func (i *ActionIconButton) Layout(gtx C) D {
for i.Clickable.Clicked(gtx) {
i.act.Do()
}
if !i.act.Enabled() {
i.Style = i.DisabledStyle
}
return i.IconButton.Layout(gtx)
}
func (i *ToggleIconButton) Layout(gtx C) D {
for i.Clickable.Clicked(gtx) {
i.b.Toggle()
}
if !i.b.Enabled() {
i.Style = i.DisabledStyle
}
if !i.b.Value() {
i.Icon = i.OffIcon
i.Tip = i.OffTip
}
return i.IconButton.Layout(gtx)
}
func Tooltip(th *Theme, tip string) component.Tooltip {

View File

@ -159,13 +159,16 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
layout.Rigid(layout.Spacer{Width: 4}.Layout),
layout.Rigid(octave),
layout.Rigid(func(gtx C) D {
return layout.E.Layout(gtx, ToggleIconBtn(t.Model.LinkInstrTrack(), t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint))
linkInstrTrack := ToggleIconBtn(t.Model.LinkInstrTrack(), t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint)
return layout.E.Layout(gtx, linkInstrTrack.Layout)
}),
layout.Rigid(func(gtx C) D {
return layout.E.Layout(gtx, ToggleIconBtn(t.Model.InstrEnlarged(), t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint))
instrEnlarged := ToggleIconBtn(t.Model.InstrEnlarged(), t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint)
return layout.E.Layout(gtx, instrEnlarged.Layout)
}),
layout.Rigid(func(gtx C) D {
return layout.E.Layout(gtx, ActionIconBtn(t.Model.AddInstrument(), t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint))
addInstrumetn := ActionIconBtn(t.Model.AddInstrument(), t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint)
return layout.E.Layout(gtx, addInstrumetn.Layout)
}),
)
}),
@ -212,6 +215,15 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
t.LoadInstrument(reader)
}
splitInstrument := ActionIconBtn(t.SplitInstrument(), t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint)
commentExpanded := ToggleIconBtn(t.CommentExpanded(), t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint)
solo := ToggleIconBtn(t.Solo(), t.Theme, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint)
mute := ToggleIconBtn(t.Mute(), t.Theme, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint)
saveInstrument := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument")
loadInstrument := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument")
copyInstrument := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument")
deleteInstrument := ActionIconBtn(t.DeleteInstrument(), t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint)
header := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(layout.Spacer{Width: 6}.Layout),
@ -220,23 +232,24 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return t.InstrumentVoices.Layout(gtx, t.Model.InstrumentVoices(), t.Theme, &t.Theme.NumericUpDown, "Number of voices for this instrument")
}),
layout.Rigid(ActionIconBtn(t.SplitInstrument(), t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint)),
layout.Rigid(splitInstrument.Layout),
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
layout.Rigid(ToggleIconBtn(t.CommentExpanded(), t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint)),
layout.Rigid(ToggleIconBtn(t.Solo(), t.Theme, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint)),
layout.Rigid(ToggleIconBtn(t.Mute(), t.Theme, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint)),
layout.Rigid(commentExpanded.Layout),
layout.Rigid(solo.Layout),
layout.Rigid(mute.Layout),
layout.Rigid(func(gtx C) D {
dims := TipIconBtn(t.Theme, &t.Theme.IconButton, ie.presetMenuBtn, icons.NavigationMenu, "Load preset", true).Layout(gtx)
preset := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.presetMenuBtn, icons.NavigationMenu, "Load preset")
dims := preset.Layout(gtx)
op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops)
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(500))
gtx.Constraints.Max.X = gtx.Dp(unit.Dp(180))
m.Layout(gtx, ie.presetMenuItems...)
return dims
}),
layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument", true).Layout),
layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument", true).Layout),
layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument", true).Layout),
layout.Rigid(ActionIconBtn(t.DeleteInstrument(), t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint)),
layout.Rigid(saveInstrument.Layout),
layout.Rigid(loadInstrument.Layout),
layout.Rigid(copyInstrument.Layout),
layout.Rigid(deleteInstrument.Layout),
)
}
@ -467,7 +480,8 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
t.AddUnit(false).Do()
}
margin := layout.Inset{Right: unit.Dp(20), Bottom: unit.Dp(1)}
return margin.Layout(gtx, TipIconBtn(t.Theme, &t.Theme.AddUnit, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)", true).Layout)
addUnit := IconBtn(t.Theme, &t.Theme.IconButton.Emphasis, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)")
return margin.Layout(gtx, addUnit.Layout)
}),
)
})

View File

@ -161,14 +161,15 @@ func PopupMenu(th *Theme, s *LabelStyle, menu *Menu) MenuStyle {
}
}
func (tr *Tracker) layoutMenu(gtx C, title string, clickable *Clickable, menu *Menu, width unit.Dp, items ...MenuItem) layout.Widget {
for clickable.Clicked(gtx) {
func (tr *Tracker) layoutMenu(gtx C, title string, ct *ClickableTip, menu *Menu, width unit.Dp, items ...MenuItem) layout.Widget {
for ct.Clicked(gtx) {
menu.Visible = true
}
m := PopupMenu(tr.Theme, &tr.Theme.Menu.Text, menu)
return func(gtx C) D {
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
dims := Btn(tr.Theme, &tr.Theme.Button.Menu, clickable, title)(gtx)
btn := Btn(tr.Theme, &tr.Theme.Button.Menu, ct, title, "")
dims := btn.Layout(gtx)
op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops)
gtx.Constraints.Max.X = gtx.Dp(width)
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(300))

View File

@ -183,22 +183,22 @@ func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
midiInBtnStyle := ToggleBtn(t.TrackMidiIn(), t.Theme, te.TrackMidiInBtn, "MIDI", "Input notes from MIDI keyboard")
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.Rigid(subtractSemitoneBtnStyle),
layout.Rigid(addOctaveBtnStyle),
layout.Rigid(subtractOctaveBtnStyle),
layout.Rigid(noteOffBtnStyle),
layout.Rigid(effectBtnStyle),
layout.Rigid(uniqueBtnStyle),
layout.Rigid(addSemitoneBtnStyle.Layout),
layout.Rigid(subtractSemitoneBtnStyle.Layout),
layout.Rigid(addOctaveBtnStyle.Layout),
layout.Rigid(subtractOctaveBtnStyle.Layout),
layout.Rigid(noteOffBtnStyle.Layout),
layout.Rigid(effectBtnStyle.Layout),
layout.Rigid(uniqueBtnStyle.Layout),
layout.Rigid(layout.Spacer{Width: 10}.Layout),
layout.Rigid(Label(t.Theme, &t.Theme.NoteEditor.Header, "Voices").Layout),
layout.Rigid(layout.Spacer{Width: 4}.Layout),
layout.Rigid(voiceUpDown),
layout.Rigid(splitTrackBtnStyle),
layout.Rigid(midiInBtnStyle),
layout.Rigid(splitTrackBtnStyle.Layout),
layout.Rigid(midiInBtnStyle.Layout),
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
layout.Rigid(deleteTrackBtnStyle),
layout.Rigid(newTrackBtnStyle))
layout.Rigid(deleteTrackBtnStyle.Layout),
layout.Rigid(newTrackBtnStyle.Layout))
})
}

View File

@ -49,6 +49,9 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tr
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
onceBtn := ToggleBtn(once, th, s.onceBtn, "Once", "Trigger once on next event")
wrapBtn := ToggleBtn(wrap, th, s.wrapBtn, "Wrap", "Wrap buffer when full")
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Flexed(1, func(gtx C) D { return s.layoutWave(gtx, wave, th) }),
layout.Rigid(func(gtx C) D {
@ -56,7 +59,7 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tr
layout.Rigid(leftSpacer),
layout.Rigid(Label(th, &th.SongPanel.RowHeader, "Trigger").Layout),
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
layout.Rigid(ToggleBtn(once, th, s.onceBtn, "Once", "Trigger once on next event")),
layout.Rigid(onceBtn.Layout),
layout.Rigid(func(gtx C) D {
return s.triggerChannelNumber.Layout(gtx, vtrig, th, &th.NumericUpDown, "Trigger channel")
}),
@ -68,7 +71,7 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tr
layout.Rigid(leftSpacer),
layout.Rigid(Label(th, &th.SongPanel.RowHeader, "Buffer").Layout),
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
layout.Rigid(ToggleBtn(wrap, th, s.wrapBtn, "Wrap", "Wrap buffer when full")),
layout.Rigid(wrapBtn.Layout),
layout.Rigid(func(gtx C) D {
return s.lengthInBeatsNumber.Layout(gtx, vlen, th, &th.NumericUpDown, "Buffer length in beats")
}),

View File

@ -22,8 +22,8 @@ type SongPanel struct {
LoudnessExpander *Expander
PeakExpander *Expander
WeightingTypeBtn *Clickable
OversamplingBtn *Clickable
WeightingTypeBtn *ClickableTip
OversamplingBtn *ClickableTip
BPM *NumericUpDown
RowsPerPattern *NumericUpDown
@ -48,8 +48,8 @@ func NewSongPanel(model *tracker.Model) *SongPanel {
MenuBar: NewMenuBar(model),
PlayBar: NewPlayBar(),
WeightingTypeBtn: &Clickable{},
OversamplingBtn: &Clickable{},
WeightingTypeBtn: new(ClickableTip),
OversamplingBtn: new(ClickableTip),
SongSettingsExpander: &Expander{Expanded: true},
ScopeExpander: &Expander{},
@ -98,13 +98,13 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
weightingTxt = "No weight (RMS)"
}
weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt)
weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt, "")
oversamplingTxt := "Sample peak"
if tr.Model.Oversampling().Value() {
oversamplingTxt = "True peak"
}
oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt)
oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt, "")
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D {
@ -164,7 +164,7 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
}),
layout.Rigid(func(gtx C) D {
gtx.Constraints.Min.X = 0
return weightingBtn(gtx)
return weightingBtn.Layout(gtx)
}),
)
},
@ -193,7 +193,7 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
}),
layout.Rigid(func(gtx C) D {
gtx.Constraints.Min.X = 0
return oversamplingBtn(gtx)
return oversamplingBtn.Layout(gtx)
}),
)
},
@ -296,7 +296,7 @@ func (e *Expander) layoutHeader(gtx C, th *Theme, title string, smallWidget layo
}
type MenuBar struct {
Clickables []Clickable
Clickables []ClickableTip
Menus []Menu
fileMenuItems []MenuItem
@ -309,7 +309,7 @@ type MenuBar struct {
func NewMenuBar(model *tracker.Model) *MenuBar {
ret := &MenuBar{
Clickables: make([]Clickable, 3),
Clickables: make([]ClickableTip, 3),
Menus: make([]Menu, 3),
PanicBtn: new(ClickableTip),
panicHint: makeHint("Panic", " (%s)", "PanicToggle"),
@ -343,12 +343,15 @@ func (t *MenuBar) Layout(gtx C, tr *Tracker) D {
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(36))
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36))
panicBtnStyle := ToggleIconBtn(tr.Panic(), tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint)
panicBtn := ToggleIconBtn(tr.Panic(), tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint)
if tr.Panic().Value() {
panicBtn.Style = tr.Theme.IconButton.Error
}
flex := layout.Flex{Axis: layout.Horizontal, Alignment: layout.End}
fileFC := layout.Rigid(tr.layoutMenu(gtx, "File", &t.Clickables[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...))
editFC := layout.Rigid(tr.layoutMenu(gtx, "Edit", &t.Clickables[1], &t.Menus[1], unit.Dp(200), t.editMenuItems...))
midiFC := layout.Rigid(tr.layoutMenu(gtx, "MIDI", &t.Clickables[2], &t.Menus[2], unit.Dp(200), t.midiMenuItems...))
panicFC := layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, panicBtnStyle) })
panicFC := layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, panicBtn.Layout) })
if len(t.midiMenuItems) > 0 {
return flex.Layout(gtx, fileFC, editFC, midiFC, panicFC)
}
@ -390,13 +393,19 @@ func NewPlayBar() *PlayBar {
}
func (pb *PlayBar) Layout(gtx C, tr *Tracker) D {
playBtn := ToggleIconBtn(tr.Playing(), tr.Theme, pb.PlayingBtn, icons.AVPlayArrow, icons.AVStop, pb.playHint, pb.stopHint)
rewindBtn := ActionIconBtn(tr.PlaySongStart(), tr.Theme, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint)
recordBtn := ToggleIconBtn(tr.IsRecording(), tr.Theme, pb.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, pb.recordHint, pb.stopRecordHint)
followBtn := ToggleIconBtn(tr.Follow(), tr.Theme, pb.FollowBtn, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, pb.followOffHint, pb.followOnHint)
loopBtn := ToggleIconBtn(tr.LoopToggle(), tr.Theme, pb.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, pb.loopOffHint, pb.loopOnHint)
return Surface{Gray: 37}.Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Flexed(1, ToggleIconBtn(tr.Playing(), tr.Theme, pb.PlayingBtn, icons.AVPlayArrow, icons.AVStop, pb.playHint, pb.stopHint)),
layout.Rigid(ActionIconBtn(tr.PlaySongStart(), tr.Theme, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint)),
layout.Rigid(ToggleIconBtn(tr.IsRecording(), tr.Theme, pb.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, pb.recordHint, pb.stopRecordHint)),
layout.Rigid(ToggleIconBtn(tr.Follow(), tr.Theme, pb.FollowBtn, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, pb.followOffHint, pb.followOnHint)),
layout.Rigid(ToggleIconBtn(tr.LoopToggle(), tr.Theme, pb.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, pb.loopOffHint, pb.loopOnHint)),
layout.Flexed(1, playBtn.Layout),
layout.Rigid(rewindBtn.Layout),
layout.Rigid(recordBtn.Layout),
layout.Rigid(followBtn.Layout),
layout.Rigid(loopBtn.Layout),
)
})
}

View File

@ -19,8 +19,12 @@ type Theme struct {
Disabled ButtonStyle
Menu ButtonStyle
}
IconButton IconButtonStyle
AddUnit IconButtonStyle
IconButton struct {
Enabled IconButtonStyle
Disabled IconButtonStyle
Emphasis IconButtonStyle
Error IconButtonStyle
}
Oscilloscope OscilloscopeStyle
NumericUpDown NumericUpDownStyle
DialogTitle LabelStyle

View File

@ -55,14 +55,23 @@ button:
height: *buttonheight
inset: *buttoninset
iconbutton:
enabled:
color: *primarycolor
size: 24
inset: { top: 6, bottom: 6, left: 6, right: 6 }
addunit:
disabled:
color: *disabled
size: 24
inset: { top: 6, bottom: 6, left: 6, right: 6 }
emphasis:
color: *contrastfg
background: *primarycolor
size: 24
inset: { top: 6, bottom: 6, left: 6, right: 6 }
error:
color: *errorcolor
size: 24
inset: { top: 6, bottom: 6, left: 6, right: 6 }
oscilloscope:
curvecolors: [*primarycolor, *secondarycolor]
limitcolor: { r: 255, g: 255, b: 255, a: 8 }

View File

@ -140,14 +140,19 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
text = pe.caser.String(text)
}
hintText := Label(t.Theme, &t.Theme.UnitEditor.Hint, text)
deleteUnit := ActionIconBtn(t.DeleteUnit(), t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
copyUnit := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint)
disableUnit := ToggleIconBtn(t.UnitDisabled(), t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint)
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(ActionIconBtn(t.DeleteUnit(), t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")),
layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint, true).Layout),
layout.Rigid(ToggleIconBtn(t.UnitDisabled(), t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint)),
layout.Rigid(deleteUnit.Layout),
layout.Rigid(copyUnit.Layout),
layout.Rigid(disableUnit.Layout),
layout.Rigid(func(gtx C) D {
var dims D
if t.Units().SelectedType() != "" {
dims = ActionIconBtn(t.ClearUnit(), t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")(gtx)
clearUnit := ActionIconBtn(t.ClearUnit(), t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")
dims = clearUnit.Layout(gtx)
}
return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)}
}),
@ -218,9 +223,9 @@ func (pe *UnitEditor) command(e key.Event, t *Tracker) {
type ParameterWidget struct {
floatWidget widget.Float
boolWidget widget.Bool
instrBtn Clickable
instrBtn ClickableTip
instrMenu Menu
unitBtn Clickable
unitBtn ClickableTip
unitMenu Menu
Parameter tracker.Parameter
tipArea component.TipArea