feat(tracker): enum-style values and menus to choose one option

This commit is contained in:
5684185+vsariola@users.noreply.github.com
2026-01-27 23:26:13 +02:00
parent ca4b87d43d
commit 4bb5df9c87
19 changed files with 645 additions and 306 deletions

View File

@ -41,6 +41,8 @@ type SongPanel struct {
Step *NumericUpDownState
SongLength *NumericUpDownState
weightingMenuState *MenuState
List *layout.List
ScrollBar *ScrollBar
@ -79,6 +81,8 @@ func NewSongPanel(tr *Tracker) *SongPanel {
List: &layout.List{Axis: layout.Vertical},
ScrollBar: &ScrollBar{Axis: layout.Vertical},
weightingMenuState: new(MenuState),
SpectrumState: NewSpectrumState(),
SpectrumScaleBar: &ScaleBar{Axis: layout.Vertical, BarSize: 10, Size: 300},
ScopeScaleBar: &ScaleBar{Axis: layout.Vertical, BarSize: 10, Size: 300},
@ -87,9 +91,6 @@ func NewSongPanel(tr *Tracker) *SongPanel {
}
func (s *SongPanel) Update(gtx C, t *Tracker) {
for s.WeightingTypeBtn.Clicked(gtx) {
t.Model.Detector().Weighting().SetValue((t.Detector().Weighting().Value() + 1) % int(tracker.NumWeightingTypes))
}
for s.OversamplingBtn.Clicked(gtx) {
t.Model.Detector().Oversampling().SetValue(!t.Detector().Oversampling().Value())
}
@ -113,19 +114,8 @@ func (t *SongPanel) layoutSongOptions(gtx C) D {
tr := TrackerFromContext(gtx)
paint.FillShape(gtx.Ops, tr.Theme.SongPanel.Bg, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
var weightingTxt string
switch tracker.WeightingType(tr.Model.Detector().Weighting().Value()) {
case tracker.KWeighting:
weightingTxt = "K-weight (LUFS)"
case tracker.AWeighting:
weightingTxt = "A-weight"
case tracker.CWeighting:
weightingTxt = "C-weight"
case tracker.NoWeighting:
weightingTxt = "No weight (RMS)"
}
weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt, "")
weightingBtn := MenuBtn(t.weightingMenuState, t.WeightingTypeBtn, tr.Detector().Weighting().String()).
WithBtnStyle(&tr.Theme.Button.Text).WithPopupStyle(&tr.Theme.Popup.ContextMenu)
oversamplingTxt := "Sample peak"
if tr.Model.Detector().Oversampling().Value() {
@ -236,7 +226,7 @@ func (t *SongPanel) layoutSongOptions(gtx C) D {
}),
layout.Rigid(func(gtx C) D {
gtx.Constraints.Min.X = 0
return weightingBtn.Layout(gtx)
return weightingBtn.Layout(gtx, IntMenuChild(tr.Detector().Weighting(), icons.NavigationCheck))
}),
)
},
@ -465,8 +455,6 @@ type MenuBar struct {
Clickables []Clickable
MenuStates []MenuState
midiMenuItems []ActionMenuItem
panicHint string
PanicBtn *Clickable
}
@ -478,11 +466,6 @@ func NewMenuBar(tr *Tracker) *MenuBar {
PanicBtn: new(Clickable),
panicHint: makeHint("Panic", " (%s)", "PanicToggle"),
}
for input := range tr.MIDI().InputDevices {
ret.midiMenuItems = append(ret.midiMenuItems,
MenuItem(tr.MIDI().Open(input), input, "", icons.ImageControlPoint),
)
}
return ret
}
@ -492,50 +475,65 @@ func (t *MenuBar) Layout(gtx C) D {
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36))
flex := layout.Flex{Axis: layout.Horizontal, Alignment: layout.End}
fileBtn := MenuBtn(tr.Theme, &t.MenuStates[0], &t.Clickables[0], "File")
fileBtn := MenuBtn(&t.MenuStates[0], &t.Clickables[0], "File")
fileFC := layout.Rigid(func(gtx C) D {
items := [...]ActionMenuItem{
MenuItem(tr.Song().New(), "New Song", keyActionMap["NewSong"], icons.ContentClear),
MenuItem(tr.Song().Open(), "Open Song", keyActionMap["OpenSong"], icons.FileFolder),
MenuItem(tr.Song().Save(), "Save Song", keyActionMap["SaveSong"], icons.ContentSave),
MenuItem(tr.Song().SaveAs(), "Save Song As...", keyActionMap["SaveSongAs"], icons.ContentSave),
MenuItem(tr.Song().Export(), "Export Wav...", keyActionMap["ExportWav"], icons.ImageAudiotrack),
MenuItem(tr.RequestQuit(), "Quit", keyActionMap["Quit"], icons.ActionExitToApp),
items := [...]MenuChild{
ActionMenuChild(tr.Song().New(), "New Song", keyActionMap["NewSong"], icons.ContentClear),
ActionMenuChild(tr.Song().Open(), "Open Song", keyActionMap["OpenSong"], icons.FileFolder),
ActionMenuChild(tr.Song().Save(), "Save Song", keyActionMap["SaveSong"], icons.ContentSave),
ActionMenuChild(tr.Song().SaveAs(), "Save Song As...", keyActionMap["SaveSongAs"], icons.ContentSave),
DividerMenuChild(),
ActionMenuChild(tr.Song().Export(), "Export Wav...", keyActionMap["ExportWav"], icons.ImageAudiotrack),
DividerMenuChild(),
ActionMenuChild(tr.RequestQuit(), "Quit", keyActionMap["Quit"], icons.ActionExitToApp),
}
if !canQuit {
return fileBtn.Layout(gtx, items[:len(items)-1]...)
return fileBtn.Layout(gtx, items[:len(items)-2]...)
}
return fileBtn.Layout(gtx, items[:]...)
})
editBtn := MenuBtn(tr.Theme, &t.MenuStates[1], &t.Clickables[1], "Edit")
editBtn := MenuBtn(&t.MenuStates[1], &t.Clickables[1], "Edit")
editFC := layout.Rigid(func(gtx C) D {
return editBtn.Layout(gtx,
MenuItem(tr.History().Undo(), "Undo", keyActionMap["Undo"], icons.ContentUndo),
MenuItem(tr.History().Redo(), "Redo", keyActionMap["Redo"], icons.ContentRedo),
MenuItem(tr.Order().RemoveUnusedPatterns(), "Remove unused data", keyActionMap["RemoveUnused"], icons.ImageCrop),
ActionMenuChild(tr.History().Undo(), "Undo", keyActionMap["Undo"], icons.ContentUndo),
ActionMenuChild(tr.History().Redo(), "Redo", keyActionMap["Redo"], icons.ContentRedo),
DividerMenuChild(),
ActionMenuChild(tr.Order().RemoveUnusedPatterns(), "Remove unused data", keyActionMap["RemoveUnused"], icons.ImageCrop),
)
})
midiBtn := MenuBtn(tr.Theme, &t.MenuStates[2], &t.Clickables[2], "MIDI")
midiBtn := MenuBtn(&t.MenuStates[2], &t.Clickables[2], "MIDI")
midiFC := layout.Rigid(func(gtx C) D {
return midiBtn.Layout(gtx, t.midiMenuItems...)
return midiBtn.Layout(gtx,
ActionMenuChild(tr.MIDI().Refresh(), "Refresh port list", keyActionMap["MIDIRefresh"], icons.NavigationRefresh),
BoolMenuChild(tr.MIDI().InputtingNotes(), "Use for note input", keyActionMap["ToggleMIDIInputtingNotes"], icons.NavigationCheck),
DividerMenuChild(),
IntMenuChild(tr.MIDI().Input(), icons.NavigationCheck),
)
})
helpBtn := MenuBtn(tr.Theme, &t.MenuStates[3], &t.Clickables[3], "?")
helpBtn := MenuBtn(&t.MenuStates[3], &t.Clickables[3], "?")
helpFC := layout.Rigid(func(gtx C) D {
return helpBtn.Layout(gtx,
MenuItem(tr.ShowManual(), "Manual", keyActionMap["ShowManual"], icons.AVLibraryBooks),
MenuItem(tr.AskHelp(), "Ask help", keyActionMap["AskHelp"], icons.ActionHelp),
MenuItem(tr.ReportBug(), "Report bug", keyActionMap["ReportBug"], icons.ActionBugReport),
MenuItem(tr.ShowLicense(), "License", keyActionMap["ShowLicense"], icons.ActionCopyright))
ActionMenuChild(tr.ShowManual(), "Manual", keyActionMap["ShowManual"], icons.AVLibraryBooks),
ActionMenuChild(tr.AskHelp(), "Ask help", keyActionMap["AskHelp"], icons.ActionHelp),
ActionMenuChild(tr.ReportBug(), "Report bug", keyActionMap["ReportBug"], icons.ActionBugReport),
DividerMenuChild(),
ActionMenuChild(tr.ShowLicense(), "License", keyActionMap["ShowLicense"], icons.ActionCopyright))
})
panicBtn := ToggleIconBtn(tr.Play().Panicked(), tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint)
if tr.Play().Panicked().Value() {
panicBtn.Style = &tr.Theme.IconButton.Error
}
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, helpFC, panicFC)
return flex.Layout(gtx, fileFC, editFC, midiFC, helpFC, panicFC)
}
func (sp *SongPanel) Tags(level int, yield TagYieldFunc) bool {
for i := range sp.MenuBar.MenuStates {
if !sp.MenuBar.MenuStates[i].Tags(level, yield) {
return false
}
}
return flex.Layout(gtx, fileFC, editFC, helpFC, panicFC)
return true
}
type PlayBar struct {